/// <summary>Removes the pointers marked for removal.</summary>
        internal static void Tidy()
        {
            int tidyIndex = 0;

            for (int i = 0; i < PointerCount; i++)
            {
                // Get the pointer:
                InputPointer pointer = AllRaw[i];

                if (pointer.Removed)
                {
                    continue;
                }

                // Transfer:
                if (tidyIndex != i)
                {
                    AllRaw[tidyIndex] = pointer;
                }

                // Move index up:
                tidyIndex++;
            }

            // Update count:
            PointerCount = tidyIndex;
        }
Example #2
0
        /// <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;
                        }
                    }
                }
            }
        }
Example #3
0
		/// <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
			
		}
Example #4
0
		/// <summary>Finds an EventTarget from the given screen location.
		/// This fires a ray into the scene if the point isn't on the main UI and caches the hit in the given pointer.
		/// X and Y are updated with the document-relative point if it's on a WorldUI/ on the Unity UI.</summary>
		public static EventTarget TargetFromPoint(ref float x,ref float y,InputPointer pointer){
			
			Element result=null;
			
			if(UI.document!=null){
				
				// Test the main UI:
				result=UI.document.elementFromPointOnScreen(x,y);
				
				if(result!=null){
					
					// Is it transparent or not html/body?
					if(AcceptsInput(result)){
						// Great, we're done!
						
						if(pointer!=null){
							pointer.LatestHitSuccess=false;
						}
						
						return result;
					}
					
				}
				
			}
			
			// Handle a Unity UI if needed.
			Vector2 point;
			
			#if HANDLE_UNITY_UI
			
			if(UnityUICaster!=null){
				
				// Try hitting the Unity UI first.
				point=new Vector2(x,ScreenInfo.ScreenY-1-y);
				
				HtmlDocument panelDoc=MapToUIPanel(ref point);
				
				if(panelDoc!=null){
					
					// Test for an element there:
					result=panelDoc.elementFromPointOnScreen(point.x,point.y);
					
					if(result!=null){
						
						// Is it transparent?
						
						// We're still checking a UI so we might still pass "through" it.
						if(AcceptsInput(result)){
							
							// Got a hit! Update x/y:
							x=point.x;
							y=point.y;
							
							if(pointer!=null){
								pointer.LatestHitSuccess=false;
							}
							
							return result;
							
						}
						
					}
					
				}
			
			}
			
			#endif
			
			// Go after WorldUI's next.
			RaycastHit worldUIHit;
			Camera cam=CameraFor3DInput;
			
			if(cam==null){
				// No camera!
				
				if(pointer!=null){
					pointer.LatestHitSuccess=false;
				}
				
				return null;
			}
			
			Vector2 screenPoint = new Vector2(x,ScreenInfo.ScreenY-1-y);
			
			if(pointer == null){
				
				// Cast into the scene now:
				Ray ray=cam.ScreenPointToRay(screenPoint);
				if(!Physics.Raycast(ray,out worldUIHit)){
					
					// Nothing hit.
					return null;
					
				}
				
			}else if(!pointer.Raycast(out worldUIHit,cam,screenPoint)){
				
				// Nothing hit.
				pointer.LatestHitSuccess=false;
				return null;
				
			}
			
			// Cache it:
			if(pointer!=null){
				pointer.LatestHit=worldUIHit;
				pointer.LatestHitSuccess=true;
			}
			
			// Did it hit a worldUI?
			WorldUI worldUI=WorldUI.Find(worldUIHit);
			
			if(worldUI==null){
				
				// Nope! Get the eventTarget for the gameObject:
				#if Enable3DInput
				return worldUIHit.transform.gameObject.getEventTarget();
				#else
				return null;
				#endif
			}
			
			// Resolve the hit into a -0.5 to +0.5 point:
			// (we're ok to reuse x/y here):
			worldUI.ResolvePoint(worldUIHit,out x,out y);
			
			// Map it from a relative point to a 'real' one:
			point=worldUI.RelativePoint(x,y);
			
			// Apply to x/y:
			x=point.x;
			y=point.y;
			
			// Pull an element from that worldUI:
			return worldUI.document.elementFromPointOnScreen(x,y);
			
		}
Example #5
0
		/// <summary>Finds an Element from the given screen location.
		/// This fires a ray into the scene if the point isn't on the main UI and caches the hit in the given pointer.
		/// X and Y are updated with the document-relative point if it's on a WorldUI/ on the Unity UI.</summary>
		public static Element ElementFromPoint(ref float x,ref float y,InputPointer pointer){
			return TargetFromPoint(ref x,ref y,pointer) as Element;
		}
 /// <summary>Doubles the size of the pointer stack, AllRaw.</summary>
 private static void ResizePointerStack()
 {
     InputPointer[] newStack = new InputPointer[AllRaw.Length * 2];
     Array.Copy(AllRaw, 0, newStack, 0, AllRaw.Length);
     AllRaw = newStack;
 }