public void ReEvaluate(Node context, CssProperty property) { Property = property; IRenderableNode rdr = (context.parentNode_ as IRenderableNode); if (rdr != null) { // Inherit right now: Context = rdr.RenderData; From_ = rdr.ComputedStyle[property]; Inherit inher = From_ as Inherit; if (inher != null) { // Collapse the cascade - Pull the inner value instead: From_ = inher.From; Context = inher.Context; } } if (From_ == null) { From_ = property.InitialValue; } if (Context == null) { Context = (context as IRenderableNode).RenderData; } Type = From_.Type; }
/// <summary>Called when a selection starts using the given input pointer (typically a mouse).</summary> public static void BeginSelect(PowerUI.InputPointer pointer, Css.Value mode) { // Selection starting at the position the pointer started dragging // Firstly, resolve our doc coords to a selection range start. // Get as first containing child node (should be text or null): NodeList kids = pointer.ActivePressed.childNodes_; if (kids != null) { for (int i = 0; i < kids.length; i++) { IRenderableNode irn = (kids[i] as IRenderableNode); if (irn != null) { // Contains it? LayoutBox box = irn.RenderData.BoxAt(pointer.DownDocumentX, pointer.DownDocumentY); if (box == null) { // Use the last one: box = irn.RenderData.LastBox; } if (box != null) { // Great! Try it as a text node (should always be one): PowerUI.RenderableTextNode htn = (kids[i] as PowerUI.RenderableTextNode); if (htn != null) { // Awesome - get the letter indices: int startIndex = htn.LetterIndex(pointer.DownDocumentX, box); int endIndex = htn.LetterIndex(pointer.DocumentX, pointer.DocumentY); // Create a range: Range range = new Range(); range.startOffset = startIndex; range.endOffset = endIndex; range.startContainer = htn; range.endContainer = htn; // Get the current selection: Selection s = (kids[i].document as HtmlDocument).getSelection(); // Clear all: s.removeAllRanges(); // Add range: s.addRange(range); } break; } } } } }
/// <summary> /// Gives the values of all the CSS properties of an element after /// applying the active stylesheets and resolving any basic computation /// those values may contain. /// </summary> /// <param name="pseudo">The optional pseudo selector to use.</param> /// <returns>The style declaration describing the element.</returns> public Css.ComputedStyle getComputedStyle(string pseudo) { if (string.IsNullOrEmpty(pseudo)) { return(Style.Computed); } // Get the particular pseudo-element: pseudo = pseudo.ToLower().Trim(); // Check if it contains : int index = pseudo.LastIndexOf(':'); if (index != -1) { // Chop from there: pseudo = pseudo.Substring(index + 1); } IRenderableNode el = Style.Computed.GetVirtualChild(pseudo) as IRenderableNode; if (el == null) { return(null); } return(el.ComputedStyle); }
public void AddRenderableNode(string name, IRenderableNode value) { ClearBonesAndMeshList(); if (value == null) { return; } var newNode = value.GetRenderableNode(); // Prevent duplicates. Paths should be unique. if (!renderableNodeNames.Contains(name)) { renderableNodes.Add(newNode); renderableNodeNames.Add(name); } // Duplicate nodes should still update the mesh list. if (newNode is RSkeleton skeleton) { DisplaySkeleton(skeleton); } else if (newNode is IRenderableModel renderableModel) { DisplayMeshes(renderableModel.GetModel()); DisplaySkeleton(renderableModel.GetSkeleton()); } if (value is NUMDL_Node) { var rnumdl = (RNUMDL)newNode; FrameSelection(rnumdl.Model); } }
public void ClearFiles() { animationBar.Model = null; animationBar.Skeleton = null; RenderableNode = null; GC.WaitForPendingFinalizers(); GLObjectManager.DeleteUnusedGLObjects(); }
/// <summary>Doesn't create the virtual if it doesn't exist.</summary> internal void GetVirtual(CssEvent e, int priority) { // Get the CS: ComputedStyle cs = e.SelectorTarget.computedStyle; VirtualElements virts = cs.RenderData.Virtuals; IRenderableNode node = null; if (virts != null) { // Get and apply: node = virts.Get(priority) as IRenderableNode; } // Update the target: e.SelectorTarget = (node == null) ? null : node.RenderData; }
/// <summary>Gets all child elements with the given tag.</summary> /// <param name="selectors">The selectors to match.</param> /// <returns>The set of all tags with this tag.</returns> public void querySelectorAll(Selector[] selectors, INodeList results, CssEvent e, bool one) { if (childNodes_ == null) { return; } for (int i = 0; i < childNodes_.length; i++) { Node node = childNodes_[i]; Element child = node as Element; IRenderableNode irn = (child as IRenderableNode); if (child == null || irn == null) { continue; } ComputedStyle cs = irn.ComputedStyle; for (int s = 0; s < selectors.Length; s++) { // Match? if (selectors[s].StructureMatch(cs, e)) { // Yep! results.push(node); if (one) { return; } } } irn.querySelectorAll(selectors, results, e, one); if (one && results.length == 1) { return; } } }
/// <summary>Called when an @font-face font loads.</summary> public void FontLoaded(DynamicFont font) { if (childNodes_ == null) { return; } int count = childNodes_.length; for (int i = 0; i < count; i++) { IRenderableNode node = (childNodes_[i] as IRenderableNode); if (node == null) { continue; } node.FontLoaded(font); } }
/// <summary>Called when this element goes offscreen.</summary> public void WentOffScreen() { RenderableData renderable = RenderData; renderable.WentOffScreen(); // Apply to all virtual elements: VirtualElements virts = renderable.Virtuals; if (virts != null) { foreach (KeyValuePair <int, Node> kvp in virts.Elements) { // Tell it that it's gone offscreen: IRenderableNode irn = (kvp.Value as IRenderableNode); if (irn != null) { irn.WentOffScreen(); } } } if (childNodes_ != null) { for (int i = 0; i < childNodes_.length; i++) { // Get as a HTML node: IRenderableNode htmlNode = (childNodes_[i] as IRenderableNode); if (htmlNode == null) { return; } // Call offscreen: htmlNode.WentOffScreen(); } } }
/// <summary>Updates mouse overs, touches and the mouse position.</summary> public static void Update(){ InputPointer pointer; // Look out for any 'new' touch events: int touchCount=UnityEngine.Input.touchCount; if(touchCount>0){ // For each one.. for(int i=0;i<touchCount;i++){ // Get the info: Touch touch=UnityEngine.Input.GetTouch(i); pointer=null; // Already seen this one? // There won't be many touches so a straight linear scan is by far the fastest option here. // (It's much better than a dictionary). for(int p=InputPointer.PointerCount-1;p>=0;p--){ // Get the pointer: InputPointer current=InputPointer.AllRaw[p]; if(current.ID==touch.fingerId){ // Got it! pointer=current; break; } } TouchPointer tp=null; if(pointer==null){ // Touch start! A new finger has been seen for the first time. #if UNITY_5_3_OR_NEWER // Could be a stylus: if(touch.type==TouchType.Stylus){ tp=new StylusPointer(); }else{ tp=new FingerPointer(); } #else // Can only assume it's a finger here: tp=new FingerPointer(); #endif // Add it to the available set: tp.ID=touch.fingerId; tp.Add(); }else{ // Apply latest info: tp=(pointer as TouchPointer); } // Mark it as still alive so it doesn't get removed shortly. tp.StillAlive=true; tp.LatestPosition=touch.position; #if UNITY_5_3_OR_NEWER tp.Radius=touch.radius; tp.RadiusVariance=touch.radiusVariance; tp.LatestPressure=touch.pressure; // Is it a stylus? if(tp is StylusPointer){ tp.UpdateStylus(touch.azimuthAngle,touch.altitudeAngle); } // Always a pressure of 1 otherwise (the default). #endif } } // Update each pointer, invalidating itself only if it has moved: bool pointerRemoved=false; for(int i=InputPointer.PointerCount-1;i>=0;i--){ // Get the pointer: pointer=InputPointer.AllRaw[i]; // Update its position and state now: Vector2 delta; bool moved=pointer.Relocate(out delta); if(pointer.Removed){ // It got removed! (E.g. a finger left the screen). pointerRemoved=true; // Clear pressure (mouseup etc): pointer.SetPressure(0f); if(pointer.ActiveOverTarget!=null){ // Shared event: MouseEvent mouseEvent=new MouseEvent(pointer.ScreenX,pointer.ScreenY,pointer.ButtonID,false); mouseEvent.trigger=pointer; mouseEvent.clientX=pointer.DocumentX; mouseEvent.clientY=pointer.DocumentY; mouseEvent.SetModifiers(); mouseEvent.SetTrusted(); // Trigger a mouseout (bubbles): mouseEvent.EventType="mouseout"; mouseEvent.SetTrusted(); pointer.ActiveOverTarget.dispatchEvent(mouseEvent); // And a mouseleave (doesn't bubble). mouseEvent.Reset(); mouseEvent.bubbles=false; mouseEvent.EventType="mouseleave"; pointer.ActiveOverTarget.dispatchEvent(mouseEvent); // Update the CSS (hover; typically out): IRenderableNode irn = (pointer.ActiveOverTarget as IRenderableNode); if(irn!=null){ irn.ComputedStyle.RefreshLocal(true); } } continue; } // If the pointer is invalid or they all are: if(moved || PointersInvalid){ // Figure out what's under it. This takes its pos on the screen // and figures out what's there, as well as converting the position // to one which is relative to the document (used by e.g. a WorldUI). float documentX=pointer.ScreenX; float documentY=pointer.ScreenY; EventTarget newActiveOver=TargetFromPoint(ref documentX,ref documentY,pointer); // Update docX/Y: pointer.DocumentX=documentX; pointer.DocumentY=documentY; // Get the old one: EventTarget oldActiveOver=pointer.ActiveOverTarget; // Shared event: MouseEvent mouseEvent=new MouseEvent(documentX,documentY,pointer.ButtonID,pointer.IsDown); mouseEvent.trigger=pointer; mouseEvent.clientX=pointer.DocumentX; mouseEvent.clientY=pointer.DocumentY; mouseEvent.SetModifiers(); mouseEvent.SetTrusted(); // If overElement has changed from the previous one.. if(newActiveOver!=oldActiveOver){ if(oldActiveOver!=null){ // Trigger a mouseout (bubbles): mouseEvent.EventType="mouseout"; mouseEvent.SetTrusted(); mouseEvent.relatedTarget=newActiveOver; oldActiveOver.dispatchEvent(mouseEvent); // And a mouseleave (doesn't bubble). // Only triggered if newActiveOver is *not* the parent of oldActiveOver. if(oldActiveOver.eventTargetParentNode!=newActiveOver){ mouseEvent.Reset(); mouseEvent.bubbles=false; mouseEvent.EventType="mouseleave"; mouseEvent.relatedTarget=newActiveOver; oldActiveOver.dispatchEvent(mouseEvent); } } // Update it: pointer.ActiveOverTarget=newActiveOver; if(oldActiveOver!=null){ // Update the CSS (hover; typically out): IRenderableNode irn = (oldActiveOver as IRenderableNode); if(irn!=null){ irn.ComputedStyle.RefreshLocal(true); } } if(newActiveOver!=null){ // Update the CSS (hover; typically in) IRenderableNode irn = (newActiveOver as IRenderableNode); if(irn!=null){ irn.ComputedStyle.RefreshLocal(true); } } // Trigger a mouseover (bubbles): mouseEvent.Reset(); mouseEvent.bubbles=true; mouseEvent.relatedTarget=oldActiveOver; mouseEvent.EventType="mouseover"; if(newActiveOver==null){ // Clear the main UI tooltip: UI.document.tooltip=null; // Dispatch to unhandled: Unhandled.dispatchEvent(mouseEvent); }else{ newActiveOver.dispatchEvent(mouseEvent); // Set the tooltip if we've got one: UI.document.tooltip=newActiveOver.getTitle(); // And a mouseenter (doesn't bubble). // Only triggered if newActiveOver is *not* a child of oldActiveOver. if(newActiveOver.eventTargetParentNode!=oldActiveOver){ mouseEvent.Reset(); mouseEvent.bubbles=false; mouseEvent.EventType="mouseenter"; mouseEvent.relatedTarget=oldActiveOver; newActiveOver.dispatchEvent(mouseEvent); } } } if(moved){ // Trigger a mousemove event: mouseEvent.Reset(); mouseEvent.bubbles=true; mouseEvent.EventType="mousemove"; if(newActiveOver==null){ Unhandled.dispatchEvent(mouseEvent); }else{ newActiveOver.dispatchEvent(mouseEvent); } } } // If the pointer requires pressure changes.. if(pointer.FireTouchEvents){ // Set the pressure (which triggers mousedown/up for us too): TouchPointer tp=(pointer as TouchPointer); tp.SetPressure(tp.LatestPressure); // We test touch move down here because it must happen after we've // transferred 'ActiveOver' to 'ActiveDown' which happens inside SetPressure. // Touch events are triggered on the element that was pressed down on // (even if we've moved beyond it's bounds). if(moved){ // Trigger a touchmove event too: TouchEvent te=new TouchEvent("touchmove"); te.trigger=pointer; te.SetTrusted(); te.clientX=pointer.DocumentX; te.clientY=pointer.DocumentY; te.SetModifiers(); if(pointer.ActivePressedTarget==null){ // Trigger on unhandled: Unhandled.dispatchEvent(te); }else{ pointer.ActivePressedTarget.dispatchEvent(te); } } } // Test for dragstart if(pointer.IsDown && moved){ // How far has it moved since it went down? if(pointer.DragStatus==InputPointer.DRAG_UNKNOWN){ // Is the element we pressed (or any of its parents) draggable? if(pointer.MinDragDistance==0f){ // Find if we've got a draggable element: pointer.ActiveUpdatingTarget=GetDraggable(pointer.ActivePressedTarget as Element); // Find the min drag distance: pointer.MinDragDistance=pointer.GetMinDragDistance(); } if(pointer.MovedBeyondDragDistance){ // Possibly dragging. // Actually marked as 'draggable'? if(pointer.ActiveUpdatingTarget!=null){ // Try start drag: DragEvent de=new DragEvent("dragstart"); de.trigger=pointer; de.SetModifiers(); de.SetTrusted(); de.deltaX=delta.x; de.deltaY=delta.y; de.clientX=pointer.DocumentX; de.clientY=pointer.DocumentY; if(pointer.ActivePressedTarget.dispatchEvent(de)){ // We're now dragging! pointer.DragStatus=InputPointer.DRAGGING; }else{ // It didn't allow it. This status prevents it from spamming dragstart. pointer.DragStatus=InputPointer.DRAG_NOT_AVAILABLE; } }else{ // Selectable? IRenderableNode irn = (pointer.ActivePressedTarget as IRenderableNode); if(irn!=null){ // It's renderable; can it be selected? ComputedStyle cs=irn.ComputedStyle; Css.Value userSelect=cs[Css.Properties.UserSelect.GlobalProperty]; if(userSelect!=null && !(userSelect.IsType(typeof(Css.Keywords.None))) && !userSelect.IsAuto){ // Selectable! Css.Properties.UserSelect.BeginSelect(pointer,userSelect); // Set status: pointer.DragStatus=InputPointer.SELECTING; // Focus it if needed: HtmlElement html=(pointer.ActivePressedTarget as HtmlElement); if(html!=null){ html.focus(); } }else{ // This status prevents it from spamming, at least until we release. pointer.DragStatus=InputPointer.DRAG_NOT_AVAILABLE; } } } } }else if(pointer.DragStatus==InputPointer.DRAGGING){ // Move the dragged element (the event goes to everybody): if(pointer.ActivePressedTarget!=null){ DragEvent de=new DragEvent("drag"); de.trigger=pointer; de.SetModifiers(); de.SetTrusted(); de.deltaX=delta.x; de.deltaY=delta.y; de.clientX=pointer.DocumentX; de.clientY=pointer.DocumentY; if(pointer.ActivePressedTarget.dispatchEvent(de)){ // Note that onDrag runs on the *dragging* element only. Element dragging; if(pointer.ActiveUpdatingTarget==null){ dragging = (Element)pointer.ActivePressedTarget; }else{ dragging = (Element)pointer.ActiveUpdatingTarget; } if(dragging.OnDrag(de)){ // Run the PowerUI default - move the element: RenderableData renderData=(dragging as IRenderableNode).RenderData; // Get the "actual" left/top values: if(renderData.FirstBox!=null){ // Get computed style: ComputedStyle cs=renderData.computedStyle; // Fix if it isn't already: if(renderData.FirstBox.PositionMode!=PositionMode.Fixed){ cs.ChangeTagProperty("position","fixed"); } // Get top/left pos: float left=renderData.FirstBox.X+delta.x; float top=renderData.FirstBox.Y+delta.y; // Write it back out: cs.ChangeTagProperty("left",new Css.Units.DecimalUnit(left)); cs.ChangeTagProperty("top",new Css.Units.DecimalUnit(top)); } } } } }else if(pointer.DragStatus==InputPointer.SELECTING){ // Update the selection. if(pointer.ActivePressedTarget!=null){ DragEvent de=new DragEvent("drag"); de.trigger=pointer; de.SetModifiers(); de.SetTrusted(); de.clientX=pointer.DocumentX; de.clientY=pointer.DocumentY; if(pointer.ActivePressedTarget.dispatchEvent(de)){ // We can only select elements: Element pressedElement = (Element)pointer.ActivePressedTarget; // Get the current selection: Selection s=(pressedElement.document as HtmlDocument).getSelection(); // Safety check: if(s.ranges.Count>0){ // Get the range: Range range=s.ranges[0]; // Get text node: RenderableTextNode htn=(range.startContainer as RenderableTextNode); if(htn!=null){ // Get the new end index: int endIndex=htn.LetterIndex(pointer.DocumentX,pointer.DocumentY); // Update: range.endOffset=endIndex; // Flush: s.UpdateSelection(true,range); } } } } } } } if(pointerRemoved){ // Tidy the removed ones: InputPointer.Tidy(); } // Clear invalidated state: PointersInvalid=false; #if MOBILE // Handle mobile keyboard: if(MobileKeyboard!=null){ HandleMobileKeyboardInput(); } #endif }
/// <summary>Part of shrink-to-fit. Computes the maximum and minimum possible width for an element. /// This does not include the elements own padding/margin/border.</summary> public void GetWidthBounds(out float min, out float max) { min = 0f; max = 0f; // For each child, get its width bounds too. if (RenderData.FirstBox == null) { return; } if (childNodes_ != null) { // Current line: float cMin = 0f; float cMax = 0f; for (int i = 0; i < childNodes_.length; i++) { Node child = childNodes_[i]; IRenderableNode renderable = (child as IRenderableNode); if (renderable == null) { continue; } float bMin; float bMax; if (child is PowerUI.RenderableTextNode) { // Always get bounds: renderable.GetWidthBounds(out bMin, out bMax); } else { // Get the first box from the render data: RenderableData rd = renderable.RenderData; LayoutBox box = rd.FirstBox; if (box == null) { continue; } // If it's inline (or float) then it's additive to the current line. if ((box.DisplayMode & DisplayMode.OutsideBlock) != 0 && box.FloatMode == FloatMode.None) { // Line break! cMin = 0f; cMax = 0f; } // Get an explicit width: bool wasAuto; bMin = rd.GetWidth(true, out wasAuto); if (bMin == float.MinValue) { // Get the bounds: renderable.GetWidthBounds(out bMin, out bMax); } else { bMax = bMin; } // Add margins etc: float extraStyle = ( box.Border.Left + box.Border.Right + box.Padding.Left + box.Padding.Right + box.Margin.Left + box.Margin.Right ); bMin += extraStyle; bMax += extraStyle; } // Apply to line: cMin += bMin; cMax += bMax; // Longest line? if (cMin > min) { min = cMin; } if (cMax > max) { max = cMax; } } } }
/* * public override void OnChildrenLoaded(){ * * // Construct the selector structure now: * // Style.Computed.RefreshStructure(); * * } */ /// <summary>Part of shrink-to-fit. Computes the maximum and minimum possible width for an element. /// This does not include the elements own padding/margin/border.</summary> public virtual void GetWidthBounds(out float min, out float max) { min = 0f; max = 0f; // For each child, get its width bounds too. if (RenderData.FirstBox == null) { return; } if (childNodes_ != null) { // Current line: float cMin = 0f; float cMax = 0f; for (int i = 0; i < childNodes_.length; i++) { Node child = childNodes_[i]; IRenderableNode renderable = (child as IRenderableNode); if (renderable == null) { continue; } float bMin; float bMax; if (child is RenderableTextNode) { // Always get bounds: renderable.GetWidthBounds(out bMin, out bMax); } else { // Get the first box from the render data: ComputedStyle cs = renderable.ComputedStyle; RenderableData rd = renderable.RenderData; LayoutBox box = rd.FirstBox; if (box == null) { continue; } int displayMode = box.DisplayMode; // If it's inline (or float) then it's additive to the current line. if ((displayMode & DisplayMode.OutsideBlock) != 0 && box.FloatMode == FloatMode.None) { // Line break! cMin = 0f; cMax = 0f; } // Get an explicit width: bool wasAuto; bMin = rd.GetWidth(true, out wasAuto); if (bMin == float.MinValue) { // Get the bounds: renderable.GetWidthBounds(out bMin, out bMax); } else { bMax = bMin; } // Add margins etc (NB: These are calculated twice due to %): BoxStyle padding = cs.GetPaddingBox(displayMode); BoxStyle border = cs.GetBorderBox(displayMode); // Compute the initial margin: bool marginAuto = false; BoxStyle margin = cs.GetMarginBox(displayMode, box.FloatMode, ref marginAuto); float extraStyle = ( border.Left + border.Right + padding.Left + padding.Right + margin.Left + margin.Right ); bMin += extraStyle; bMax += extraStyle; } // Apply to line: cMin += bMin; cMax += bMax; // Longest line? if (cMin > min) { min = cMin; } if (cMax > max) { max = cMax; } } } }
/// <summary>Applies a structurally matched selector to the DOM. /// Occurs shortly after StructureMatch.</summary> public MatchingSelector BakeToTarget(ComputedStyle cs, CssEvent e) { // Get the node: Node node = cs.Element; // First, generate our instance: MatchingSelector ms = new MatchingSelector(); // Update it: ms.Selector = this; ms.MatchedRoots = new MatchingRoot[RootCount]; // For each root, create a MatchingRoot object. // Apply target - this helps track which element we're actually testing: e.CurrentNode = node; e.SelectorTarget = null; // We always start from the tail and work backwards. // If we get a match, then the caller can do whatever it wants to the target. for (int i = RootCount - 1; i >= 0; i--) { // Get the matcher: RootMatcher rm = Roots[i]; // Try matching this root: if (!rm.TryMatch(e.CurrentNode)) { // Failed! If we had a matcher and it has Repeat set true, try again: if (rm.NextMatcher != null && rm.NextMatcher.Repeat) { // Move target: rm.NextMatcher.MoveUpwards(e); // Try matching again: i++; continue; } } else { // Match! e.CurrentNode is the node to add. // Create the instance: MatchingRoot matchedRoot = new MatchingRoot(); matchedRoot.Root = rm; matchedRoot.Selector = ms; matchedRoot.Node = e.CurrentNode; // Get renderable node: IRenderableNode renderable = (e.CurrentNode as IRenderableNode); // Add to selector: ms.MatchedRoots[i] = matchedRoot; // Add: ComputedStyle nodeCs = renderable.ComputedStyle; // Push the match now into the linked list: if (nodeCs.FirstMatch == null) { nodeCs.FirstMatch = matchedRoot; nodeCs.LastMatch = matchedRoot; } else { matchedRoot.PreviousInStyle = nodeCs.LastMatch; nodeCs.LastMatch.NextInStyle = matchedRoot; nodeCs.LastMatch = matchedRoot; } if (rm.IsTarget) { // Update the target now: e.SelectorTarget = renderable.RenderData; } } // If we have a structure matcher, run it now. It'll move CurrentNode for us: if (rm.PreviousMatcher != null) { // Move target: rm.PreviousMatcher.MoveUpwards(e); } } // Final pass - if we have a pseudo-element, apply it now: if (PseudoElement != null) { PseudoElement.Select(e); } // Apply target: ms.Target = e.SelectorTarget; // Finally, refresh all: ms.ResetActive(); return(ms); }
public void Clear() { RenderableNode = null; }
/// <summary>Computes the box for the given element now.</summary> public BoxStyle Compute(Css.Value value, RenderableData context, ComputedStyle parent, int display, int floatMode, ref bool dimsRequired) { // Result: BoxStyle result = new BoxStyle(); if (value == null) { return(result); } float marginSpace; // Right and left: Css.Value a = value[1]; Css.Value b = value[3]; // Default here is '0': if (a == null) { a = Css.Value.Empty; } if (b == null) { b = Css.Value.Empty; } if (a.IsAuto || b.IsAuto) { // One or both are auto. // If we are an inline level element or we're floating then any 'auto' values are 0. if (floatMode != 0 || (display & DisplayMode.OutsideInline) != 0) { if (!a.IsAuto) { // Left auto, right is a number. result.Right = a.GetDecimal(context, Right); } else if (!b.IsAuto) { // Right auto, left is a number. result.Left = b.GetDecimal(context, Left); } } else { if (!dimsRequired) { // Dimensions are required - quit here. dimsRequired = true; return(result); } if (parent != null) { while (parent.DisplayX == DisplayMode.Inline) { // Special case - InnerWidth is invalid. // Go up the DOM to reach a flow-level element: IRenderableNode parentNode = parent.Element.parentNode as IRenderableNode; if (parentNode == null) { break; } parent = parentNode.ComputedStyle; } } if (parent == null) { // Parent is actually the document. marginSpace = context.Document.Viewport.Width - context.FirstBox.BorderedWidth; } else { // Get available margin space. marginSpace = parent.InnerWidth - context.FirstBox.BorderedWidth; } if (!a.IsAuto) { // Left auto, right is a number. result.Right = a.GetDecimal(context, Right); result.Left = marginSpace - result.Right; // Auto doesn't create negatives: if (result.Left < 0f) { result.Left = 0f; } } else if (!b.IsAuto) { // Right auto, left is a number. result.Left = b.GetDecimal(context, Left); result.Right = marginSpace - result.Left; // Auto doesn't create negatives: if (result.Right < 0f) { result.Right = 0f; } } else if (marginSpace > 0f) // No negatives allowed // Centering: { result.Right = result.Left = marginSpace / 2f; } } } else { // Both are ordinary numbers: result.Right = a.GetDecimal(context, Right); result.Left = b.GetDecimal(context, Left); } // Ignore margin top/bottom entirely on inline, in-flow elements. if (display != DisplayMode.Inline) { // Top and bottom: a = value[0]; b = value[2]; // Default here is '0': if (a == null) { a = Css.Value.Empty; } if (b == null) { b = Css.Value.Empty; } if (a.IsAuto || b.IsAuto) { // One or both are auto. // If we are an inline level element or floating then any 'auto' values are 0. if (floatMode != 0 || (display & DisplayMode.OutsideInline) != 0) { if (!a.IsAuto) { // Bottom auto, top is a number. result.Top = a.GetDecimal(context, Top); } else if (!b.IsAuto) { // Top auto, bottom is a number. result.Bottom = b.GetDecimal(context, Bottom); } } else { if (!dimsRequired) { // Dimensions are required - quit here. dimsRequired = true; return(result); } if (parent != null) { while (parent.DisplayX == DisplayMode.Inline) { // Special case - InnerHeight is invalid. // Go up the DOM to reach a flow-level element: IRenderableNode parentNode = parent.Element.parentNode as IRenderableNode; if (parentNode == null) { break; } parent = parentNode.ComputedStyle; } } if (parent == null) { // Parent is actually the document. marginSpace = context.Document.Viewport.Height - context.FirstBox.BorderedHeight; } else { // Get available margin space. marginSpace = parent.InnerHeight - context.FirstBox.BorderedHeight; } if (!a.IsAuto) { // Bottom auto, top is a number. result.Top = a.GetDecimal(context, Top); result.Bottom = marginSpace - result.Top; // Auto doesn't create negatives: if (result.Bottom < 0f) { result.Bottom = 0f; } } else if (!b.IsAuto) { // Top auto, bottom is a number. result.Bottom = b.GetDecimal(context, Bottom); result.Top = marginSpace - result.Bottom; // Auto doesn't create negatives: if (result.Top < 0f) { result.Top = 0f; } } else if (marginSpace > 0f) // No negatives allowed // Centering: { result.Top = result.Bottom = marginSpace / 2f; } } } else { // Both are ordinary numbers: result.Top = a.GetDecimal(context, Top); result.Bottom = b.GetDecimal(context, Bottom); } } return(result); }
/// <summary>Sets the pressure level.</summary> public void SetPressure(float v) { // Was it up before? bool wasUp = (Pressure == 0f); // Set pressure: Pressure = v; // If it's non-zero then we'll need to grab the clicked object: if (v == 0f) { if (wasUp) { // No change. } else { // It's up now. Clear: EventTarget oldActivePressed = ActivePressedTarget; // Clear: ActivePressedTarget = null; if (oldActivePressed != null) { // Refresh CSS (active; applies to parents too): EventTarget current = oldActivePressed; while (current != null) { // Get it as a renderable node: IRenderableNode irn = (current as IRenderableNode); if (irn != null) { irn.ComputedStyle.RefreshLocal(); } current = current.eventTargetParentNode; } } // Trigger up event. MouseEvent e = new MouseEvent(DocumentX, DocumentY, ButtonID, false); e.trigger = this; e.SetModifiers(); e.EventType = "mouseup"; if (oldActivePressed == null) { Input.Unhandled.dispatchEvent(e); } else { oldActivePressed.dispatchEvent(e); } // Click if needed: if (oldActivePressed == ActiveOverTarget && DragStatus == 0) { // Click! e.Reset(); e.trigger = this; e.SetModifiers(); e.EventType = "click"; if (oldActivePressed == null) { Input.Unhandled.dispatchEvent(e); } else if (oldActivePressed.dispatchEvent(e)) { // Clear the selection if necessary: HtmlElement h = (oldActivePressed as HtmlElement); if (h != null) { // Clear selection if there is one: (h.document as HtmlDocument).clearSelection(); } } } if (FireTouchEvents) { // Trigger a touchend event too: TouchEvent te = new TouchEvent("touchend"); te.trigger = this; te.SetModifiers(); te.SetTrusted(); te.clientX = DocumentX; te.clientY = DocumentY; if (oldActivePressed == null) { Input.Unhandled.dispatchEvent(te); } else { oldActivePressed.dispatchEvent(te); } } if (DragStatus == DRAGGING) { // Trigger dragend: DragEvent de = new DragEvent("dragend"); de.trigger = this; de.SetModifiers(); de.SetTrusted(); de.clientX = ScreenX; de.clientY = ScreenY; if (oldActivePressed.dispatchEvent(de)) { // Trigger a drop event next: de.Reset(); de.EventType = "drop"; if (ActiveOverTarget != null && ActiveOverTarget.dispatchEvent(de)) { // Proceed to try and drop it into the dropzone (ActiveOver). } } } else if (DragStatus == SELECTING) { // Finished selection - trigger selectionend: Dom.Event sc = new Dom.Event("selectionend"); sc.SetTrusted(); // Dispatch on the element: oldActivePressed.dispatchEvent(sc); } // Always clear drag status: DragStatus = 0; MinDragDistance = 0f; } } else if (wasUp) { // It was up and it's now just gone down. // Cache position: DownDocumentX = DocumentX; DownDocumentY = DocumentY; // Cache down: ActivePressedTarget = ActiveOverTarget; // Trigger down event. if (ActivePressedTarget != null) { // Refresh CSS (active; applies to parents too): EventTarget current = ActivePressedTarget; while (current != null) { // Get it as a renderable node: IRenderableNode irn = (current as IRenderableNode); if (irn != null) { irn.ComputedStyle.RefreshLocal(); } current = current.eventTargetParentNode; } } // Trigger down event. MouseEvent e = new MouseEvent(DocumentX, DocumentY, ButtonID, true); e.trigger = this; e.EventType = "mousedown"; e.SetModifiers(); if (ActivePressedTarget == null) { Input.Unhandled.dispatchEvent(e); } else { ActivePressedTarget.dispatchEvent(e); } if (FireTouchEvents) { // Trigger a touchend event too: TouchEvent te = new TouchEvent("touchstart"); te.trigger = this; te.clientX = DocumentX; te.clientY = DocumentY; te.SetTrusted(); te.SetModifiers(); if (ActivePressedTarget == null) { Input.Unhandled.dispatchEvent(te); } else { ActivePressedTarget.dispatchEvent(te); } } } }
/// <summary>Relocates all DOM elements by calculating their onscreen position. /// Each element may allocate sections of the 3D mesh (blocks) which are then flushed out /// into the unity mesh and onto the screen.</summary> public void Layout() { DoLayout = false; FullReflow = true; HighestUpdateMode = UpdateMode.None; Reset(); // Invalidate input pointers: // (So they figure out what elements are under the mouse/fingers) PowerUI.Input.PointersInvalid = true; // First, push all batches to the pool - inlined for speed: // Note that no isolated batches enter either the queue or the pool until their no longer isolated. if (FirstBatch != null) { LastBatch.BatchAfter = UIBatchPool.First; UIBatchPool.First = FirstBatch; } FirstBatch = LastBatch = null; // Note: Batches are Prepared For Layout as they are added. LayoutOccuring = true; // Position elements locally. // This sets their ParentOffset values and as a result finds their PixelWidth. IRenderableNode root = RootDocument.documentElement as IRenderableNode; if (root != null) { // Perform the initial reflow: RenderableData rd = root.RenderData; rd.UpdateCss(this); rd.Reflow(this); // Next up, position them globally: // This calculates OffsetLeft/Top and also fires the render event on the computed style object. rd.Render(this); } LayoutOccuring = false; // Tell each batch we're done laying them out: UIBatch currentBatch = FirstBatch; while (currentBatch != null) { currentBatch.CompletedLayout(); currentBatch = currentBatch.BatchAfter; } if (StylesToUpdate != null) { // Clear the isPainting flag. RenderableData style = StylesToUpdate; StylesToUpdate = null; while (style != null) { style.NextUpdateMode = UpdateMode.None; style = style.Next; } } // Hide all pool entries: UIBatchPool.HideAll(); FullReflow = false; }
/// <summary>Update causes all changes to be applied and layouts to occur.</summary> public void Update() { if (DoLayout && AllowLayout) { // Layout RootDocument. Layout(); } else if (StylesToUpdate != null) { // Local update - these events typically fire from changes to things like colour/z-index etc // as well as for reflows of "flow root" nodes. // It's done down here incase a full layout request is made (above). // If a full layout request was made, it would cover all of these anyway. UpdateMode modeToUse = HighestUpdateMode; HighestUpdateMode = UpdateMode.None; bool anyReflowMode = ((int)modeToUse > (int)UpdateMode.PaintAll); if (anyReflowMode) { // We'll be re-rendering. Reset(); // Invalidate input pointers: // (So they figure out what elements are under the mouse/fingers) PowerUI.Input.PointersInvalid = true; // First, push all batches to the pool - inlined for speed: // Note that no isolated batches enter either the queue or the pool until their no longer isolated. if (FirstBatch != null) { LastBatch.BatchAfter = UIBatchPool.First; UIBatchPool.First = FirstBatch; } FirstBatch = LastBatch = null; // Note: Batches are Prepared For Layout as they are added. LayoutOccuring = true; } Css.RenderableData style = StylesToUpdate; StylesToUpdate = null; while (style != null) { UpdateMode mode = style.NextUpdateMode; switch (mode) { case UpdateMode.PaintAll: // Don't bother if we're doing either kind of reflow: if (anyReflowMode) { continue; } // Repaint it: style.RepaintAll(this); break; case UpdateMode.Paint: // Repaint it: style.Repaint(this); // Must also repaint the nodes child text nodes too (as they share the same CS): NodeList kids = style.Node.childNodes_; if (kids != null) { // For each child node.. for (int i = 0; i < kids.length; i++) { // Get it as a text node: Node child = kids[i]; if (child is TextNode) { // Repaint it too: (child as IRenderableNode).RenderData.Repaint(this); } } } break; case UpdateMode.Reflow: // Flow root reflow request. // Must setup stacks and any other renderer settings here! // Perform the initial reflow now: style.UpdateCss(this); style.Reflow(this); break; case UpdateMode.Render: // Only call render: style.Render(this); break; /* * case UpdateMode.FastReflow: * * // Fast reflow request only requires a repaint. * * break; */ } // Clear its update mode: style.NextUpdateMode = UpdateMode.None; style = style.Next; } if (!anyReflowMode) { // Only a flush is required: UIBatch toFlush = FirstBatch; while (toFlush != null) { toFlush.Flush(); toFlush = toFlush.BatchAfter; } } else { // Position elements locally. // This sets their ParentOffset values and as a result finds their PixelWidth. IRenderableNode root = RootDocument.documentElement as IRenderableNode; if (root != null) { // Finally, position them globally: // This calculates OffsetLeft/Top and also fires the render event on the computed style object. root.RenderData.Render(this); } LayoutOccuring = false; // Tell each batch we're done laying them out: UIBatch currentBatch = FirstBatch; while (currentBatch != null) { currentBatch.CompletedLayout(); currentBatch = currentBatch.BatchAfter; } // Hide all pool entries: UIBatchPool.HideAll(); } } }
internal void BuildString(Node node) { ComputedDataFor = node; string result = ""; if (node.Properties != null && node.Properties.Count > 0) { result += "Attributes:\r\n"; foreach (KeyValuePair <string, string> kvp in node.Properties) { result += kvp.Key + ": " + kvp.Value + "\r\n"; } result += "\r\n"; } // Is the node renderable? IRenderableNode renderable = (node as IRenderableNode); if (renderable != null) { result += "Applied Selectors:\r\n"; // Get the computed style: ComputedStyle cs = renderable.ComputedStyle; // All applied styles (without allocating a new list): MatchingRoot match = cs.FirstMatch; while (match != null) { // Note! These nodes can be participants from other nearby elements. // That happens with any combined selector. // To filter those ones out, check if this element is the actual target: if (match.IsTarget) { // Rule is simply matching.Rule: StyleRule rule = match.Rule; // To get the underlying selector, it's rule.Selector: Selector selector = rule.Selector; // Note that this builds the selector text - avoid calling from OnGUI! result += selector.selectorText + "\r\n"; } // Next one: match = match.NextInStyle; } result += "\r\n"; result += "Computed style:\r\n"; // Computed values: foreach (KeyValuePair <CssProperty, Css.Value> kvp in cs.Properties) { // kvp.Key (a CSS property) is set to kvp.Value (a CSS value) result += kvp.Key.Name + ": "; if (kvp.Value is Css.Keywords.Inherit) { result += (kvp.Value as Css.Keywords.Inherit).From + " (inherit)"; } else { result += kvp.Value; } result += "\r\n"; } result += "\r\n"; result += "Computed boxes:\r\n"; // Computed values: RenderableData renderData = renderable.RenderData; if (renderData.FirstBox == null) { result += "There's no boxes. The element isn't visible.\r\n"; } else { LayoutBox box = renderData.FirstBox; while (box != null) { result += box.ToString() + " (Scroll: " + box.Scroll.Left + ", " + box.Scroll.Top + ")\r\n"; box = box.NextInElement; } } result += "\r\n"; } // JS event hooks: EventTarget target = (node as EventTarget); if (target != null) { result += "Events:\r\n"; if (target.Events == null || target.Events.Handlers == null || target.Events.Handlers.Count == 0) { result += "No events hooked up to this element.\r\n"; } else { // Grab the JS event handlers: Dictionary <string, List <EventListener> > allHandlers = target.Events.Handlers; // Key is e.g. "mousedown" // Value is all the things that'll run when the event triggers. foreach (KeyValuePair <string, List <EventListener> > kvp in allHandlers) { int count = kvp.Value.Count; string plural = count == 1?"":"s"; result += kvp.Key + ": (" + count + " listener" + plural + ")\r\n"; } } } ComputedNodeData = result; }