Created
October 19, 2016 20:01
-
-
Save mattpodwysocki/25f9dafa095fa3db934f56243443d821 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (c) 2014-present, Facebook, Inc. | |
* All rights reserved. | |
* | |
* This source code is licensed under the BSD-style license found in the | |
* LICENSE file in the root directory of this source tree. An additional grant | |
* of patent rights can be found in the PATENTS file in the same directory. | |
*/ | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Runtime.InteropServices; | |
namespace Facebook.CSSLayout | |
{ | |
public class CSSNode : IDisposable, IEnumerable<CSSNode> | |
{ | |
private bool _isDisposed; | |
private IntPtr _cssNode; | |
private GCHandle _context; | |
private WeakReference _parent; | |
private List<CSSNode> _children; | |
private MeasureFunction _measureFunction; | |
private static CSSMeasureFunc _cssMeasureFunc = MeasureInternal; | |
private object _data; | |
public CSSNode() | |
{ | |
Reinitialize(); | |
} | |
private void AssertNativeInstance() | |
{ | |
if (_isDisposed) | |
{ | |
throw new ObjectDisposedException("CSSNode"); | |
} | |
if (_cssNode == IntPtr.Zero) | |
{ | |
throw new InvalidOperationException("Null native pointer"); | |
} | |
} | |
~CSSNode() | |
{ | |
Dispose(false); | |
} | |
public void Dispose() | |
{ | |
Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
protected virtual void Dispose(bool disposing) | |
{ | |
if (!_isDisposed) | |
{ | |
if (disposing) | |
{ | |
FreeManaged(); | |
} | |
FreeUnmanaged(); | |
_isDisposed = true; | |
} | |
} | |
private void FreeManaged() | |
{ | |
if (_children != null) | |
{ | |
for (int i = 0; i < _children.Count; ++i) | |
{ | |
var child = _children[i]; | |
child.AssertNativeInstance(); | |
child._parent = null; | |
Native.CSSNodeRemoveChild(_cssNode, child._cssNode); | |
} | |
_children = null; | |
} | |
if (_parent != null) | |
{ | |
var parent = _parent.Target as CSSNode; | |
parent.AssertNativeInstance(); | |
parent._children.Remove(this); | |
Native.CSSNodeRemoveChild(parent._cssNode, _cssNode); | |
_parent = null; | |
} | |
_measureFunction = null; | |
} | |
private void FreeUnmanaged() | |
{ | |
if (_cssNode != IntPtr.Zero) | |
{ | |
Native.CSSNodeFree(_cssNode); | |
_cssNode = IntPtr.Zero; | |
} | |
if (_context.IsAllocated) | |
{ | |
_context.Free(); | |
} | |
} | |
public void Reinitialize() | |
{ | |
if (_cssNode != IntPtr.Zero) | |
{ | |
throw new InvalidOperationException("Allready initialized node"); | |
} | |
CSSAssert.Initialize(); | |
_cssNode = Native.CSSNodeNew(); | |
_context = GCHandle.Alloc(this); | |
_children = new List<CSSNode>(4); | |
Native.CSSNodeSetContext(_cssNode, GCHandle.ToIntPtr(_context)); | |
Native.CSSNodeSetPrintFunc(_cssNode, PrintInternal); | |
} | |
public void Free() | |
{ | |
AssertNativeInstance(); | |
if (_parent != null || (_children != null && _children.Count > 0)) | |
{ | |
throw new InvalidOperationException("You should not free an attached CSSNode"); | |
} | |
FreeManaged(); | |
FreeUnmanaged(); | |
} | |
public bool IsDirty | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeIsDirty(_cssNode); | |
} | |
} | |
public virtual void MarkDirty() | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeMarkDirty(_cssNode); | |
} | |
public bool IsTextNode | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeGetIsTextnode(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeSetIsTextnode(_cssNode, value); | |
} | |
} | |
public bool HasNewLayout | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeGetHasNewLayout(_cssNode); | |
} | |
} | |
public void MarkHasNewLayout() | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeSetHasNewLayout(_cssNode, true); | |
} | |
public CSSNode Parent | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return _parent != null ? _parent.Target as CSSNode : null; | |
} | |
} | |
public bool IsMeasureDefined | |
{ | |
get | |
{ | |
return _measureFunction != null; | |
} | |
} | |
public CSSDirection StyleDirection | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetDirection(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetDirection(_cssNode, value); | |
} | |
} | |
public CSSFlexDirection FlexDirection | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetFlexDirection(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetFlexDirection(_cssNode, value); | |
} | |
} | |
public CSSJustify JustifyContent | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetJustifyContent(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetJustifyContent(_cssNode, value); | |
} | |
} | |
public CSSAlign AlignItems | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetAlignItems(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetAlignItems(_cssNode, value); | |
} | |
} | |
public CSSAlign AlignSelf | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetAlignSelf(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetAlignSelf(_cssNode, value); | |
} | |
} | |
public CSSAlign AlignContent | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetAlignContent(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetAlignContent(_cssNode, value); | |
} | |
} | |
public CSSPositionType PositionType | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetPositionType(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetPositionType(_cssNode, value); | |
} | |
} | |
public CSSWrap Wrap | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetFlexWrap(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetFlexWrap(_cssNode, value); | |
} | |
} | |
public float Flex | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetFlex(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetFlex(_cssNode, value); | |
} | |
} | |
public float FlexGrow | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetFlexGrow(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetFlexGrow(_cssNode, value); | |
} | |
} | |
public float FlexShrink | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetFlexShrink(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetFlexShrink(_cssNode, value); | |
} | |
} | |
public float FlexBasis | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetFlexBasis(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetFlexBasis(_cssNode, value); | |
} | |
} | |
public Spacing GetMargin() | |
{ | |
AssertNativeInstance(); | |
var margin = new Spacing(); | |
margin.Set(Spacing.Left, Native.CSSNodeStyleGetMargin(_cssNode, CSSEdge.Left)); | |
margin.Set(Spacing.Top, Native.CSSNodeStyleGetMargin(_cssNode, CSSEdge.Top)); | |
margin.Set(Spacing.Right, Native.CSSNodeStyleGetMargin(_cssNode, CSSEdge.Right)); | |
margin.Set(Spacing.Bottom, Native.CSSNodeStyleGetMargin(_cssNode, CSSEdge.Bottom)); | |
margin.Set(Spacing.Start, Native.CSSNodeStyleGetMargin(_cssNode, CSSEdge.Start)); | |
margin.Set(Spacing.End, Native.CSSNodeStyleGetMargin(_cssNode, CSSEdge.End)); | |
return margin; | |
} | |
public void SetMargin(CSSEdge edge, float value) | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetMargin(_cssNode, edge, value); | |
} | |
public Spacing GetPadding() | |
{ | |
AssertNativeInstance(); | |
var padding = new Spacing(); | |
padding.Set(Spacing.Left, Native.CSSNodeStyleGetPadding(_cssNode, CSSEdge.Left)); | |
padding.Set(Spacing.Top, Native.CSSNodeStyleGetPadding(_cssNode, CSSEdge.Top)); | |
padding.Set(Spacing.Right, Native.CSSNodeStyleGetPadding(_cssNode, CSSEdge.Right)); | |
padding.Set(Spacing.Bottom, Native.CSSNodeStyleGetPadding(_cssNode, CSSEdge.Bottom)); | |
padding.Set(Spacing.Start, Native.CSSNodeStyleGetPadding(_cssNode, CSSEdge.Start)); | |
padding.Set(Spacing.End, Native.CSSNodeStyleGetPadding(_cssNode, CSSEdge.End)); | |
return padding; | |
} | |
public void SetPadding(CSSEdge edge, float padding) | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetPadding(_cssNode, edge, padding); | |
} | |
public Spacing GetBorder() | |
{ | |
AssertNativeInstance(); | |
var border = new Spacing(); | |
border.Set(Spacing.Left, Native.CSSNodeStyleGetBorder(_cssNode, CSSEdge.Left)); | |
border.Set(Spacing.Top, Native.CSSNodeStyleGetBorder(_cssNode, CSSEdge.Top)); | |
border.Set(Spacing.Right, Native.CSSNodeStyleGetBorder(_cssNode, CSSEdge.Right)); | |
border.Set(Spacing.Bottom, Native.CSSNodeStyleGetBorder(_cssNode, CSSEdge.Bottom)); | |
border.Set(Spacing.Start, Native.CSSNodeStyleGetBorder(_cssNode, CSSEdge.Start)); | |
border.Set(Spacing.End, Native.CSSNodeStyleGetBorder(_cssNode, CSSEdge.End)); | |
return border; | |
} | |
public void SetBorder(CSSEdge edge, float border) | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetBorder(_cssNode, edge, border); | |
} | |
public Spacing GetPosition() | |
{ | |
AssertNativeInstance(); | |
var position = new Spacing(); | |
position.Set(Spacing.Left, Native.CSSNodeStyleGetPosition(_cssNode, CSSEdge.Left)); | |
position.Set(Spacing.Top, Native.CSSNodeStyleGetPosition(_cssNode, CSSEdge.Top)); | |
position.Set(Spacing.Right, Native.CSSNodeStyleGetPosition(_cssNode, CSSEdge.Right)); | |
position.Set(Spacing.Bottom, Native.CSSNodeStyleGetPosition(_cssNode, CSSEdge.Bottom)); | |
position.Set(Spacing.Start, Native.CSSNodeStyleGetPosition(_cssNode, CSSEdge.Start)); | |
position.Set(Spacing.End, Native.CSSNodeStyleGetPosition(_cssNode, CSSEdge.End)); | |
return position; | |
} | |
public void SetPosition(CSSEdge edge, float position) | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetPosition(_cssNode, edge, position); | |
} | |
public float StyleWidth | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetWidth(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetWidth(_cssNode, value); | |
} | |
} | |
public float StyleHeight | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetHeight(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetHeight(_cssNode, value); | |
} | |
} | |
public float StyleMaxWidth | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetMaxWidth(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetMaxWidth(_cssNode, value); | |
} | |
} | |
public float StyleMaxHeight | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetMaxHeight(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetMaxHeight(_cssNode, value); | |
} | |
} | |
public float StyleMinWidth | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetMinWidth(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetMinWidth(_cssNode, value); | |
} | |
} | |
public float StyleMinHeight | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetMinHeight(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetMinHeight(_cssNode, value); | |
} | |
} | |
public float LayoutX | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeLayoutGetLeft(_cssNode); | |
} | |
} | |
public float LayoutY | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeLayoutGetTop(_cssNode); | |
} | |
} | |
public float LayoutWidth | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeLayoutGetWidth(_cssNode); | |
} | |
} | |
public float LayoutHeight | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeLayoutGetHeight(_cssNode); | |
} | |
} | |
public CSSDirection LayoutDirection | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeLayoutGetDirection(_cssNode); | |
} | |
} | |
public CSSOverflow Overflow | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return Native.CSSNodeStyleGetOverflow(_cssNode); | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeStyleSetOverflow(_cssNode, value); | |
} | |
} | |
public object Data | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return _data; | |
} | |
set | |
{ | |
AssertNativeInstance(); | |
_data = value; | |
} | |
} | |
public CSSNode this[int index] | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return _children[index]; | |
} | |
} | |
public int Count | |
{ | |
get | |
{ | |
AssertNativeInstance(); | |
return _children.Count; | |
} | |
} | |
public void MarkLayoutSeen() | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeSetHasNewLayout(_cssNode, false); | |
} | |
public bool ValuesEqual(float f1, float f2) | |
{ | |
if (float.IsNaN(f1) || float.IsNaN(f2)) | |
{ | |
return float.IsNaN(f1) && float.IsNaN(f2); | |
} | |
return Math.Abs(f2 - f1) < float.Epsilon; | |
} | |
public void Insert(int index, CSSNode node) | |
{ | |
AssertNativeInstance(); | |
_children.Insert(index, node); | |
node._parent = new WeakReference(this); | |
Native.CSSNodeInsertChild(_cssNode, node._cssNode, (uint)index); | |
} | |
public void RemoveAt(int index) | |
{ | |
AssertNativeInstance(); | |
var child = _children[index]; | |
child._parent = null; | |
_children.RemoveAt(index); | |
Native.CSSNodeRemoveChild(_cssNode, child._cssNode); | |
} | |
public int IndexOf(CSSNode node) | |
{ | |
AssertNativeInstance(); | |
return _children.IndexOf(node); | |
} | |
public void SetMeasureFunction(MeasureFunction measureFunction) | |
{ | |
AssertNativeInstance(); | |
_measureFunction = measureFunction; | |
Native.CSSNodeSetMeasureFunc(_cssNode, measureFunction != null ? _cssMeasureFunc : (CSSMeasureFunc)null); | |
} | |
public void CalculateLayout() | |
{ | |
AssertNativeInstance(); | |
Native.CSSNodeCalculateLayout(_cssNode, CSSConstants.Undefined, CSSConstants.Undefined, Native.CSSNodeStyleGetDirection(_cssNode)); | |
} | |
private static CSSSize MeasureInternal( | |
IntPtr context, | |
float width, | |
CSSMeasureMode widthMode, | |
float height, | |
CSSMeasureMode heightMode) | |
{ | |
var handle = GCHandle.FromIntPtr(context); | |
var cssNode = (CSSNode)handle.Target; | |
if (cssNode._measureFunction == null) | |
{ | |
throw new InvalidOperationException("Measure function is not defined."); | |
} | |
var measureResult = new MeasureOutput(); | |
cssNode._measureFunction(cssNode, width, widthMode, height, heightMode, measureResult); | |
return new CSSSize { width = measureResult.Width, height = measureResult.Height }; | |
} | |
private void PrintInternal(IntPtr context) | |
{ | |
System.Diagnostics.Debug.WriteLine(ToString()); | |
} | |
public IEnumerator<CSSNode> GetEnumerator() | |
{ | |
return ((IEnumerable<CSSNode>)_children).GetEnumerator(); | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return ((IEnumerable<CSSNode>)_children).GetEnumerator(); | |
} | |
public static int GetInstanceCount() | |
{ | |
return Native.CSSNodeGetInstanceCount(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment