29 changed files with 3263 additions and 0 deletions
@ -1,3 +1,7 @@ |
|||
[submodule "src/Perspex.ReactiveUI/src"] |
|||
path = src/Perspex.ReactiveUI/src |
|||
url = https://github.com/reactiveui/ReactiveUI.git |
|||
[submodule "src/Perspex.HtmlRenderer/external"] |
|||
path = src/Perspex.HtmlRenderer/external |
|||
url = https://github.com/Perspex/HTML-Renderer.git |
|||
branch = perspex-pcl |
|||
|
|||
@ -0,0 +1,170 @@ |
|||
<html> |
|||
<head> |
|||
<title>Additional features</title> |
|||
<link rel="Stylesheet" href="StyleSheet" /> |
|||
<style> |
|||
<!-- |
|||
.g1, .g2, .g3, .g4, .g5 { |
|||
background-color: red; |
|||
background-gradient: yellow; |
|||
padding: 22px; |
|||
} |
|||
|
|||
.g1 { background-gradient-angle: 0; } |
|||
|
|||
.g2 { background-gradient-angle: 45; } |
|||
|
|||
.g3 { background-gradient-angle: 90; } |
|||
|
|||
.g4 { background-gradient-angle: 135; } |
|||
|
|||
.g5 { background-gradient-angle: 180; } |
|||
|
|||
.c1, .c2, .c3, .c4, .c5 { |
|||
background-color: olive; |
|||
border: 0px; |
|||
color: white; |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
.c1 { corner-radius: 0px; } |
|||
|
|||
.c2 { corner-radius: 10px; } |
|||
|
|||
.c3 { corner-radius: 0px 10px 10px 0px; } |
|||
|
|||
.c4 { corner-radius: 18px; } |
|||
|
|||
.c5 { |
|||
corner-radius: 10px; |
|||
border: outset #BBBB00 2px; |
|||
} |
|||
|
|||
table { border-style: outset; } |
|||
|
|||
td, th { border-style: inset; } |
|||
|
|||
td { text-align: center; } |
|||
--> |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<h1> |
|||
Additional features |
|||
</h1> |
|||
<blockquote> |
|||
<p> |
|||
There are some additional features that you may already discovered about the renderer |
|||
core engine.</p> |
|||
<h2> |
|||
Graphic features</h2> |
|||
<p> |
|||
I have always wanted the W3C to add this features to the CSS spec (and so far, not |
|||
there yet :)</p> |
|||
<ul> |
|||
<li><b>Gradients on backgrounds</b></li> |
|||
<li><b>Rounded corners</b></li> |
|||
</ul> |
|||
<p> |
|||
And I think many many web designers would agree. Is it so hard or what?.</p> |
|||
<h3> |
|||
Background Gradients</h3> |
|||
<p> |
|||
It is a simple two color linear gradient, achieved by the adding of two CSS properties:</p> |
|||
<ol> |
|||
<li><code>background-gradient: (#Color)</code> - Second color of the gradient background, |
|||
the first one is given by <code>background-color</code>. Not inherited.</li> |
|||
<li><code>background-gradient-angle: (number)</code> - Angle (in degrees, clockwise) of |
|||
the gradient. Not inherited. Initial value:90</li> |
|||
</ol> |
|||
<b>Some examples</b> |
|||
<!-- Gradients table --> |
|||
<table width="300px"> |
|||
<tr> |
|||
<td class="g1"> |
|||
</td> |
|||
<td class="g2"> |
|||
</td> |
|||
<td class="g3"> |
|||
</td> |
|||
<td class="g4"> |
|||
</td> |
|||
<td class="g5"> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td> |
|||
0 degrees |
|||
</td> |
|||
<td> |
|||
45 degrees |
|||
</td> |
|||
<td> |
|||
90 degrees |
|||
</td> |
|||
<td> |
|||
135 degrees |
|||
</td> |
|||
<td> |
|||
180 degrees |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
<h3> |
|||
Rounded corners</h3> |
|||
<p> |
|||
As you may already know, CSS is based on a <a href="http://www.w3.org/TR/CSS21/box.html"> |
|||
Box Model</a>, where every box has it's own set of properties. Since we are |
|||
talking abound <b>boxes</b>, why not to make them with rounded corners, almost every |
|||
website you visit nowadays makes use of rounded corners, where a not very nice trick |
|||
with images and tables must be used.</p> |
|||
<p> |
|||
In this renderer, the rounded corners are achieved by adding this CSS properties:</p> |
|||
<ul> |
|||
<li><code>corner-ne-radius: (length)</code> Indicates the radius of the north-east corner. |
|||
Not ineritted</li> |
|||
<li><code>corner-se-radius: (length)</code> Indicates the radius of the south-east corner. |
|||
Not ineritted</li> |
|||
<li><code>corner-sw-radius: (length)</code> Indicates the radius of the south-west corner. |
|||
Not ineritted</li> |
|||
<li><code>corner-nw-radius: (length)</code> Indicates the radius of the north-west corner. |
|||
Not ineritted</li> |
|||
<li><code>corner-radius: (length){1,4}</code> Shorthand for the other corner properties. |
|||
Not ineritted</li> |
|||
</ul> |
|||
<!-- Corners table --> |
|||
<b>Some examples</b> |
|||
<table width="320px" cellspacing="10"> |
|||
<tr> |
|||
<td width="1" style="border: 0px"> |
|||
<p> |
|||
</p> |
|||
<p> |
|||
</p> |
|||
</td> |
|||
<td class="c1"> |
|||
c1 |
|||
</td> |
|||
<td class="c2"> |
|||
c2 |
|||
</td> |
|||
<td class="c3"> |
|||
c3 |
|||
</td> |
|||
<td class="c4"> |
|||
c4 |
|||
</td> |
|||
<td class="c5"> |
|||
c5 |
|||
</td> |
|||
</tr> |
|||
</table> |
|||
<pre>.c1, .c2, .c3, .c4, .c5 { background-color:olive; border:0px; color:white; vertical-align:middle; } |
|||
.c1 { corner-radius: 0px } |
|||
.c2 { corner-radius: 10px } |
|||
.c3 { corner-radius: 0px 10px 10px 0px } |
|||
.c4 { corner-radius: 18px } |
|||
.c5 { corner-radius: 10px; border: outset #bb0 2px; }</pre> |
|||
</blockquote> |
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,47 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using Perspex.Media; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex brushes.
|
|||
/// </summary>
|
|||
internal sealed class BrushAdapter : RBrush |
|||
{ |
|||
/// <summary>
|
|||
/// The actual Perspex brush instance.
|
|||
/// </summary>
|
|||
private readonly Brush _brush; |
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
public BrushAdapter(Brush brush) |
|||
{ |
|||
_brush = brush; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The actual Perspex brush instance.
|
|||
/// </summary>
|
|||
public Brush Brush |
|||
{ |
|||
get { return _brush; } |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ } |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using System; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex context menu for core.
|
|||
/// </summary>
|
|||
internal sealed class NullContextMenuAdapter : RContextMenu |
|||
{ |
|||
//TODO: actually implement context menu
|
|||
|
|||
private int _itemCount; |
|||
public override int ItemsCount => _itemCount; |
|||
public override void AddDivider() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public override void AddItem(string text, bool enabled, EventHandler onClick) |
|||
{ |
|||
_itemCount++; |
|||
} |
|||
|
|||
public override void RemoveLastDivider() |
|||
{ |
|||
_itemCount++; |
|||
} |
|||
|
|||
public override void Show(RControl parent, RPoint location) |
|||
{ |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using Perspex.Controls; |
|||
using Perspex.Input; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
using TheArtOfDev.HtmlRenderer.Core.Utils; |
|||
using TheArtOfDev.HtmlRenderer.Perspex.Utilities; |
|||
// ReSharper disable ConvertPropertyToExpressionBody
|
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex Control for core.
|
|||
/// </summary>
|
|||
internal sealed class ControlAdapter : RControl |
|||
{ |
|||
/// <summary>
|
|||
/// the underline Perspex control.
|
|||
/// </summary>
|
|||
private readonly Control _control; |
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
public ControlAdapter(Control control) |
|||
: base(PerspexAdapter.Instance) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(control, "control"); |
|||
|
|||
_control = control; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get the underline Perspex control
|
|||
/// </summary>
|
|||
public Control Control |
|||
{ |
|||
get { return _control; } |
|||
} |
|||
|
|||
public override RPoint MouseLocation |
|||
{ |
|||
get |
|||
{ |
|||
//TODO: Implement
|
|||
//return Utils.Convert(_control.PointFromScreen(Mouse.GetPosition(_control)));
|
|||
return new RPoint(0, 0); |
|||
} |
|||
} |
|||
|
|||
public override bool LeftMouseButton |
|||
{ |
|||
get |
|||
{ |
|||
return false; |
|||
//TODO: Implement
|
|||
//return Mouse.LeftButton == MouseButtonState.Pressed;
|
|||
} |
|||
} |
|||
|
|||
public override bool RightMouseButton |
|||
{ |
|||
get |
|||
{ |
|||
return false; |
|||
//TODO: Implement
|
|||
//return Mouse.RightButton == MouseButtonState.Pressed;
|
|||
} |
|||
} |
|||
|
|||
public override void SetCursorDefault() |
|||
{ |
|||
_control.Cursor = new Cursor(StandardCursorType.Arrow); |
|||
} |
|||
|
|||
public override void SetCursorHand() |
|||
{ |
|||
_control.Cursor = new Cursor(StandardCursorType.Hand); |
|||
} |
|||
|
|||
public override void SetCursorIBeam() |
|||
{ |
|||
_control.Cursor = new Cursor(StandardCursorType.Ibeam); |
|||
} |
|||
|
|||
public override void DoDragDropCopy(object dragDropData) |
|||
{ |
|||
//TODO: Implement
|
|||
//DragDrop.DoDragDrop(_control, dragDropData, DragDropEffects.Copy);
|
|||
} |
|||
|
|||
public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth) |
|||
{ |
|||
using (var g = new GraphicsAdapter()) |
|||
{ |
|||
g.MeasureString(str, font, maxWidth, out charFit, out charFitWidth); |
|||
} |
|||
} |
|||
|
|||
public override void Invalidate() |
|||
{ |
|||
_control.InvalidateVisual(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using Perspex.Media; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex Font.
|
|||
/// </summary>
|
|||
internal sealed class FontAdapter : RFont |
|||
{ |
|||
public RFontStyle Style { get; } |
|||
|
|||
#region Fields and Consts
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// the size of the font
|
|||
/// </summary>
|
|||
private readonly double _size; |
|||
|
|||
/// <summary>
|
|||
/// the vertical offset of the font underline location from the top of the font.
|
|||
/// </summary>
|
|||
private readonly double _underlineOffset = -1; |
|||
|
|||
/// <summary>
|
|||
/// Cached font height.
|
|||
/// </summary>
|
|||
private readonly double _height = -1; |
|||
|
|||
/// <summary>
|
|||
/// Cached font whitespace width.
|
|||
/// </summary>
|
|||
private double _whitespaceWidth = -1; |
|||
|
|||
|
|||
#endregion
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
public FontAdapter(string fontFamily, double size, RFontStyle style) |
|||
{ |
|||
Style = style; |
|||
Name = fontFamily; |
|||
_size = size; |
|||
//TODO: Somehow get proper line spacing and underlinePosition
|
|||
var lineSpacing = 2; |
|||
var underlinePosition = 1; |
|||
|
|||
_height = 96d / 72d * _size * lineSpacing; |
|||
_underlineOffset = 96d / 72d * _size * (lineSpacing + underlinePosition); |
|||
|
|||
} |
|||
|
|||
public string Name { get; set; } |
|||
|
|||
|
|||
public override double Size |
|||
{ |
|||
get { return _size; } |
|||
} |
|||
|
|||
public override double UnderlineOffset |
|||
{ |
|||
get { return _underlineOffset; } |
|||
} |
|||
|
|||
public override double Height |
|||
{ |
|||
get { return _height; } |
|||
} |
|||
|
|||
public override double LeftPadding |
|||
{ |
|||
get { return _height / 6f; } |
|||
} |
|||
|
|||
public override double GetWhitespaceWidth(RGraphics graphics) |
|||
{ |
|||
if (_whitespaceWidth < 0) |
|||
{ |
|||
_whitespaceWidth = graphics.MeasureString(" ", this).Width; |
|||
} |
|||
return _whitespaceWidth; |
|||
} |
|||
|
|||
public FontStyle FontStyle => Style.HasFlag(RFontStyle.Italic) ? FontStyle.Italic : FontStyle.Normal; |
|||
|
|||
public FontWeight Weight => Style.HasFlag(RFontStyle.Bold) ? FontWeight.Bold : FontWeight.Normal; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex Font family object for core.
|
|||
/// </summary>
|
|||
internal sealed class FontFamilyAdapter : RFontFamily |
|||
{ |
|||
public FontFamilyAdapter(string fontFamily) |
|||
{ |
|||
Name = fontFamily; |
|||
} |
|||
|
|||
public override string Name { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,277 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using System; |
|||
using System.Globalization; |
|||
using Perspex; |
|||
using Perspex.Media; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
using TheArtOfDev.HtmlRenderer.Core.Utils; |
|||
using TheArtOfDev.HtmlRenderer.Perspex.Utilities; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex Graphics.
|
|||
/// </summary>
|
|||
internal sealed class GraphicsAdapter : RGraphics |
|||
{ |
|||
#region Fields and Consts
|
|||
|
|||
/// <summary>
|
|||
/// The wrapped Perspex graphics object
|
|||
/// </summary>
|
|||
private readonly IDrawingContext _g; |
|||
|
|||
/// <summary>
|
|||
/// if to release the graphics object on dispose
|
|||
/// </summary>
|
|||
private readonly bool _releaseGraphics; |
|||
|
|||
#endregion
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
/// <param name="g">the Perspex graphics object to use</param>
|
|||
/// <param name="initialClip">the initial clip of the graphics</param>
|
|||
/// <param name="releaseGraphics">optional: if to release the graphics object on dispose (default - false)</param>
|
|||
public GraphicsAdapter(IDrawingContext g, RRect initialClip, bool releaseGraphics = false) |
|||
: base(PerspexAdapter.Instance, initialClip) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(g, "g"); |
|||
|
|||
_g = g; |
|||
_releaseGraphics = releaseGraphics; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
public GraphicsAdapter() |
|||
: base(PerspexAdapter.Instance, RRect.Empty) |
|||
{ |
|||
_g = null; |
|||
_releaseGraphics = false; |
|||
} |
|||
|
|||
public override void PopClip() |
|||
{ |
|||
/* |
|||
_g.Pop(); |
|||
_clipStack.Pop(); |
|||
*/ |
|||
} |
|||
|
|||
public override void PushClip(RRect rect) |
|||
{ |
|||
//_clipStack.Push(rect);
|
|||
//_g.PushClip(new RectangleGeometry(Utils.Convert(rect)));
|
|||
} |
|||
|
|||
public override void PushClipExclude(RRect rect) |
|||
{ |
|||
//var geometry = new CombinedGeometry();
|
|||
//geometry.Geometry1 = new RectangleGeometry(Utils.Convert(_clipStack.Peek()));
|
|||
//geometry.Geometry2 = new RectangleGeometry(Utils.Convert(rect));
|
|||
//geometry.GeometryCombineMode = GeometryCombineMode.Exclude;
|
|||
|
|||
//_clipStack.Push(_clipStack.Peek());
|
|||
//_g.PushClip(geometry);
|
|||
} |
|||
|
|||
public override Object SetAntiAliasSmoothingMode() |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
public override void ReturnPreviousSmoothingMode(Object prevMode) |
|||
{ } |
|||
|
|||
public override RSize MeasureString(string str, RFont font) |
|||
{ |
|||
var text = GetText(str, font); |
|||
var measure = text.Measure(); |
|||
return new RSize(measure.Width, measure.Height); |
|||
|
|||
} |
|||
|
|||
FormattedText GetText(string str, RFont font) |
|||
{ |
|||
var f = ((FontAdapter)font); |
|||
return new FormattedText(str, f.Name, font.Size, f.FontStyle, TextAlignment.Left, f.Weight); |
|||
} |
|||
|
|||
public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth) |
|||
{ |
|||
var text = GetText(str, font); |
|||
charFit = str.Length; |
|||
charFitWidth = text.Measure().Width; |
|||
} |
|||
|
|||
public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl) |
|||
{ |
|||
var text = GetText(str, font); |
|||
text.Constraint = Util.Convert(size); |
|||
_g.DrawText(new SolidColorBrush(Util.Convert(color)), Util.Convert(point), text); |
|||
|
|||
//var colorConv = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
|
|||
|
|||
//bool glyphRendered = false;
|
|||
//GlyphTypeface glyphTypeface = ((FontAdapter)font).GlyphTypeface;
|
|||
//if (glyphTypeface != null)
|
|||
//{
|
|||
// double width = 0;
|
|||
// ushort[] glyphs = new ushort[str.Length];
|
|||
// double[] widths = new double[str.Length];
|
|||
|
|||
// int i = 0;
|
|||
// for (; i < str.Length; i++)
|
|||
// {
|
|||
// ushort glyph;
|
|||
// if (!glyphTypeface.CharacterToGlyphMap.TryGetValue(str[i], out glyph))
|
|||
// break;
|
|||
|
|||
// glyphs[i] = glyph;
|
|||
// width += glyphTypeface.AdvanceWidths[glyph];
|
|||
// widths[i] = 96d / 72d * font.Size * glyphTypeface.AdvanceWidths[glyph];
|
|||
// }
|
|||
|
|||
// if (i >= str.Length)
|
|||
// {
|
|||
// point.Y += glyphTypeface.Baseline * font.Size * 96d / 72d;
|
|||
// point.X += rtl ? 96d / 72d * font.Size * width : 0;
|
|||
|
|||
// glyphRendered = true;
|
|||
// var glyphRun = new GlyphRun(glyphTypeface, rtl ? 1 : 0, false, 96d / 72d * font.Size, glyphs, Utils.ConvertRound(point), widths, null, null, null, null, null, null);
|
|||
// _g.DrawGlyphRun(colorConv, glyphRun);
|
|||
// }
|
|||
//}
|
|||
|
|||
//if (!glyphRendered)
|
|||
//{
|
|||
// var formattedText = new FormattedText(str, CultureInfo.CurrentCulture, rtl ? FlowDirection.RightToLeft : FlowDirection.LeftToRight, ((FontAdapter)font).Font, 96d / 72d * font.Size, colorConv);
|
|||
// point.X += rtl ? formattedText.Width : 0;
|
|||
// _g.DrawText(formattedText, Utils.ConvertRound(point));
|
|||
//}
|
|||
} |
|||
|
|||
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation) |
|||
{ |
|||
//TODO: Implement texture brush
|
|||
return PerspexAdapter.Instance.GetSolidBrush(Util.Convert(Colors.Magenta)); |
|||
|
|||
//var brush = new ImageBrush(((ImageAdapter)image).Image);
|
|||
//brush.Stretch = Stretch.None;
|
|||
//brush.TileMode = TileMode.Tile;
|
|||
//brush.Viewport = Utils.Convert(dstRect);
|
|||
//brush.ViewportUnits = BrushMappingMode.Absolute;
|
|||
//brush.Transform = new TranslateTransform(translateTransformLocation.X, translateTransformLocation.Y);
|
|||
//brush.Freeze();
|
|||
//return new BrushAdapter(brush);
|
|||
} |
|||
|
|||
public override RGraphicsPath GetGraphicsPath() |
|||
{ |
|||
return new GraphicsPathAdapter(); |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
//TODO: Do something about Dispose
|
|||
//if (_releaseGraphics)
|
|||
// _g.Close();
|
|||
} |
|||
|
|||
|
|||
#region Delegate graphics methods
|
|||
|
|||
public override void DrawLine(RPen pen, double x1, double y1, double x2, double y2) |
|||
{ |
|||
x1 = (int)x1; |
|||
x2 = (int)x2; |
|||
y1 = (int)y1; |
|||
y2 = (int)y2; |
|||
|
|||
var adj = pen.Width; |
|||
if (Math.Abs(x1 - x2) < .1 && Math.Abs(adj % 2 - 1) < .1) |
|||
{ |
|||
x1 += .5; |
|||
x2 += .5; |
|||
} |
|||
if (Math.Abs(y1 - y2) < .1 && Math.Abs(adj % 2 - 1) < .1) |
|||
{ |
|||
y1 += .5; |
|||
y2 += .5; |
|||
} |
|||
|
|||
_g.DrawLine(((PenAdapter)pen).CreatePen(), new Point(x1, y1), new Point(x2, y2)); |
|||
} |
|||
|
|||
public override void DrawRectangle(RPen pen, double x, double y, double width, double height) |
|||
{ |
|||
var adj = pen.Width; |
|||
if (Math.Abs(adj % 2 - 1) < .1) |
|||
{ |
|||
x += .5; |
|||
y += .5; |
|||
} |
|||
_g.DrawRectange(((PenAdapter) pen).CreatePen(), new Rect(x, y, width, height)); |
|||
} |
|||
|
|||
public override void DrawRectangle(RBrush brush, double x, double y, double width, double height) |
|||
{ |
|||
_g.FillRectange(((BrushAdapter) brush).Brush, new Rect(x, y, width, height)); |
|||
} |
|||
|
|||
public override void DrawImage(RImage image, RRect destRect, RRect srcRect) |
|||
{ |
|||
_g.DrawImage(((ImageAdapter) image).Image, 1, Util.Convert(srcRect), Util.Convert(destRect)); |
|||
} |
|||
|
|||
public override void DrawImage(RImage image, RRect destRect) |
|||
{ |
|||
_g.DrawImage(((ImageAdapter) image).Image, 1, new Rect(0, 0, image.Width, image.Height), |
|||
Util.Convert(destRect)); |
|||
} |
|||
|
|||
public override void DrawPath(RPen pen, RGraphicsPath path) |
|||
{ |
|||
_g.DrawGeometry(null, ((PenAdapter)pen).CreatePen(), ((GraphicsPathAdapter)path).GetClosedGeometry()); |
|||
} |
|||
|
|||
public override void DrawPath(RBrush brush, RGraphicsPath path) |
|||
{ |
|||
_g.DrawGeometry(((BrushAdapter)brush).Brush, null, ((GraphicsPathAdapter)path).GetClosedGeometry()); |
|||
} |
|||
|
|||
public override void DrawPolygon(RBrush brush, RPoint[] points) |
|||
{ |
|||
if (points != null && points.Length > 0) |
|||
{ |
|||
var g = new StreamGeometry(); |
|||
using (var context = g.Open()) |
|||
{ |
|||
context.BeginFigure(Util.Convert(points[0]), true); |
|||
for (int i = 1; i < points.Length; i++) |
|||
context.LineTo(Util.Convert(points[i])); |
|||
} |
|||
|
|||
_g.DrawGeometry(((BrushAdapter)brush).Brush, null, g); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using Perspex; |
|||
using Perspex.Media; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex graphics path object for core.
|
|||
/// </summary>
|
|||
internal sealed class GraphicsPathAdapter : RGraphicsPath |
|||
{ |
|||
/// <summary>
|
|||
/// The actual Perspex graphics geometry instance.
|
|||
/// </summary>
|
|||
private readonly StreamGeometry _geometry = new StreamGeometry(); |
|||
|
|||
/// <summary>
|
|||
/// The context used in Perspex geometry to render path
|
|||
/// </summary>
|
|||
private readonly StreamGeometryContext _geometryContext; |
|||
|
|||
public GraphicsPathAdapter() |
|||
{ |
|||
_geometryContext = _geometry.Open(); |
|||
} |
|||
|
|||
public override void Start(double x, double y) |
|||
{ |
|||
_geometryContext.BeginFigure(new Point(x, y), true); |
|||
} |
|||
|
|||
public override void LineTo(double x, double y) |
|||
{ |
|||
_geometryContext.LineTo(new Point(x, y)); |
|||
} |
|||
|
|||
public override void ArcTo(double x, double y, double size, Corner corner) |
|||
{ |
|||
_geometryContext.ArcTo(new Point(x, y), new Size(size, size), 0, false, SweepDirection.Clockwise); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Close the geometry to so no more path adding is allowed and return the instance so it can be rendered.
|
|||
/// </summary>
|
|||
public StreamGeometry GetClosedGeometry() |
|||
{ |
|||
_geometryContext.EndFigure(true); |
|||
return _geometry; |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ } |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using Perspex.Media.Imaging; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex Image object for core.
|
|||
/// </summary>
|
|||
internal sealed class ImageAdapter : RImage |
|||
{ |
|||
/// <summary>
|
|||
/// the underline Perspex image.
|
|||
/// </summary>
|
|||
private readonly Bitmap _image; |
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
public ImageAdapter(Bitmap image) |
|||
{ |
|||
_image = image; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// the underline Perspex image.
|
|||
/// </summary>
|
|||
public Bitmap Image => _image; |
|||
|
|||
public override double Width => _image.PixelWidth; |
|||
|
|||
public override double Height => _image.PixelHeight; |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
//TODO: Implement
|
|||
/*if (_image.StreamSource != null) |
|||
_image.StreamSource.Dispose();*/ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using Perspex.Media; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
/// <summary>
|
|||
/// Adapter for Perspex pens objects for core.
|
|||
/// </summary>
|
|||
internal sealed class PenAdapter : RPen |
|||
{ |
|||
/// <summary>
|
|||
/// The actual Perspex brush instance.
|
|||
/// </summary>
|
|||
private readonly Brush _brush; |
|||
|
|||
/// <summary>
|
|||
/// the width of the pen
|
|||
/// </summary>
|
|||
private double _width; |
|||
|
|||
/// <summary>
|
|||
/// the dash style of the pen
|
|||
/// </summary>
|
|||
//private DashStyle _dashStyle = DashStyles.Solid;
|
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
public PenAdapter(Brush brush) |
|||
{ |
|||
_brush = brush; |
|||
} |
|||
|
|||
public override double Width |
|||
{ |
|||
get { return _width; } |
|||
set { _width = value; } |
|||
} |
|||
|
|||
public override RDashStyle DashStyle |
|||
{ |
|||
set |
|||
{ |
|||
//TODO: Implement DashStyles
|
|||
/* |
|||
switch (value) |
|||
{ |
|||
case RDashStyle.Solid: |
|||
_dashStyle = DashStyles.Solid; |
|||
break; |
|||
case RDashStyle.Dash: |
|||
_dashStyle = DashStyles.Dash; |
|||
break; |
|||
case RDashStyle.Dot: |
|||
_dashStyle = DashStyles.Dot; |
|||
break; |
|||
case RDashStyle.DashDot: |
|||
_dashStyle = DashStyles.DashDot; |
|||
break; |
|||
case RDashStyle.DashDotDot: |
|||
_dashStyle = DashStyles.DashDotDot; |
|||
break; |
|||
default: |
|||
_dashStyle = DashStyles.Solid; |
|||
break; |
|||
}*/ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create the actual Perspex pen instance.
|
|||
/// </summary>
|
|||
public Pen CreatePen() |
|||
{ |
|||
var pen = new Pen(_brush, _width); |
|||
//pen.DashStyle = _dashStyle;
|
|||
return pen; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Perspex; |
|||
using Perspex.Media; |
|||
using Perspex.Media.Imaging; |
|||
using TheArtOfDev.HtmlRenderer.Adapters; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
using TheArtOfDev.HtmlRenderer.Perspex.Utilities; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters |
|||
{ |
|||
class PerspexAdapter : RAdapter |
|||
{ |
|||
public static PerspexAdapter Instance { get; } = new PerspexAdapter(); |
|||
|
|||
/// <summary>
|
|||
/// List of valid predefined color names in lower-case
|
|||
/// </summary>
|
|||
private static readonly Dictionary<string, Color> ColorNameDic = new Dictionary<string, Color>(); |
|||
|
|||
|
|||
static PerspexAdapter() |
|||
{ |
|||
foreach (var colorProp in typeof(Colors).GetRuntimeProperties() |
|||
.Where(p=>p.PropertyType == typeof(Color))) |
|||
{ |
|||
ColorNameDic[colorProp.Name.ToLower()] = (Color)colorProp.GetValue(null); |
|||
} |
|||
} |
|||
|
|||
protected override RColor GetColorInt(string colorName) |
|||
{ |
|||
Color c; |
|||
if(!ColorNameDic.TryGetValue(colorName.ToLower(), out c)) |
|||
return RColor.Empty; |
|||
return Util.Convert(c); |
|||
} |
|||
|
|||
protected override RPen CreatePen(RColor color) |
|||
{ |
|||
return new PenAdapter(GetSolidColorBrush(color)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get solid color brush for the given color.
|
|||
/// </summary>
|
|||
private static Brush GetSolidColorBrush(RColor color) |
|||
{ |
|||
Brush solidBrush; |
|||
if (color == RColor.White) |
|||
solidBrush = Brushes.White; |
|||
else if (color == RColor.Black) |
|||
solidBrush = Brushes.Black; |
|||
else if (color.A < 1) |
|||
solidBrush = Brushes.Transparent; |
|||
else |
|||
solidBrush = new SolidColorBrush(Util.Convert(color)); |
|||
return solidBrush; |
|||
} |
|||
|
|||
protected override RBrush CreateSolidBrush(RColor color) |
|||
{ |
|||
return new BrushAdapter(GetSolidColorBrush(color)); |
|||
} |
|||
|
|||
protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle) |
|||
{ |
|||
var startColor = angle <= 180 ? Util.Convert(color1) : Util.Convert(color2); |
|||
var endColor = angle <= 180 ? Util.Convert(color2) : Util.Convert(color1); |
|||
angle = angle <= 180 ? angle : angle - 180; |
|||
double x = angle < 135 ? Math.Max((angle - 45) / 90, 0) : 1; |
|||
double y = angle <= 45 ? Math.Max(0.5 - angle / 90, 0) : angle > 135 ? Math.Abs(1.5 - angle / 90) : 0; |
|||
return new BrushAdapter(new LinearGradientBrush |
|||
{ |
|||
StartPoint = new Point(x, y), |
|||
EndPoint = new Point(1 - x, 1 - y), |
|||
GradientStops = |
|||
{ |
|||
new GradientStop(startColor, 0), |
|||
new GradientStop(endColor, 1) |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
protected override RImage ConvertImageInt(object image) |
|||
{ |
|||
return image != null ? new ImageAdapter((Bitmap)image) : null; |
|||
} |
|||
|
|||
protected override RImage ImageFromStreamInt(Stream memoryStream) |
|||
{ |
|||
//TODO: Implement bitmap loader
|
|||
return null; |
|||
} |
|||
|
|||
protected override RFont CreateFontInt(string family, double size, RFontStyle style) |
|||
{ |
|||
return new FontAdapter(family, size, style); |
|||
} |
|||
|
|||
protected override RFont CreateFontInt(RFontFamily family, double size, RFontStyle style) |
|||
{ |
|||
return new FontAdapter(family.Name, size, style); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
internal class CategoryAttribute : Attribute |
|||
{ |
|||
public CategoryAttribute(string s) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
internal class DescriptionAttribute : Attribute |
|||
{ |
|||
public DescriptionAttribute(string s) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
internal class BrowsableAttribute : Attribute |
|||
{ |
|||
public BrowsableAttribute(bool b) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using TheArtOfDev.HtmlRenderer.Core.Handlers; |
|||
|
|||
namespace System.Threading |
|||
{ |
|||
class ThreadPool |
|||
{ |
|||
public static void QueueUserWorkItem(Action<object> cb, object state) |
|||
{ |
|||
Task.Factory.StartNew(() => cb(state)); |
|||
} |
|||
|
|||
public static void QueueUserWorkItem(Action<object> cb) |
|||
{ |
|||
Task.Factory.StartNew(() => cb(null)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,468 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Perspex.Input; |
|||
using Perspex.Media; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
using TheArtOfDev.HtmlRenderer.Core; |
|||
using TheArtOfDev.HtmlRenderer.Core.Entities; |
|||
using TheArtOfDev.HtmlRenderer.Core.Parse; |
|||
using TheArtOfDev.HtmlRenderer.Core.Utils; |
|||
using TheArtOfDev.HtmlRenderer.Perspex.Adapters; |
|||
using TheArtOfDev.HtmlRenderer.Perspex.Utilities; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Low level handling of Html Renderer logic, this class is used by <see cref="HtmlParser"/>,
|
|||
/// <see cref="HtmlLabel"/>, <see cref="HtmlToolTip"/> and <see cref="HtmlRender"/>.<br/>
|
|||
/// </summary>
|
|||
/// <seealso cref="HtmlContainerInt"/>
|
|||
public sealed class HtmlContainer : IDisposable |
|||
{ |
|||
#region Fields and Consts
|
|||
|
|||
/// <summary>
|
|||
/// The internal core html container
|
|||
/// </summary>
|
|||
private readonly HtmlContainerInt _htmlContainerInt; |
|||
|
|||
#endregion
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
public HtmlContainer() |
|||
{ |
|||
_htmlContainerInt = new HtmlContainerInt(PerspexAdapter.Instance); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when the set html document has been fully loaded.<br/>
|
|||
/// Allows manipulation of the html dom, scroll position, etc.
|
|||
/// </summary>
|
|||
public event EventHandler LoadComplete |
|||
{ |
|||
add { _htmlContainerInt.LoadComplete += value; } |
|||
remove { _htmlContainerInt.LoadComplete -= value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when the user clicks on a link in the html.<br/>
|
|||
/// Allows canceling the execution of the link.
|
|||
/// </summary>
|
|||
public event EventHandler<HtmlLinkClickedEventArgs> LinkClicked |
|||
{ |
|||
add { _htmlContainerInt.LinkClicked += value; } |
|||
remove { _htmlContainerInt.LinkClicked -= value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when html renderer requires refresh of the control hosting (invalidation and re-layout).
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// There is no guarantee that the event will be raised on the main thread, it can be raised on thread-pool thread.
|
|||
/// </remarks>
|
|||
public event EventHandler<HtmlRefreshEventArgs> Refresh |
|||
{ |
|||
add { _htmlContainerInt.Refresh += value; } |
|||
remove { _htmlContainerInt.Refresh -= value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when Html Renderer request scroll to specific location.<br/>
|
|||
/// This can occur on document anchor click.
|
|||
/// </summary>
|
|||
public event EventHandler<HtmlScrollEventArgs> ScrollChange |
|||
{ |
|||
add { _htmlContainerInt.ScrollChange += value; } |
|||
remove { _htmlContainerInt.ScrollChange -= value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when an error occurred during html rendering.<br/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// There is no guarantee that the event will be raised on the main thread, it can be raised on thread-pool thread.
|
|||
/// </remarks>
|
|||
public event EventHandler<HtmlRenderErrorEventArgs> RenderError |
|||
{ |
|||
add { _htmlContainerInt.RenderError += value; } |
|||
remove { _htmlContainerInt.RenderError -= value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when a stylesheet is about to be loaded by file path or URI by link element.<br/>
|
|||
/// This event allows to provide the stylesheet manually or provide new source (file or Uri) to load from.<br/>
|
|||
/// If no alternative data is provided the original source will be used.<br/>
|
|||
/// </summary>
|
|||
public event EventHandler<HtmlStylesheetLoadEventArgs> StylesheetLoad |
|||
{ |
|||
add { _htmlContainerInt.StylesheetLoad += value; } |
|||
remove { _htmlContainerInt.StylesheetLoad -= value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when an image is about to be loaded by file path or URI.<br/>
|
|||
/// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI.
|
|||
/// </summary>
|
|||
public event EventHandler<HtmlImageLoadEventArgs> ImageLoad |
|||
{ |
|||
add { _htmlContainerInt.ImageLoad += value; } |
|||
remove { _htmlContainerInt.ImageLoad -= value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The internal core html container
|
|||
/// </summary>
|
|||
internal HtmlContainerInt HtmlContainerInt |
|||
{ |
|||
get { return _htmlContainerInt; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// the parsed stylesheet data used for handling the html
|
|||
/// </summary>
|
|||
public CssData CssData |
|||
{ |
|||
get { return _htmlContainerInt.CssData; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating if image asynchronous loading should be avoided (default - false).<br/>
|
|||
/// True - images are loaded synchronously during html parsing.<br/>
|
|||
/// False - images are loaded asynchronously to html parsing when downloaded from URL or loaded from disk.<br/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Asynchronously image loading allows to unblock html rendering while image is downloaded or loaded from disk using IO
|
|||
/// ports to achieve better performance.<br/>
|
|||
/// Asynchronously image loading should be avoided when the full html content must be available during render, like render to image.
|
|||
/// </remarks>
|
|||
public bool AvoidAsyncImagesLoading |
|||
{ |
|||
get { return _htmlContainerInt.AvoidAsyncImagesLoading; } |
|||
set { _htmlContainerInt.AvoidAsyncImagesLoading = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating if image loading only when visible should be avoided (default - false).<br/>
|
|||
/// True - images are loaded as soon as the html is parsed.<br/>
|
|||
/// False - images that are not visible because of scroll location are not loaded until they are scrolled to.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Images late loading improve performance if the page contains image outside the visible scroll area, especially if there is large
|
|||
/// amount of images, as all image loading is delayed (downloading and loading into memory).<br/>
|
|||
/// Late image loading may effect the layout and actual size as image without set size will not have actual size until they are loaded
|
|||
/// resulting in layout change during user scroll.<br/>
|
|||
/// Early image loading may also effect the layout if image without known size above the current scroll location are loaded as they
|
|||
/// will push the html elements down.
|
|||
/// </remarks>
|
|||
public bool AvoidImagesLateLoading |
|||
{ |
|||
get { return _htmlContainerInt.AvoidImagesLateLoading; } |
|||
set { _htmlContainerInt.AvoidImagesLateLoading = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Is content selection is enabled for the rendered html (default - true).<br/>
|
|||
/// If set to 'false' the rendered html will be static only with ability to click on links.
|
|||
/// </summary>
|
|||
public bool IsSelectionEnabled |
|||
{ |
|||
get { return _htmlContainerInt.IsSelectionEnabled; } |
|||
set { _htmlContainerInt.IsSelectionEnabled = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Is the build-in context menu enabled and will be shown on mouse right click (default - true)
|
|||
/// </summary>
|
|||
public bool IsContextMenuEnabled |
|||
{ |
|||
get { return _htmlContainerInt.IsContextMenuEnabled; } |
|||
set { _htmlContainerInt.IsContextMenuEnabled = value; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The scroll offset of the html.<br/>
|
|||
/// This will adjust the rendered html by the given offset so the content will be "scrolled".<br/>
|
|||
/// </summary>
|
|||
/// <example>
|
|||
/// Element that is rendered at location (50,100) with offset of (0,200) will not be rendered as it
|
|||
/// will be at -100 therefore outside the client Rect.
|
|||
/// </example>
|
|||
public Point ScrollOffset |
|||
{ |
|||
get { return Util.Convert(_htmlContainerInt.ScrollOffset); } |
|||
set { _htmlContainerInt.ScrollOffset = Util.Convert(value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The top-left most location of the rendered html.<br/>
|
|||
/// This will offset the top-left corner of the rendered html.
|
|||
/// </summary>
|
|||
public Point Location |
|||
{ |
|||
get { return Util.Convert(_htmlContainerInt.Location); } |
|||
set { _htmlContainerInt.Location = Util.Convert(value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The max width and height of the rendered html.<br/>
|
|||
/// The max width will effect the html layout wrapping lines, resize images and tables where possible.<br/>
|
|||
/// The max height does NOT effect layout, but will not render outside it (clip).<br/>
|
|||
/// <see cref="ActualSize"/> can be exceed the max size by layout restrictions (unwrappable line, set image size, etc.).<br/>
|
|||
/// Set zero for unlimited (width\height separately).<br/>
|
|||
/// </summary>
|
|||
public Size MaxSize |
|||
{ |
|||
get { return Util.Convert(_htmlContainerInt.MaxSize); } |
|||
set { _htmlContainerInt.MaxSize = Util.Convert(value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The actual size of the rendered html (after layout)
|
|||
/// </summary>
|
|||
public Size ActualSize |
|||
{ |
|||
get { return Util.Convert(_htmlContainerInt.ActualSize); } |
|||
internal set { _htmlContainerInt.ActualSize = Util.Convert(value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get the currently selected text segment in the html.
|
|||
/// </summary>
|
|||
public string SelectedText |
|||
{ |
|||
get { return _htmlContainerInt.SelectedText; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copy the currently selected html segment with style.
|
|||
/// </summary>
|
|||
public string SelectedHtml |
|||
{ |
|||
get { return _htmlContainerInt.SelectedHtml; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clear the current selection.
|
|||
/// </summary>
|
|||
public void ClearSelection() |
|||
{ |
|||
HtmlContainerInt.ClearSelection(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Init with optional document and stylesheet.
|
|||
/// </summary>
|
|||
/// <param name="htmlSource">the html to init with, init empty if not given</param>
|
|||
/// <param name="baseCssData">optional: the stylesheet to init with, init default if not given</param>
|
|||
public void SetHtml(string htmlSource, CssData baseCssData = null) |
|||
{ |
|||
_htmlContainerInt.SetHtml(htmlSource, baseCssData); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clear the content of the HTML container releasing any resources used to render previously existing content.
|
|||
/// </summary>
|
|||
public void Clear() |
|||
{ |
|||
_htmlContainerInt.Clear(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get html from the current DOM tree with style if requested.
|
|||
/// </summary>
|
|||
/// <param name="styleGen">Optional: controls the way styles are generated when html is generated (default: <see cref="HtmlGenerationStyle.Inline"/>)</param>
|
|||
/// <returns>generated html</returns>
|
|||
public string GetHtml(HtmlGenerationStyle styleGen = HtmlGenerationStyle.Inline) |
|||
{ |
|||
return _htmlContainerInt.GetHtml(styleGen); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get attribute value of element at the given x,y location by given key.<br/>
|
|||
/// If more than one element exist with the attribute at the location the inner most is returned.
|
|||
/// </summary>
|
|||
/// <param name="location">the location to find the attribute at</param>
|
|||
/// <param name="attribute">the attribute key to get value by</param>
|
|||
/// <returns>found attribute value or null if not found</returns>
|
|||
public string GetAttributeAt(Point location, string attribute) |
|||
{ |
|||
return _htmlContainerInt.GetAttributeAt(Util.Convert(location), attribute); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get all the links in the HTML with the element Rect and href data.
|
|||
/// </summary>
|
|||
/// <returns>collection of all the links in the HTML</returns>
|
|||
public List<LinkElementData<Rect>> GetLinks() |
|||
{ |
|||
var linkElements = new List<LinkElementData<Rect>>(); |
|||
foreach (var link in HtmlContainerInt.GetLinks()) |
|||
{ |
|||
linkElements.Add(new LinkElementData<Rect>(link.Id, link.Href, Util.Convert(link.Rectangle))); |
|||
} |
|||
return linkElements; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get css link href at the given x,y location.
|
|||
/// </summary>
|
|||
/// <param name="location">the location to find the link at</param>
|
|||
/// <returns>css link href if exists or null</returns>
|
|||
public string GetLinkAt(Point location) |
|||
{ |
|||
return _htmlContainerInt.GetLinkAt(Util.Convert(location)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get the Rect of html element as calculated by html layout.<br/>
|
|||
/// Element if found by id (id attribute on the html element).<br/>
|
|||
/// Note: to get the screen Rect you need to adjust by the hosting control.<br/>
|
|||
/// </summary>
|
|||
/// <param name="elementId">the id of the element to get its Rect</param>
|
|||
/// <returns>the Rect of the element or null if not found</returns>
|
|||
public Rect? GetElementRectangle(string elementId) |
|||
{ |
|||
var r = _htmlContainerInt.GetElementRectangle(elementId); |
|||
return r.HasValue ? Util.Convert(r.Value) : (Rect?)null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Measures the bounds of box and children, recursively.
|
|||
/// </summary>
|
|||
public void PerformLayout() |
|||
{ |
|||
using (var ig = new GraphicsAdapter()) |
|||
{ |
|||
_htmlContainerInt.PerformLayout(ig); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Render the html using the given device.
|
|||
/// </summary>
|
|||
/// <param name="g">the device to use to render</param>
|
|||
/// <param name="clip">the clip rectangle of the html container</param>
|
|||
public void PerformPaint(IDrawingContext g, Rect clip) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(g, "g"); |
|||
|
|||
using (var ig = new GraphicsAdapter(g, Util.Convert(clip))) |
|||
{ |
|||
_htmlContainerInt.PerformPaint(ig); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle mouse down to handle selection.
|
|||
/// </summary>
|
|||
/// <param name="parent">the control hosting the html to invalidate</param>
|
|||
/// <param name="e">the mouse event args</param>
|
|||
public void HandleLeftMouseDown(Control parent, PointerEventArgs e) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(parent, "parent"); |
|||
ArgChecker.AssertArgNotNull(e, "e"); |
|||
|
|||
_htmlContainerInt.HandleMouseDown(new ControlAdapter(parent), Util.Convert(e.GetPosition(parent))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle mouse up to handle selection and link click.
|
|||
/// </summary>
|
|||
/// <param name="parent">the control hosting the html to invalidate</param>
|
|||
/// <param name="e">the mouse event args</param>
|
|||
public void HandleLeftMouseUp(Control parent, PointerEventArgs e) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(parent, "parent"); |
|||
ArgChecker.AssertArgNotNull(e, "e"); |
|||
|
|||
var mouseEvent = new RMouseEvent(true); |
|||
_htmlContainerInt.HandleMouseUp(new ControlAdapter(parent), Util.Convert(e.GetPosition(parent)), mouseEvent); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle mouse double click to select word under the mouse.
|
|||
/// </summary>
|
|||
/// <param name="parent">the control hosting the html to set cursor and invalidate</param>
|
|||
/// <param name="e">mouse event args</param>
|
|||
public void HandleMouseDoubleClick(Control parent, PointerEventArgs e) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(parent, "parent"); |
|||
ArgChecker.AssertArgNotNull(e, "e"); |
|||
|
|||
_htmlContainerInt.HandleMouseDoubleClick(new ControlAdapter(parent), Util.Convert(e.GetPosition(parent))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle mouse move to handle hover cursor and text selection.
|
|||
/// </summary>
|
|||
/// <param name="parent">the control hosting the html to set cursor and invalidate</param>
|
|||
/// <param name="mousePos">the mouse event args</param>
|
|||
public void HandleMouseMove(Control parent, Point mousePos) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(parent, "parent"); |
|||
|
|||
_htmlContainerInt.HandleMouseMove(new ControlAdapter(parent), Util.Convert(mousePos)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle mouse leave to handle hover cursor.
|
|||
/// </summary>
|
|||
/// <param name="parent">the control hosting the html to set cursor and invalidate</param>
|
|||
public void HandleMouseLeave(Control parent) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(parent, "parent"); |
|||
|
|||
_htmlContainerInt.HandleMouseLeave(new ControlAdapter(parent)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle key down event for selection and copy.
|
|||
/// </summary>
|
|||
/// <param name="parent">the control hosting the html to invalidate</param>
|
|||
/// <param name="e">the pressed key</param>
|
|||
public void HandleKeyDown(Control parent, KeyEventArgs e) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(parent, "parent"); |
|||
ArgChecker.AssertArgNotNull(e, "e"); |
|||
|
|||
_htmlContainerInt.HandleKeyDown(new ControlAdapter(parent), CreateKeyEevent(e)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
_htmlContainerInt.Dispose(); |
|||
} |
|||
|
|||
|
|||
#region Private methods
|
|||
|
|||
/// <summary>
|
|||
/// Create HtmlRenderer key event from Perspex key event.
|
|||
/// </summary>
|
|||
private static RKeyEvent CreateKeyEevent(KeyEventArgs e) |
|||
{ |
|||
var control = (e.Modifiers & ModifierKeys.Control) == ModifierKeys.Control; |
|||
return new RKeyEvent(control, e.Key == Key.A, e.Key == Key.C); |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,620 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using System; |
|||
using Perspex.Controls.Primitives; |
|||
using Perspex.HtmlRenderer; |
|||
using Perspex.Input; |
|||
using Perspex.Interactivity; |
|||
using Perspex.Media; |
|||
using Perspex.Threading; |
|||
using TheArtOfDev.HtmlRenderer.Core; |
|||
using TheArtOfDev.HtmlRenderer.Core.Entities; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Provides HTML rendering using the text property.<br/>
|
|||
/// Perspex control that will render html content in it's client rectangle.<br/>
|
|||
/// The control will handle mouse and keyboard events on it to support html text selection, copy-paste and mouse clicks.<br/>
|
|||
/// <para>
|
|||
/// The major differential to use HtmlPanel or HtmlLabel is size and scrollbars.<br/>
|
|||
/// If the size of the control depends on the html content the HtmlLabel should be used.<br/>
|
|||
/// If the size is set by some kind of layout then HtmlPanel is more suitable, also shows scrollbars if the html contents is larger than the control client rectangle.<br/>
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// <h4>LinkClicked event:</h4>
|
|||
/// Raised when the user clicks on a link in the html.<br/>
|
|||
/// Allows canceling the execution of the link.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// <h4>StylesheetLoad event:</h4>
|
|||
/// Raised when a stylesheet is about to be loaded by file path or URI by link element.<br/>
|
|||
/// This event allows to provide the stylesheet manually or provide new source (file or uri) to load from.<br/>
|
|||
/// If no alternative data is provided the original source will be used.<br/>
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// <h4>ImageLoad event:</h4>
|
|||
/// Raised when an image is about to be loaded by file path or URI.<br/>
|
|||
/// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// <h4>RenderError event:</h4>
|
|||
/// Raised when an error occurred during html rendering.<br/>
|
|||
/// </para>
|
|||
/// </summary>
|
|||
|
|||
public class HtmlControl : Control |
|||
{ |
|||
#region Fields and Consts
|
|||
|
|||
/// <summary>
|
|||
/// Underline html container instance.
|
|||
/// </summary>
|
|||
protected readonly HtmlContainer _htmlContainer; |
|||
|
|||
/// <summary>
|
|||
/// the base stylesheet data used in the control
|
|||
/// </summary>
|
|||
protected CssData _baseCssData; |
|||
|
|||
/// <summary>
|
|||
/// The last position of the scrollbars to know if it has changed to update mouse
|
|||
/// </summary>
|
|||
protected Point _lastScrollOffset; |
|||
|
|||
#endregion
|
|||
|
|||
|
|||
#region Dependency properties / routed events
|
|||
|
|||
public static readonly PerspexProperty AvoidImagesLateLoadingProperty = |
|||
PropertyHelper.Register<HtmlControl, bool>("AvoidImagesLateLoading", false, OnPerspexProperty_valueChanged); |
|||
public static readonly PerspexProperty IsSelectionEnabledProperty = |
|||
PropertyHelper.Register<HtmlControl, bool>("IsSelectionEnabled", true, OnPerspexProperty_valueChanged); |
|||
public static readonly PerspexProperty IsContextMenuEnabledProperty = |
|||
PropertyHelper.Register<HtmlControl, bool>("IsContextMenuEnabled", true, OnPerspexProperty_valueChanged); |
|||
|
|||
public static readonly PerspexProperty BaseStylesheetProperty = |
|||
PropertyHelper.Register<HtmlControl, string>("BaseStylesheet", null, OnPerspexProperty_valueChanged); |
|||
|
|||
public static readonly PerspexProperty TextProperty = |
|||
PropertyHelper.Register<HtmlControl, string>("Text", null, OnPerspexProperty_valueChanged); |
|||
|
|||
public static readonly PerspexProperty BackgroundProperty = |
|||
PerspexProperty.Register<HtmlControl, Color>("Background", Colors.White); |
|||
|
|||
public static readonly PerspexProperty BorderThicknessProperty = |
|||
PerspexProperty.Register<HtmlControl, Thickness>("BorderThickness", new Thickness(0)); |
|||
|
|||
public static readonly PerspexProperty PaddingProperty = |
|||
PerspexProperty.Register<HtmlControl, Thickness>("Padding", new Thickness(0)); |
|||
|
|||
public static readonly RoutedEvent LoadCompleteEvent = |
|||
RoutedEvent.Register<RoutedEventArgs>("LoadComplete", RoutingStrategies.Bubble, typeof(HtmlControl)); |
|||
public static readonly RoutedEvent LinkClickedEvent = |
|||
RoutedEvent.Register<RoutedEventArgsWrapper<HtmlLinkClickedEventArgs>>("LinkClicked", RoutingStrategies.Bubble, typeof(HtmlControl)); |
|||
public static readonly RoutedEvent RenderErrorEvent |
|||
= RoutedEvent.Register<RoutedEventArgsWrapper<HtmlRenderErrorEventArgs>>("RenderError", RoutingStrategies.Bubble, typeof(HtmlControl)); |
|||
public static readonly RoutedEvent RefreshEvent |
|||
= RoutedEvent.Register< RoutedEventArgsWrapper<HtmlRefreshEventArgs>>("Refresh", RoutingStrategies.Bubble, typeof(HtmlControl)); |
|||
public static readonly RoutedEvent StylesheetLoadEvent |
|||
= RoutedEvent.Register<RoutedEventArgsWrapper<HtmlStylesheetLoadEventArgs>>("StylesheetLoad", RoutingStrategies.Bubble, typeof(HtmlControl)); |
|||
|
|||
public static readonly RoutedEvent ImageLoadEvent |
|||
= RoutedEvent.Register<RoutedEventArgsWrapper<HtmlImageLoadEventArgs>>("ImageLoad", RoutingStrategies.Bubble, |
|||
typeof (HtmlControl)); |
|||
|
|||
#endregion
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Creates a new HtmlPanel and sets a basic css for it's styling.
|
|||
/// </summary>
|
|||
protected HtmlControl() |
|||
{ |
|||
_htmlContainer = new HtmlContainer();/* |
|||
_htmlContainer.LoadComplete += OnLoadComplete; |
|||
_htmlContainer.LinkClicked += OnLinkClicked; |
|||
_htmlContainer.RenderError += OnRenderError; |
|||
_htmlContainer.Refresh += OnRefresh; |
|||
_htmlContainer.StylesheetLoad += OnStylesheetLoad; |
|||
_htmlContainer.ImageLoad += OnImageLoad;*/ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when the set html document has been fully loaded.<br/>
|
|||
/// Allows manipulation of the html dom, scroll position, etc.
|
|||
/// </summary>
|
|||
public event EventHandler LoadComplete |
|||
{ |
|||
add { AddHandler(LoadCompleteEvent, value); } |
|||
remove { RemoveHandler(LoadCompleteEvent, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when the user clicks on a link in the html.<br/>
|
|||
/// Allows canceling the execution of the link.
|
|||
/// </summary>
|
|||
public event EventHandler<HtmlLinkClickedEventArgs> LinkClicked |
|||
{ |
|||
add { AddHandler(LinkClickedEvent, value); } |
|||
remove { RemoveHandler(LinkClickedEvent, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when an error occurred during html rendering.<br/>
|
|||
/// </summary>
|
|||
public event EventHandler<HtmlRenderErrorEventArgs> RenderError |
|||
{ |
|||
add { AddHandler(RenderErrorEvent, value); } |
|||
remove { RemoveHandler(RenderErrorEvent, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when a stylesheet is about to be loaded by file path or URI by link element.<br/>
|
|||
/// This event allows to provide the stylesheet manually or provide new source (file or uri) to load from.<br/>
|
|||
/// If no alternative data is provided the original source will be used.<br/>
|
|||
/// </summary>
|
|||
public event EventHandler<HtmlStylesheetLoadEventArgs> StylesheetLoad |
|||
{ |
|||
add { AddHandler(StylesheetLoadEvent, value); } |
|||
remove { RemoveHandler(StylesheetLoadEvent, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Raised when an image is about to be loaded by file path or URI.<br/>
|
|||
/// This event allows to provide the image manually, if not handled the image will be loaded from file or download from URI.
|
|||
/// </summary>
|
|||
public event EventHandler<HtmlImageLoadEventArgs> ImageLoad |
|||
{ |
|||
add { AddHandler(ImageLoadEvent, value); } |
|||
remove { RemoveHandler(ImageLoadEvent, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets a value indicating if image loading only when visible should be avoided (default - false).<br/>
|
|||
/// True - images are loaded as soon as the html is parsed.<br/>
|
|||
/// False - images that are not visible because of scroll location are not loaded until they are scrolled to.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Images late loading improve performance if the page contains image outside the visible scroll area, especially if there is large
|
|||
/// amount of images, as all image loading is delayed (downloading and loading into memory).<br/>
|
|||
/// Late image loading may effect the layout and actual size as image without set size will not have actual size until they are loaded
|
|||
/// resulting in layout change during user scroll.<br/>
|
|||
/// Early image loading may also effect the layout if image without known size above the current scroll location are loaded as they
|
|||
/// will push the html elements down.
|
|||
/// </remarks>
|
|||
[Category("Behavior")] |
|||
[Description("If image loading only when visible should be avoided")] |
|||
public bool AvoidImagesLateLoading |
|||
{ |
|||
get { return (bool)GetValue(AvoidImagesLateLoadingProperty); } |
|||
set { SetValue(AvoidImagesLateLoadingProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Is content selection is enabled for the rendered html (default - true).<br/>
|
|||
/// If set to 'false' the rendered html will be static only with ability to click on links.
|
|||
/// </summary>
|
|||
[Category("Behavior")] |
|||
[Description("Is content selection is enabled for the rendered html.")] |
|||
public bool IsSelectionEnabled |
|||
{ |
|||
get { return (bool)GetValue(IsSelectionEnabledProperty); } |
|||
set { SetValue(IsSelectionEnabledProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Is the build-in context menu enabled and will be shown on mouse right click (default - true)
|
|||
/// </summary>
|
|||
[Category("Behavior")] |
|||
[Description("Is the build-in context menu enabled and will be shown on mouse right click.")] |
|||
public bool IsContextMenuEnabled |
|||
{ |
|||
get { return (bool)GetValue(IsContextMenuEnabledProperty); } |
|||
set { SetValue(IsContextMenuEnabledProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Set base stylesheet to be used by html rendered in the panel.
|
|||
/// </summary>
|
|||
[Category("Appearance")] |
|||
[Description("Set base stylesheet to be used by html rendered in the control.")] |
|||
public string BaseStylesheet |
|||
{ |
|||
get { return (string)GetValue(BaseStylesheetProperty); } |
|||
set { SetValue(BaseStylesheetProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the text of this panel
|
|||
/// </summary>
|
|||
[Description("Sets the html of this control.")] |
|||
public string Text |
|||
{ |
|||
get { return (string)GetValue(TextProperty); } |
|||
set { SetValue(TextProperty, value); } |
|||
} |
|||
|
|||
public Thickness BorderThickness |
|||
{ |
|||
get { return (Thickness) GetValue(BorderThicknessProperty); } |
|||
set { SetValue(BorderThicknessProperty, value); } |
|||
} |
|||
|
|||
public Thickness Padding |
|||
{ |
|||
get { return (Thickness)GetValue(PaddingProperty); } |
|||
set { SetValue(PaddingProperty, value); } |
|||
} |
|||
|
|||
public Color Background |
|||
{ |
|||
get { return (Color?) GetValue(BackgroundProperty) ?? Colors.White; } |
|||
set { SetValue(BackgroundProperty, value);} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get the currently selected text segment in the html.
|
|||
/// </summary>
|
|||
[Browsable(false)] |
|||
public virtual string SelectedText |
|||
{ |
|||
get { return _htmlContainer.SelectedText; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copy the currently selected html segment with style.
|
|||
/// </summary>
|
|||
[Browsable(false)] |
|||
public virtual string SelectedHtml |
|||
{ |
|||
get { return _htmlContainer.SelectedHtml; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get html from the current DOM tree with inline style.
|
|||
/// </summary>
|
|||
/// <returns>generated html</returns>
|
|||
public virtual string GetHtml() |
|||
{ |
|||
return _htmlContainer != null ? _htmlContainer.GetHtml() : null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get the rectangle of html element as calculated by html layout.<br/>
|
|||
/// Element if found by id (id attribute on the html element).<br/>
|
|||
/// Note: to get the screen rectangle you need to adjust by the hosting control.<br/>
|
|||
/// </summary>
|
|||
/// <param name="elementId">the id of the element to get its rectangle</param>
|
|||
/// <returns>the rectangle of the element or null if not found</returns>
|
|||
public virtual Rect? GetElementRectangle(string elementId) |
|||
{ |
|||
return _htmlContainer != null ? _htmlContainer.GetElementRectangle(elementId) : null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clear the current selection.
|
|||
/// </summary>
|
|||
public void ClearSelection() |
|||
{ |
|||
if (_htmlContainer != null) |
|||
_htmlContainer.ClearSelection(); |
|||
} |
|||
|
|||
|
|||
#region Private methods
|
|||
|
|||
//HACK: We don't have support for RenderSize for now
|
|||
private Size RenderSize => new Size(Bounds.Width, Bounds.Height); |
|||
|
|||
|
|||
public override void Render(IDrawingContext context) |
|||
{ |
|||
//TODO: Use actual background
|
|||
context.FillRectange(new SolidColorBrush(Colors.White), new Rect(RenderSize)); |
|||
/*if (Background.Opacity > 0) |
|||
context.DrawRectangle(Background, null, new Rect(RenderSize)); |
|||
*/ |
|||
|
|||
if (BorderThickness != new Thickness(0)) |
|||
{ |
|||
//TODO: Get default border brush from theme or something
|
|||
var brush = new SolidColorBrush(Colors.Black); |
|||
if (BorderThickness.Top > 0) |
|||
context.FillRectange(brush, new Rect(0, 0, RenderSize.Width, BorderThickness.Top)); |
|||
if (BorderThickness.Bottom > 0) |
|||
context.FillRectange(brush, new Rect(0, RenderSize.Height - BorderThickness.Bottom, RenderSize.Width, BorderThickness.Bottom)); |
|||
if (BorderThickness.Left > 0) |
|||
context.FillRectange(brush, new Rect(0, 0, BorderThickness.Left, RenderSize.Height)); |
|||
if (BorderThickness.Right > 0) |
|||
context.FillRectange(brush, new Rect(RenderSize.Width - BorderThickness.Right, 0, BorderThickness.Right, RenderSize.Height)); |
|||
} |
|||
|
|||
var htmlWidth = HtmlWidth(RenderSize); |
|||
var htmlHeight = HtmlHeight(RenderSize); |
|||
if (_htmlContainer != null && htmlWidth > 0 && htmlHeight > 0) |
|||
{ |
|||
/* |
|||
//TODO: Rever antialiasing fixes
|
|||
var windows = Window.GetWindow(this); |
|||
if (windows != null) |
|||
{ |
|||
// adjust render location to round point so we won't get anti-alias smugness
|
|||
var wPoint = TranslatePoint(new Point(0, 0), windows); |
|||
wPoint.Offset(-(int)wPoint.X, -(int)wPoint.Y); |
|||
var xTrans = wPoint.X < .5 ? -wPoint.X : 1 - wPoint.X; |
|||
var yTrans = wPoint.Y < .5 ? -wPoint.Y : 1 - wPoint.Y; |
|||
context.PushTransform(new TranslateTransform(xTrans, yTrans)); |
|||
}*/ |
|||
|
|||
using (context.PushClip(new Rect(Padding.Left + BorderThickness.Left, Padding.Top + BorderThickness.Top, |
|||
htmlWidth, (int) htmlHeight))) |
|||
{ |
|||
_htmlContainer.Location = new Point(Padding.Left + BorderThickness.Left, |
|||
Padding.Top + BorderThickness.Top); |
|||
_htmlContainer.PerformPaint(context, |
|||
new Rect(Padding.Left + BorderThickness.Left, Padding.Top + BorderThickness.Top, htmlWidth, |
|||
htmlHeight)); |
|||
} |
|||
|
|||
if (!_lastScrollOffset.Equals(_htmlContainer.ScrollOffset)) |
|||
{ |
|||
_lastScrollOffset = _htmlContainer.ScrollOffset; |
|||
InvokeMouseMove(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle mouse move to handle hover cursor and text selection.
|
|||
/// </summary>
|
|||
protected override void OnPointerMoved(PointerEventArgs e) |
|||
{ |
|||
base.OnPointerMoved(e); |
|||
if (_htmlContainer != null) |
|||
_htmlContainer.HandleMouseMove(this, e.GetPosition(this)); |
|||
} |
|||
/// <summary>
|
|||
/// Handle mouse leave to handle cursor change.
|
|||
/// </summary>
|
|||
protected override void OnPointerLeave(PointerEventArgs e) |
|||
{ |
|||
base.OnPointerLeave(e); |
|||
if (_htmlContainer != null) |
|||
_htmlContainer.HandleMouseLeave(this); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle mouse down to handle selection.
|
|||
/// </summary>
|
|||
protected override void OnPointerPressed(PointerPressEventArgs e) |
|||
{ |
|||
base.OnPointerPressed(e); |
|||
_htmlContainer?.HandleLeftMouseDown(this, e); |
|||
} |
|||
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Handle mouse up to handle selection and link click.
|
|||
/// </summary>
|
|||
protected override void OnPointerReleased(PointerEventArgs e) |
|||
{ |
|||
base.OnPointerReleased(e); |
|||
if (_htmlContainer != null) |
|||
_htmlContainer.HandleLeftMouseUp(this, e); |
|||
} |
|||
|
|||
//TODO: Implement double click
|
|||
/* |
|||
/// <summary>
|
|||
/// Handle mouse double click to select word under the mouse.
|
|||
/// </summary>
|
|||
protected override void OnMouseDoubleClick(MouseButtonEventArgs e) |
|||
{ |
|||
base.OnMouseDoubleClick(e); |
|||
if (_htmlContainer != null) |
|||
_htmlContainer.HandleMouseDoubleClick(this, e); |
|||
} |
|||
*/ |
|||
/// <summary>
|
|||
/// Handle key down event for selection, copy and scrollbars handling.
|
|||
/// </summary>
|
|||
protected override void OnKeyDown(KeyEventArgs e) |
|||
{ |
|||
base.OnKeyDown(e); |
|||
if (_htmlContainer != null) |
|||
_htmlContainer.HandleKeyDown(this, e); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Propagate the LoadComplete event from root container.
|
|||
/// </summary>
|
|||
protected virtual void OnLoadComplete(EventArgs e) |
|||
{ |
|||
//RoutedEventArgs newEventArgs = new RoutedEventArgs<EventArgs>(LoadCompleteEvent, this, e);
|
|||
//RaiseEvent(newEventArgs);
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Propagate the LinkClicked event from root container.
|
|||
/// </summary>
|
|||
protected virtual void OnLinkClicked(HtmlLinkClickedEventArgs e) |
|||
{ |
|||
//RoutedEventArgs newEventArgs = new RoutedEvenArgs<HtmlLinkClickedEventArgs>(LinkClickedEvent, this, e);
|
|||
//RaiseEvent(newEventArgs);
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Propagate the Render Error event from root container.
|
|||
/// </summary>
|
|||
protected virtual void OnRenderError(HtmlRenderErrorEventArgs e) |
|||
{ |
|||
//RoutedEventArgs newEventArgs = new RoutedEvenArgs<HtmlRenderErrorEventArgs>(RenderErrorEvent, this, e);
|
|||
//RaiseEvent(newEventArgs);
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Propagate the stylesheet load event from root container.
|
|||
/// </summary>
|
|||
protected virtual void OnStylesheetLoad(HtmlStylesheetLoadEventArgs e) |
|||
{ |
|||
//RoutedEventArgs newEventArgs = new RoutedEvenArgs<HtmlStylesheetLoadEventArgs>(StylesheetLoadEvent, this, e);
|
|||
//RaiseEvent(newEventArgs);
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Propagate the image load event from root container.
|
|||
/// </summary>
|
|||
protected virtual void OnImageLoad(HtmlImageLoadEventArgs e) |
|||
{ |
|||
//RoutedEventArgs newEventArgs = new RoutedEvenArgs<HtmlImageLoadEventArgs>(ImageLoadEvent, this, e);
|
|||
//RaiseEvent(newEventArgs);
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle html renderer invalidate and re-layout as requested.
|
|||
/// </summary>
|
|||
protected virtual void OnRefresh(HtmlRefreshEventArgs e) |
|||
{ |
|||
if (e.Layout) |
|||
InvalidateMeasure(); |
|||
InvalidateVisual(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get the width the HTML has to render in (not including vertical scroll iff it is visible)
|
|||
/// </summary>
|
|||
protected virtual double HtmlWidth(Size size) |
|||
{ |
|||
return size.Width - Padding.Left - Padding.Right - BorderThickness.Left - BorderThickness.Right; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Get the width the HTML has to render in (not including vertical scroll iff it is visible)
|
|||
/// </summary>
|
|||
protected virtual double HtmlHeight(Size size) |
|||
{ |
|||
return size.Height - Padding.Top - Padding.Bottom - BorderThickness.Top - BorderThickness.Bottom; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// call mouse move to handle paint after scroll or html change affecting mouse cursor.
|
|||
/// </summary>
|
|||
protected virtual void InvokeMouseMove() |
|||
{ |
|||
|
|||
_htmlContainer.HandleMouseMove(this, MouseDevice.Instance.GetPosition(this)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle when dependency property value changes to update the underline HtmlContainer with the new value.
|
|||
/// </summary>
|
|||
private static void OnPerspexProperty_valueChanged(PerspexObject PerspexObject, PerspexPropertyChangedEventArgs e) |
|||
{ |
|||
var control = PerspexObject as HtmlControl; |
|||
if (control != null) |
|||
{ |
|||
var htmlContainer = control._htmlContainer; |
|||
if (e.Property == AvoidImagesLateLoadingProperty) |
|||
{ |
|||
htmlContainer.AvoidImagesLateLoading = (bool)e.NewValue; |
|||
} |
|||
else if (e.Property == IsSelectionEnabledProperty) |
|||
{ |
|||
htmlContainer.IsSelectionEnabled = (bool)e.NewValue; |
|||
} |
|||
else if (e.Property == IsContextMenuEnabledProperty) |
|||
{ |
|||
htmlContainer.IsContextMenuEnabled = (bool)e.NewValue; |
|||
} |
|||
else if (e.Property == BaseStylesheetProperty) |
|||
{ |
|||
var baseCssData = HtmlRender.ParseStyleSheet((string)e.NewValue); |
|||
control._baseCssData = baseCssData; |
|||
htmlContainer.SetHtml(control.Text, baseCssData); |
|||
} |
|||
else if (e.Property == TextProperty) |
|||
{ |
|||
htmlContainer.ScrollOffset = new Point(0, 0); |
|||
htmlContainer.SetHtml((string)e.NewValue, control._baseCssData); |
|||
control.InvalidateMeasure(); |
|||
control.InvalidateVisual(); |
|||
control.InvokeMouseMove(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
#region Private event handlers
|
|||
|
|||
|
|||
/* TODO: Implement events |
|||
private void OnLoadComplete(object sender, EventArgs e) |
|||
{ |
|||
|
|||
if (CheckAccess()) |
|||
OnLoadComplete(e); |
|||
else |
|||
Dispatcher.Invoke(new Action<HtmlLinkClickedEventArgs>(OnLinkClicked), e); |
|||
|
|||
} |
|||
|
|||
private void OnLinkClicked(object sender, HtmlLinkClickedEventArgs e) |
|||
{ |
|||
if (CheckAccess()) |
|||
OnLinkClicked(e); |
|||
else |
|||
Dispatcher.Invoke(new Action<HtmlLinkClickedEventArgs>(OnLinkClicked), e); |
|||
} |
|||
|
|||
private void OnRenderError(object sender, HtmlRenderErrorEventArgs e) |
|||
{ |
|||
if (CheckAccess()) |
|||
OnRenderError(e); |
|||
else |
|||
Dispatcher.Invoke(new Action<HtmlRenderErrorEventArgs>(OnRenderError), e); |
|||
} |
|||
|
|||
private void OnStylesheetLoad(object sender, HtmlStylesheetLoadEventArgs e) |
|||
{ |
|||
if (CheckAccess()) |
|||
OnStylesheetLoad(e); |
|||
else |
|||
Dispatcher.Invoke(new Action<HtmlStylesheetLoadEventArgs>(OnStylesheetLoad), e); |
|||
} |
|||
|
|||
private void OnImageLoad(object sender, HtmlImageLoadEventArgs e) |
|||
{ |
|||
if (CheckAccess()) |
|||
OnImageLoad(e); |
|||
else |
|||
Dispatcher.Invoke(new Action<HtmlImageLoadEventArgs>(OnImageLoad), e); |
|||
} |
|||
|
|||
private void OnRefresh(object sender, HtmlRefreshEventArgs e) |
|||
{ |
|||
if (CheckAccess()) |
|||
OnRefresh(e); |
|||
else |
|||
Dispatcher.Invoke(new Action<HtmlRefreshEventArgs>(OnRefresh), e); |
|||
} |
|||
*/ |
|||
|
|||
#endregion
|
|||
|
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,135 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using System; |
|||
using Perspex.HtmlRenderer; |
|||
using Perspex.Media; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
using TheArtOfDev.HtmlRenderer.Core; |
|||
using TheArtOfDev.HtmlRenderer.Perspex.Adapters; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Provides HTML rendering using the text property.<br/>
|
|||
/// WPF control that will render html content in it's client rectangle.<br/>
|
|||
/// Using <see cref="AutoSize"/> and <see cref="AutoSizeHeightOnly"/> client can control how the html content effects the
|
|||
/// size of the label. Either case scrollbars are never shown and html content outside of client bounds will be clipped.
|
|||
/// MaxWidth/MaxHeight and MinWidth/MinHeight with AutoSize can limit the max/min size of the control<br/>
|
|||
/// The control will handle mouse and keyboard events on it to support html text selection, copy-paste and mouse clicks.<br/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// See <see cref="HtmlControl"/> for more info.
|
|||
/// </remarks>
|
|||
public class HtmlLabel : HtmlControl |
|||
{ |
|||
#region Dependency properties
|
|||
|
|||
public static readonly PerspexProperty AutoSizeProperty = PropertyHelper.Register<HtmlLabel, bool>("AutoSize", true, OnPerspexProperty_valueChanged); |
|||
public static readonly PerspexProperty AutoSizeHeightOnlyProperty = PropertyHelper.Register<HtmlLabel, bool>("AutoSizeHeightOnly", false, OnPerspexProperty_valueChanged); |
|||
|
|||
#endregion
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Init.
|
|||
/// </summary>
|
|||
static HtmlLabel() |
|||
{ |
|||
//BackgroundProperty.OverrideDefaultValue<HtmlLabel>(Brushes.Transparent);
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Automatically sets the size of the label by content size
|
|||
/// </summary>
|
|||
[Category("Layout")] |
|||
[Description("Automatically sets the size of the label by content size.")] |
|||
public bool AutoSize |
|||
{ |
|||
get { return (bool)GetValue(AutoSizeProperty); } |
|||
set { SetValue(AutoSizeProperty, value); } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Automatically sets the height of the label by content height (width is not effected).
|
|||
/// </summary>
|
|||
[Category("Layout")] |
|||
[Description("Automatically sets the height of the label by content height (width is not effected)")] |
|||
public virtual bool AutoSizeHeightOnly |
|||
{ |
|||
get { return (bool)GetValue(AutoSizeHeightOnlyProperty); } |
|||
set { SetValue(AutoSizeHeightOnlyProperty, value); } |
|||
} |
|||
|
|||
|
|||
#region Private methods
|
|||
|
|||
/// <summary>
|
|||
/// Perform the layout of the html in the control.
|
|||
/// </summary>
|
|||
protected override Size MeasureOverride(Size constraint) |
|||
{ |
|||
if (_htmlContainer != null) |
|||
{ |
|||
using (var ig = new GraphicsAdapter()) |
|||
{ |
|||
var horizontal = Padding.Left + Padding.Right + BorderThickness.Left + BorderThickness.Right; |
|||
var vertical = Padding.Top + Padding.Bottom + BorderThickness.Top + BorderThickness.Bottom; |
|||
|
|||
var size = new RSize(constraint.Width < Double.PositiveInfinity ? constraint.Width - horizontal : 0, constraint.Height < Double.PositiveInfinity ? constraint.Height - vertical : 0); |
|||
var minSize = new RSize(MinWidth < Double.PositiveInfinity ? MinWidth - horizontal : 0, MinHeight < Double.PositiveInfinity ? MinHeight - vertical : 0); |
|||
var maxSize = new RSize(MaxWidth < Double.PositiveInfinity ? MaxWidth - horizontal : 0, MaxHeight < Double.PositiveInfinity ? MaxHeight - vertical : 0); |
|||
|
|||
var newSize = HtmlRendererUtils.Layout(ig, _htmlContainer.HtmlContainerInt, size, minSize, maxSize, AutoSize, AutoSizeHeightOnly); |
|||
|
|||
constraint = new Size(newSize.Width + horizontal, newSize.Height + vertical); |
|||
} |
|||
} |
|||
|
|||
if (double.IsPositiveInfinity(constraint.Width) || double.IsPositiveInfinity(constraint.Height)) |
|||
constraint = Size.Empty; |
|||
|
|||
return constraint; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Handle when dependency property value changes to update the underline HtmlContainer with the new value.
|
|||
/// </summary>
|
|||
private static void OnPerspexProperty_valueChanged(PerspexObject PerspexObject, PerspexPropertyChangedEventArgs e) |
|||
{ |
|||
var control = PerspexObject as HtmlLabel; |
|||
if (control != null) |
|||
{ |
|||
if (e.Property == AutoSizeProperty) |
|||
{ |
|||
if ((bool)e.NewValue) |
|||
{ |
|||
PerspexObject.SetValue(AutoSizeHeightOnlyProperty, false); |
|||
control.InvalidateMeasure(); |
|||
control.InvalidateVisual(); |
|||
} |
|||
} |
|||
else if (e.Property == AutoSizeHeightOnlyProperty) |
|||
{ |
|||
if ((bool)e.NewValue) |
|||
{ |
|||
PerspexObject.SetValue(AutoSizeProperty, false); |
|||
control.InvalidateMeasure(); |
|||
control.InvalidateVisual(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,424 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using System; |
|||
|
|||
using Perspex.Media; |
|||
using Perspex.Media.Imaging; |
|||
using TheArtOfDev.HtmlRenderer.Core; |
|||
using TheArtOfDev.HtmlRenderer.Core.Entities; |
|||
using TheArtOfDev.HtmlRenderer.Core.Utils; |
|||
using TheArtOfDev.HtmlRenderer.Perspex.Adapters; |
|||
|
|||
namespace Perspex.Controls |
|||
{ |
|||
/// <summary>
|
|||
/// Standalone static class for simple and direct HTML rendering.<br/>
|
|||
/// For Perspex UI prefer using HTML controls: <see cref="HtmlPanel"/> or <see cref="HtmlLabel"/>.<br/>
|
|||
/// For low-level control and performance consider using <see cref="HtmlContainer"/>.<br/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// <para>
|
|||
/// <b>Rendering to image</b><br/>
|
|||
/// // TODO:a update!
|
|||
/// See https://htmlrenderer.codeplex.com/wikipage?title=Image%20generation <br/>
|
|||
/// Because of GDI text rendering issue with alpha channel clear type text rendering rendering to image requires special handling.<br/>
|
|||
/// <u>Solid color background -</u> generate an image where the background is filled with solid color and all the html is rendered on top
|
|||
/// of the background color, GDI text rendering will be used. (RenderToImage method where the first argument is html string)<br/>
|
|||
/// <u>Image background -</u> render html on top of existing image with whatever currently exist but it cannot have transparent pixels,
|
|||
/// GDI text rendering will be used. (RenderToImage method where the first argument is Image object)<br/>
|
|||
/// <u>Transparent background -</u> render html to empty image using GDI+ text rendering, the generated image can be transparent.
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// <b>Overwrite stylesheet resolution</b><br/>
|
|||
/// Exposed by optional "stylesheetLoad" delegate argument.<br/>
|
|||
/// Invoked when a stylesheet is about to be loaded by file path or URL in 'link' element.<br/>
|
|||
/// Allows to overwrite the loaded stylesheet by providing the stylesheet data manually, or different source (file or URL) to load from.<br/>
|
|||
/// Example: The stylesheet 'href' can be non-valid URI string that is interpreted in the overwrite delegate by custom logic to pre-loaded stylesheet object<br/>
|
|||
/// If no alternative data is provided the original source will be used.<br/>
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// <b>Overwrite image resolution</b><br/>
|
|||
/// Exposed by optional "imageLoad" delegate argument.<br/>
|
|||
/// Invoked when an image is about to be loaded by file path, URL or inline data in 'img' element or background-image CSS style.<br/>
|
|||
/// Allows to overwrite the loaded image by providing the image object manually, or different source (file or URL) to load from.<br/>
|
|||
/// Example: image 'src' can be non-valid string that is interpreted in the overwrite delegate by custom logic to resource image object<br/>
|
|||
/// Example: image 'src' in the html is relative - the overwrite intercepts the load and provide full source URL to load the image from<br/>
|
|||
/// Example: image download requires authentication - the overwrite intercepts the load, downloads the image to disk using custom code and provide
|
|||
/// file path to load the image from.<br/>
|
|||
/// If no alternative data is provided the original source will be used.<br/>
|
|||
/// Note: Cannot use asynchronous scheme overwrite scheme.<br/>
|
|||
/// </para>
|
|||
/// </remarks>
|
|||
/// <example>
|
|||
/// <para>
|
|||
/// <b>Simple rendering</b><br/>
|
|||
/// HtmlRender.Render(g, "<![CDATA[<div>Hello <b>World</b></div>]]>");<br/>
|
|||
/// HtmlRender.Render(g, "<![CDATA[<div>Hello <b>World</b></div>]]>", 10, 10, 500, CssData.Parse("body {font-size: 20px}")");<br/>
|
|||
/// </para>
|
|||
/// <para>
|
|||
/// <b>Image rendering</b><br/>
|
|||
/// HtmlRender.RenderToImage("<![CDATA[<div>Hello <b>World</b></div>]]>", new Size(600,400));<br/>
|
|||
/// HtmlRender.RenderToImage("<![CDATA[<div>Hello <b>World</b></div>]]>", 600);<br/>
|
|||
/// HtmlRender.RenderToImage(existingImage, "<![CDATA[<div>Hello <b>World</b></div>]]>");<br/>
|
|||
/// </para>
|
|||
/// </example>
|
|||
public static class HtmlRender |
|||
{ |
|||
/// <summary>
|
|||
/// Adds a font family to be used in html rendering.<br/>
|
|||
/// The added font will be used by all rendering function including <see cref="HtmlContainer"/> and all Perspex controls.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The given font family instance must be remain alive while the renderer is in use.<br/>
|
|||
/// If loaded from file then the file must not be deleted.
|
|||
/// </remarks>
|
|||
/// <param name="fontFamily">The font family to add.</param>
|
|||
public static void AddFontFamily(string fontFamily) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(fontFamily, "fontFamily"); |
|||
|
|||
PerspexAdapter.Instance.AddFontFamily(new FontFamilyAdapter(fontFamily)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Adds a font mapping from <paramref name="fromFamily"/> to <paramref name="toFamily"/> iff the <paramref name="fromFamily"/> is not found.<br/>
|
|||
/// When the <paramref name="fromFamily"/> font is used in rendered html and is not found in existing
|
|||
/// fonts (installed or added) it will be replaced by <paramref name="toFamily"/>.<br/>
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// This fonts mapping can be used as a fallback in case the requested font is not installed in the client system.
|
|||
/// </remarks>
|
|||
/// <param name="fromFamily">the font family to replace</param>
|
|||
/// <param name="toFamily">the font family to replace with</param>
|
|||
public static void AddFontFamilyMapping(string fromFamily, string toFamily) |
|||
{ |
|||
ArgChecker.AssertArgNotNullOrEmpty(fromFamily, "fromFamily"); |
|||
ArgChecker.AssertArgNotNullOrEmpty(toFamily, "toFamily"); |
|||
|
|||
PerspexAdapter.Instance.AddFontFamilyMapping(fromFamily, toFamily); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Parse the given stylesheet to <see cref="CssData"/> object.<br/>
|
|||
/// If <paramref name="combineWithDefault"/> is true the parsed css blocks are added to the
|
|||
/// default css data (as defined by W3), merged if class name already exists. If false only the data in the given stylesheet is returned.
|
|||
/// </summary>
|
|||
/// <seealso cref="http://www.w3.org/TR/CSS21/sample.html"/>
|
|||
/// <param name="stylesheet">the stylesheet source to parse</param>
|
|||
/// <param name="combineWithDefault">true - combine the parsed css data with default css data, false - return only the parsed css data</param>
|
|||
/// <returns>the parsed css data</returns>
|
|||
public static CssData ParseStyleSheet(string stylesheet, bool combineWithDefault = true) |
|||
{ |
|||
return CssData.Parse(PerspexAdapter.Instance, stylesheet, combineWithDefault); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Measure the size (width and height) required to draw the given html under given max width restriction.<br/>
|
|||
/// If no max width restriction is given the layout will use the maximum possible width required by the content,
|
|||
/// it can be the longest text line or full image width.<br/>
|
|||
/// </summary>
|
|||
/// <param name="html">HTML source to render</param>
|
|||
/// <param name="maxWidth">optional: bound the width of the html to render in (default - 0, unlimited)</param>
|
|||
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
|
|||
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
|
|||
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
|
|||
/// <returns>the size required for the html</returns>
|
|||
public static Size Measure(string html, double maxWidth = 0, CssData cssData = null, |
|||
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null) |
|||
{ |
|||
Size actualSize = Size.Empty; |
|||
if (!string.IsNullOrEmpty(html)) |
|||
{ |
|||
using (var container = new HtmlContainer()) |
|||
{ |
|||
container.MaxSize = new Size(maxWidth, 0); |
|||
container.AvoidAsyncImagesLoading = true; |
|||
container.AvoidImagesLateLoading = true; |
|||
|
|||
if (stylesheetLoad != null) |
|||
container.StylesheetLoad += stylesheetLoad; |
|||
if (imageLoad != null) |
|||
container.ImageLoad += imageLoad; |
|||
|
|||
container.SetHtml(html, cssData); |
|||
container.PerformLayout(); |
|||
|
|||
actualSize = container.ActualSize; |
|||
} |
|||
} |
|||
return actualSize; |
|||
} |
|||
/* |
|||
/// <summary>
|
|||
/// Renders the specified HTML source on the specified location and max width restriction.<br/>
|
|||
/// If <paramref name="maxWidth"/> is zero the html will use all the required width, otherwise it will perform line
|
|||
/// wrap as specified in the html<br/>
|
|||
/// Returned is the actual width and height of the rendered html.<br/>
|
|||
/// </summary>
|
|||
/// <param name="g">Device to render with</param>
|
|||
/// <param name="html">HTML source to render</param>
|
|||
/// <param name="left">optional: the left most location to start render the html at (default - 0)</param>
|
|||
/// <param name="top">optional: the top most location to start render the html at (default - 0)</param>
|
|||
/// <param name="maxWidth">optional: bound the width of the html to render in (default - 0, unlimited)</param>
|
|||
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
|
|||
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
|
|||
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
|
|||
/// <returns>the actual size of the rendered html</returns>
|
|||
public static Size Render(IDrawingContext g, string html, double left = 0, double top = 0, double maxWidth = 0, CssData cssData = null, |
|||
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(g, "g"); |
|||
return RenderClip(g, html, new Point(left, top), new Size(maxWidth, 0), cssData, stylesheetLoad, imageLoad); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Renders the specified HTML source on the specified location and max size restriction.<br/>
|
|||
/// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
|
|||
/// wrap as specified in the html<br/>
|
|||
/// If <paramref name="maxSize"/>.Height is zero the html will use all the required height, otherwise it will clip at the
|
|||
/// given max height not rendering the html below it.<br/>
|
|||
/// Returned is the actual width and height of the rendered html.<br/>
|
|||
/// </summary>
|
|||
/// <param name="g">Device to render with</param>
|
|||
/// <param name="html">HTML source to render</param>
|
|||
/// <param name="location">the top-left most location to start render the html at</param>
|
|||
/// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
|
|||
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
|
|||
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
|
|||
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
|
|||
/// <returns>the actual size of the rendered html</returns>
|
|||
public static Size Render(IDrawingContext g, string html, Point location, Size maxSize, CssData cssData = null, |
|||
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null) |
|||
{ |
|||
ArgChecker.AssertArgNotNull(g, "g"); |
|||
return RenderClip(g, html, location, maxSize, cssData, stylesheetLoad, imageLoad); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Renders the specified HTML into a new image of the requested size.<br/>
|
|||
/// The HTML will be layout by the given size but will be clipped if cannot fit.<br/>
|
|||
/// </summary>
|
|||
/// <param name="html">HTML source to render</param>
|
|||
/// <param name="size">The size of the image to render into, layout html by width and clipped by height</param>
|
|||
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
|
|||
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
|
|||
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
|
|||
/// <returns>the generated image of the html</returns>
|
|||
public static BitmapFrame RenderToImage(string html, Size size, CssData cssData = null, |
|||
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null) |
|||
{ |
|||
var renderTarget = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32); |
|||
|
|||
if (!string.IsNullOrEmpty(html)) |
|||
{ |
|||
// render HTML into the visual
|
|||
DrawingVisual drawingVisual = new DrawingVisual(); |
|||
using (IDrawingContext g = drawingVisual.RenderOpen()) |
|||
{ |
|||
RenderHtml(g, html, new Point(), size, cssData, stylesheetLoad, imageLoad); |
|||
} |
|||
|
|||
// render visual into target bitmap
|
|||
renderTarget.Render(drawingVisual); |
|||
} |
|||
|
|||
return BitmapFrame.Create(renderTarget); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Renders the specified HTML into a new image of unknown size that will be determined by max width/height and HTML layout.<br/>
|
|||
/// If <paramref name="maxWidth"/> is zero the html will use all the required width, otherwise it will perform line
|
|||
/// wrap as specified in the html<br/>
|
|||
/// If <paramref name="maxHeight"/> is zero the html will use all the required height, otherwise it will clip at the
|
|||
/// given max height not rendering the html below it.<br/>
|
|||
/// <p>
|
|||
/// Limitation: The image cannot have transparent background, by default it will be white.<br/>
|
|||
/// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
|
|||
/// </p>
|
|||
/// </summary>
|
|||
/// <param name="html">HTML source to render</param>
|
|||
/// <param name="maxWidth">optional: the max width of the rendered html, if not zero and html cannot be layout within the limit it will be clipped</param>
|
|||
/// <param name="maxHeight">optional: the max height of the rendered html, if not zero and html cannot be layout within the limit it will be clipped</param>
|
|||
/// <param name="backgroundColor">optional: the color to fill the image with (default - white)</param>
|
|||
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
|
|||
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
|
|||
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
|
|||
/// <returns>the generated image of the html</returns>
|
|||
public static BitmapFrame RenderToImage(string html, int maxWidth = 0, int maxHeight = 0, Color backgroundColor = new Color(), CssData cssData = null, |
|||
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null) |
|||
{ |
|||
return RenderToImage(html, Size.Empty, new Size(maxWidth, maxHeight), backgroundColor, cssData, stylesheetLoad, imageLoad); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Renders the specified HTML into a new image of unknown size that will be determined by min/max width/height and HTML layout.<br/>
|
|||
/// If <paramref name="maxSize.Width"/> is zero the html will use all the required width, otherwise it will perform line
|
|||
/// wrap as specified in the html<br/>
|
|||
/// If <paramref name="maxSize.Height"/> is zero the html will use all the required height, otherwise it will clip at the
|
|||
/// given max height not rendering the html below it.<br/>
|
|||
/// If <paramref name="minSize"/> (Width/Height) is above zero the rendered image will not be smaller than the given min size.<br/>
|
|||
/// <p>
|
|||
/// Limitation: The image cannot have transparent background, by default it will be white.<br/>
|
|||
/// See "Rendering to image" remarks section on <see cref="HtmlRender"/>.<br/>
|
|||
/// </p>
|
|||
/// </summary>
|
|||
/// <param name="html">HTML source to render</param>
|
|||
/// <param name="minSize">optional: the min size of the rendered html (zero - not limit the width/height)</param>
|
|||
/// <param name="maxSize">optional: the max size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height)</param>
|
|||
/// <param name="backgroundColor">optional: the color to fill the image with (default - white)</param>
|
|||
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
|
|||
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
|
|||
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
|
|||
/// <returns>the generated image of the html</returns>
|
|||
public static BitmapFrame RenderToImage(string html, Size minSize, Size maxSize, Color backgroundColor = new Color(), CssData cssData = null, |
|||
EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad = null, EventHandler<HtmlImageLoadEventArgs> imageLoad = null) |
|||
{ |
|||
RenderTargetBitmap renderTarget; |
|||
if (!string.IsNullOrEmpty(html)) |
|||
{ |
|||
using (var container = new HtmlContainer()) |
|||
{ |
|||
container.AvoidAsyncImagesLoading = true; |
|||
container.AvoidImagesLateLoading = true; |
|||
|
|||
if (stylesheetLoad != null) |
|||
container.StylesheetLoad += stylesheetLoad; |
|||
if (imageLoad != null) |
|||
container.ImageLoad += imageLoad; |
|||
container.SetHtml(html, cssData); |
|||
|
|||
var finalSize = MeasureHtmlByRestrictions(container, minSize, maxSize); |
|||
container.MaxSize = finalSize; |
|||
|
|||
renderTarget = new RenderTargetBitmap((int)finalSize.Width, (int)finalSize.Height, 96, 96, PixelFormats.Pbgra32); |
|||
|
|||
// render HTML into the visual
|
|||
DrawingVisual drawingVisual = new DrawingVisual(); |
|||
using (IDrawingContext g = drawingVisual.RenderOpen()) |
|||
{ |
|||
container.PerformPaint(g, new Rect(new Size(maxSize.Width > 0 ? maxSize.Width : double.MaxValue, maxSize.Height > 0 ? maxSize.Height : double.MaxValue))); |
|||
} |
|||
|
|||
// render visual into target bitmap
|
|||
renderTarget.Render(drawingVisual); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
renderTarget = new RenderTargetBitmap(0, 0, 96, 96, PixelFormats.Pbgra32); |
|||
} |
|||
|
|||
return BitmapFrame.Create(renderTarget); |
|||
} |
|||
|
|||
|
|||
#region Private methods
|
|||
|
|||
/// <summary>
|
|||
/// Measure the size of the html by performing layout under the given restrictions.
|
|||
/// </summary>
|
|||
/// <param name="htmlContainer">the html to calculate the layout for</param>
|
|||
/// <param name="minSize">the minimal size of the rendered html (zero - not limit the width/height)</param>
|
|||
/// <param name="maxSize">the maximum size of the rendered html, if not zero and html cannot be layout within the limit it will be clipped (zero - not limit the width/height)</param>
|
|||
/// <returns>return: the size of the html to be rendered within the min/max limits</returns>
|
|||
private static Size MeasureHtmlByRestrictions(HtmlContainer htmlContainer, Size minSize, Size maxSize) |
|||
{ |
|||
// use desktop created graphics to measure the HTML
|
|||
using (var mg = new GraphicsAdapter()) |
|||
{ |
|||
var sizeInt = HtmlRendererUtil.MeasureHtmlByRestrictions(mg, htmlContainer.HtmlContainerInt, Util.Convert(minSize), Util.Convert(maxSize)); |
|||
if (maxSize.Width < 1 && sizeInt.Width > 4096) |
|||
sizeInt.Width = 4096; |
|||
return Util.ConvertRound(sizeInt); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Renders the specified HTML source on the specified location and max size restriction.<br/>
|
|||
/// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
|
|||
/// wrap as specified in the html<br/>
|
|||
/// If <paramref name="maxSize"/>.Height is zero the html will use all the required height, otherwise it will clip at the
|
|||
/// given max height not rendering the html below it.<br/>
|
|||
/// Clip the graphics so the html will not be rendered outside the max height bound given.<br/>
|
|||
/// Returned is the actual width and height of the rendered html.<br/>
|
|||
/// </summary>
|
|||
/// <param name="g">Device to render with</param>
|
|||
/// <param name="html">HTML source to render</param>
|
|||
/// <param name="location">the top-left most location to start render the html at</param>
|
|||
/// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
|
|||
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
|
|||
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
|
|||
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
|
|||
/// <returns>the actual size of the rendered html</returns>
|
|||
private static Size RenderClip(IDrawingContext g, string html, Point location, Size maxSize, CssData cssData, EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad, EventHandler<HtmlImageLoadEventArgs> imageLoad) |
|||
{ |
|||
if (maxSize.Height > 0) |
|||
g.PushClip(new RectangleGeometry(new Rect(location, maxSize))); |
|||
|
|||
var actualSize = RenderHtml(g, html, location, maxSize, cssData, stylesheetLoad, imageLoad); |
|||
|
|||
if (maxSize.Height > 0) |
|||
g.Pop(); |
|||
|
|||
return actualSize; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Renders the specified HTML source on the specified location and max size restriction.<br/>
|
|||
/// If <paramref name="maxSize"/>.Width is zero the html will use all the required width, otherwise it will perform line
|
|||
/// wrap as specified in the html<br/>
|
|||
/// If <paramref name="maxSize"/>.Height is zero the html will use all the required height, otherwise it will clip at the
|
|||
/// given max height not rendering the html below it.<br/>
|
|||
/// Returned is the actual width and height of the rendered html.<br/>
|
|||
/// </summary>
|
|||
/// <param name="g">Device to render with</param>
|
|||
/// <param name="html">HTML source to render</param>
|
|||
/// <param name="location">the top-left most location to start render the html at</param>
|
|||
/// <param name="maxSize">the max size of the rendered html (if height above zero it will be clipped)</param>
|
|||
/// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
|
|||
/// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
|
|||
/// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
|
|||
/// <returns>the actual size of the rendered html</returns>
|
|||
private static Size RenderHtml(IDrawingContext g, string html, Point location, Size maxSize, CssData cssData, EventHandler<HtmlStylesheetLoadEventArgs> stylesheetLoad, EventHandler<HtmlImageLoadEventArgs> imageLoad) |
|||
{ |
|||
Size actualSize = Size.Empty; |
|||
|
|||
if (!string.IsNullOrEmpty(html)) |
|||
{ |
|||
using (var container = new HtmlContainer()) |
|||
{ |
|||
container.Location = location; |
|||
container.MaxSize = maxSize; |
|||
container.AvoidAsyncImagesLoading = true; |
|||
container.AvoidImagesLateLoading = true; |
|||
|
|||
if (stylesheetLoad != null) |
|||
container.StylesheetLoad += stylesheetLoad; |
|||
if (imageLoad != null) |
|||
container.ImageLoad += imageLoad; |
|||
|
|||
container.SetHtml(html, cssData); |
|||
container.PerformLayout(); |
|||
container.PerformPaint(g, new Rect(0, 0, double.MaxValue, double.MaxValue)); |
|||
|
|||
actualSize = container.ActualSize; |
|||
} |
|||
} |
|||
|
|||
return actualSize; |
|||
} |
|||
|
|||
#endregion
|
|||
*/ |
|||
} |
|||
} |
|||
@ -0,0 +1,198 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<PropertyGroup> |
|||
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion> |
|||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
|||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
|||
<ProjectGuid>{5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D}</ProjectGuid> |
|||
<OutputType>Library</OutputType> |
|||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|||
<RootNamespace>Perspex.HtmlRenderer</RootNamespace> |
|||
<AssemblyName>Perspex.HtmlRenderer</AssemblyName> |
|||
<DefaultLanguage>en-US</DefaultLanguage> |
|||
<FileAlignment>512</FileAlignment> |
|||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
|||
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile> |
|||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\Debug\</OutputPath> |
|||
<DefineConstants>TRACE;DEBUG;PCL</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|||
<DebugType>pdbonly</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\Release\</OutputPath> |
|||
<DefineConstants>TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<!-- A reference to the entire .NET Framework is automatically included --> |
|||
<Content Include="external\Source\HtmlRenderer\Core\Utils\ImageError.png" /> |
|||
<Content Include="external\Source\HtmlRenderer\Core\Utils\ImageLoad.png" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Compile Include="Adapters\BrushAdapter.cs" /> |
|||
<Compile Include="Adapters\ContextMenuAdapter.cs" /> |
|||
<Compile Include="Adapters\ControlAdapter.cs" /> |
|||
<Compile Include="Adapters\FontAdapter.cs" /> |
|||
<Compile Include="Adapters\FontFamilyAdapter.cs" /> |
|||
<Compile Include="Adapters\GraphicsAdapter.cs" /> |
|||
<Compile Include="Adapters\GraphicsPathAdapter.cs" /> |
|||
<Compile Include="Adapters\ImageAdapter.cs" /> |
|||
<Compile Include="Adapters\PenAdapter.cs" /> |
|||
<Compile Include="Adapters\PerspexAdapter.cs" /> |
|||
<Compile Include="Compat\Attributes.cs" /> |
|||
<Compile Include="Compat\ThreadPool.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer.Pcl\PclCompat.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RColor.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RDashStyle.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RFontStyle.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RKeyEvent.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RMouseEvent.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RPoint.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RRect.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\Entities\RSize.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RAdapter.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RBrush.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RContextMenu.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RControl.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RFont.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RFontFamily.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RGraphics.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RGraphicsPath.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RImage.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Adapters\RPen.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\CssData.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\CssDefaults.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\Border.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBox.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBoxFrame.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBoxHr.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBoxImage.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssBoxProperties.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssLayoutEngine.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssLayoutEngineTable.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssLength.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssLineBox.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssRect.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssRectImage.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssRectWord.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssSpacingBox.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\CssUnit.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\HoverBoxBlock.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Dom\HtmlTag.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\CssBlock.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\CssBlockSelectorItem.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlGenerationStyle.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlImageLoadEventArgs.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlLinkClickedEventArgs.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlLinkClickedException.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlRefreshEventArgs.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlRenderErrorEventArgs.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlRenderErrorType.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlScrollEventArgs.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\HtmlStylesheetLoadEventArgs.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Entities\LinkElementData.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Handlers\BackgroundImageDrawHandler.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Handlers\BordersDrawHandler.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Handlers\ContextMenuHandler.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Handlers\FontsHandler.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Handlers\ImageDownloader.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Handlers\ImageLoadHandler.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Handlers\SelectionHandler.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Handlers\StylesheetLoadHandler.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\HtmlContainerInt.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\HtmlRendererUtils.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Parse\CssParser.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Parse\CssValueParser.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Parse\DomParser.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Parse\HtmlParser.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Parse\RegexParserHelper.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Parse\RegexParserUtils.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\ArgChecker.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\CommonUtils.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\CssConstants.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\CssUtils.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\DomUtils.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\HtmlConstants.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\HtmlUtils.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\RenderUtils.cs" /> |
|||
<Compile Include="external\Source\HtmlRenderer\Core\Utils\SubString.cs" /> |
|||
<Compile Include="HtmlContainer.cs" /> |
|||
<Compile Include="HtmlControl.cs" /> |
|||
<Compile Include="HtmlLabel.cs" /> |
|||
<Compile Include="HtmlRender.cs" /> |
|||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|||
<Compile Include="PropertyHelper.cs" /> |
|||
<Compile Include="RoutedEventArgsWrapper.cs" /> |
|||
<Compile Include="Utilities\Util.cs" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Perspex.Animation\Perspex.Animation.csproj"> |
|||
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project> |
|||
<Name>Perspex.Animation</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\Perspex.Base\Perspex.Base.csproj"> |
|||
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
|||
<Name>Perspex.Base</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\Perspex.Controls\Perspex.Controls.csproj"> |
|||
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project> |
|||
<Name>Perspex.Controls</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\Perspex.Input\Perspex.Input.csproj"> |
|||
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
|||
<Name>Perspex.Input</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\Perspex.Interactivity\Perspex.Interactivity.csproj"> |
|||
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
|||
<Name>Perspex.Interactivity</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\Perspex.Layout\Perspex.Layout.csproj"> |
|||
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
|||
<Name>Perspex.Layout</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\Perspex.SceneGraph\Perspex.SceneGraph.csproj"> |
|||
<Project>{EB582467-6ABB-43A1-B052-E981BA910E3A}</Project> |
|||
<Name>Perspex.SceneGraph</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\Perspex.Styling\Perspex.Styling.csproj"> |
|||
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
|||
<Name>Perspex.Styling</Name> |
|||
</ProjectReference> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<None Include="packages.config" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
|||
<HintPath>..\..\packages\Rx-Core.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Core.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
<Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
|||
<HintPath>..\..\packages\Rx-Interfaces.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Interfaces.dll</HintPath> |
|||
<Private>True</Private> |
|||
</Reference> |
|||
</ItemGroup> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> |
|||
<Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" /> |
|||
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''"> |
|||
<Error Condition="!Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" /> |
|||
<Error Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" /> |
|||
</Target> |
|||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
|||
Other similar extension points exist, see Microsoft.Common.targets. |
|||
<Target Name="BeforeBuild"> |
|||
</Target> |
|||
<Target Name="AfterBuild"> |
|||
</Target> |
|||
--> |
|||
</Project> |
|||
@ -0,0 +1,2 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp60</s:String></wpf:ResourceDictionary> |
|||
@ -0,0 +1,30 @@ |
|||
using System.Resources; |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// General Information about an assembly is controlled through the following
|
|||
// set of attributes. Change these attribute values to modify the information
|
|||
// associated with an assembly.
|
|||
[assembly: AssemblyTitle("Perspex.HtmlRenderer")] |
|||
[assembly: AssemblyDescription("")] |
|||
[assembly: AssemblyConfiguration("")] |
|||
[assembly: AssemblyCompany("")] |
|||
[assembly: AssemblyProduct("Perspex.HtmlRenderer")] |
|||
[assembly: AssemblyCopyright("Copyright © 2015")] |
|||
[assembly: AssemblyTrademark("")] |
|||
[assembly: AssemblyCulture("")] |
|||
[assembly: NeutralResourcesLanguage("en")] |
|||
|
|||
// Version information for an assembly consists of the following four values:
|
|||
//
|
|||
// Major Version
|
|||
// Minor Version
|
|||
// Build Number
|
|||
// Revision
|
|||
//
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
|||
// by using the '*' as shown below:
|
|||
// [assembly: AssemblyVersion("1.0.*")]
|
|||
[assembly: AssemblyVersion("1.0.0.0")] |
|||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Perspex.HtmlRenderer |
|||
{ |
|||
static class PropertyHelper |
|||
{ |
|||
|
|||
public static PerspexProperty Register<TOwner, T>(string name, T def, Action<PerspexObject, PerspexPropertyChangedEventArgs> changed) where TOwner : PerspexObject |
|||
{ |
|||
var pp = PerspexProperty.Register<TOwner, T>(name, def); |
|||
Action<PerspexPropertyChangedEventArgs> cb = args => |
|||
{ |
|||
changed(args.Sender, args); |
|||
}; |
|||
|
|||
pp.Changed.Subscribe(cb); |
|||
return pp; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Perspex.Interactivity; |
|||
|
|||
namespace Perspex.HtmlRenderer |
|||
{ |
|||
public class RoutedEventArgsWrapper<T> : RoutedEventArgs |
|||
{ |
|||
public T Event { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
// "Therefore those skilled at the unorthodox
|
|||
// are infinite as heaven and earth,
|
|||
// inexhaustible as the great rivers.
|
|||
// When they come to an end,
|
|||
// they begin again,
|
|||
// like the days and months;
|
|||
// they die and are reborn,
|
|||
// like the four seasons."
|
|||
//
|
|||
// - Sun Tsu,
|
|||
// "The Art of War"
|
|||
|
|||
using Perspex; |
|||
using Perspex.Media; |
|||
using TheArtOfDev.HtmlRenderer.Adapters.Entities; |
|||
|
|||
namespace TheArtOfDev.HtmlRenderer.Perspex.Utilities |
|||
{ |
|||
/// <summary>
|
|||
/// Utilities for converting WPF entities to HtmlRenderer core entities.
|
|||
/// </summary>
|
|||
internal static class Util |
|||
{ |
|||
/// <summary>
|
|||
/// Convert from WPF point to core point.
|
|||
/// </summary>
|
|||
public static RPoint Convert(Point p) |
|||
{ |
|||
return new RPoint(p.X, p.Y); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from WPF point to core point.
|
|||
/// </summary>
|
|||
public static Point[] Convert(RPoint[] points) |
|||
{ |
|||
Point[] myPoints = new Point[points.Length]; |
|||
for (int i = 0; i < points.Length; i++) |
|||
myPoints[i] = Convert(points[i]); |
|||
return myPoints; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from core point to WPF point.
|
|||
/// </summary>
|
|||
public static Point Convert(RPoint p) |
|||
{ |
|||
return new Point(p.X, p.Y); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from core point to WPF point.
|
|||
/// </summary>
|
|||
public static Point ConvertRound(RPoint p) |
|||
{ |
|||
return new Point((int)p.X, (int)p.Y); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from WPF size to core size.
|
|||
/// </summary>
|
|||
public static RSize Convert(Size s) |
|||
{ |
|||
return new RSize(s.Width, s.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from core size to WPF size.
|
|||
/// </summary>
|
|||
public static Size Convert(RSize s) |
|||
{ |
|||
return new Size(s.Width, s.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from core point to WPF point.
|
|||
/// </summary>
|
|||
public static Size ConvertRound(RSize s) |
|||
{ |
|||
return new Size((int)s.Width, (int)s.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from WPF rectangle to core rectangle.
|
|||
/// </summary>
|
|||
public static RRect Convert(Rect r) |
|||
{ |
|||
return new RRect(r.X, r.Y, r.Width, r.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from core rectangle to WPF rectangle.
|
|||
/// </summary>
|
|||
public static Rect Convert(RRect r) |
|||
{ |
|||
return new Rect(r.X, r.Y, r.Width, r.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from core rectangle to WPF rectangle.
|
|||
/// </summary>
|
|||
public static Rect ConvertRound(RRect r) |
|||
{ |
|||
return new Rect((int)r.X, (int)r.Y, (int)r.Width, (int)r.Height); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from WPF color to core color.
|
|||
/// </summary>
|
|||
public static RColor Convert(Color c) |
|||
{ |
|||
return RColor.FromArgb(c.A, c.R, c.G, c.B); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Convert from core color to WPF color.
|
|||
/// </summary>
|
|||
public static Color Convert(RColor c) |
|||
{ |
|||
return Color.FromArgb(c.A, c.R, c.G, c.B); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
Subproject commit 8ae3973d6b766b2b286a8869c66470e6eba74ab1 |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<packages> |
|||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="portable45-net45+win8" /> |
|||
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="portable45-net45+win8" /> |
|||
<package id="Rx-Core" version="2.2.5" targetFramework="portable45-net45+win8" /> |
|||
<package id="Rx-Interfaces" version="2.2.5" targetFramework="portable45-net45+win8" /> |
|||
</packages> |
|||
Loading…
Reference in new issue