/// <summary>Moves the focus to the previous element as defined by tabindex. /// All elements with an explicit tabindex are defined as being before all /// elements which don't have an explicit tabindex.</summary> /// <returns>True if anything happened.</returns> public bool TabPrevious() { // These track the current best found element. int bestSoFar = int.MaxValue; HtmlElement best = null; // Get the current focused element: HtmlElement focused = htmlActiveElement; if (focused == null) { // Haven't got one - hunt for a node with *no* tabindex first: body.SearchChildFocusable(null, false, -1, ref bestSoFar, ref best); if (best == null) { // Find the last node with a tabIndex: body.SearchChildFocusable(null, false, int.MaxValue, ref bestSoFar, ref best); } } else { best = focused.GetFocusedPrevious(); } if (best != null) { // Focus it now: best.focus(); return(true); } return(false); }
/// <summary>If there is an element focused, this will move focus to the nearest focusable element to the right. /// You can define 'focusable' on any element, or use a tag that is focusable anyway (input, textarea, a etc). /// You can also define focus-right="anElementID" to override which element will be focused next.</summary> public void MoveFocusRight() { if (htmlActiveElement == null) { return; } // Grab the element to the right: HtmlElement element = htmlActiveElement.GetFocusableRight(); if (element != null) { // Focus it: element.focus(); } }
/// <summary>If there is an element focused, this will move focus to the nearest focusable element below. /// You can define 'focusable' on any element, or use a tag that is focusable anyway (input, textarea, a etc). /// You can also define focus-down="anElementID" to override which element will be focused next.</summary> public void MoveFocusDown() { if (htmlActiveElement == null) { return; } // Grab the element below: HtmlElement element = htmlActiveElement.GetFocusableBelow(); if (element != null) { // Focus it: element.focus(); } }
/// <summary>If there is an element focused, this will move focus to the nearest focusable element above. /// You can define 'focusable' on any element, or use a tag that is focusable anyway (input, textarea, a etc). /// You can also define focus-up="anElementID" to override which element will be focused next.</summary> public void MoveFocusUp() { if (htmlActiveElement == null) { return; } // Grab the element above: HtmlElement element = htmlActiveElement.GetFocusableAbove(); if (element != null) { // Focus it: element.focus(); } }
/// <summary>Moves the focus to the next element as defined by tabindex. /// All elements with an explicit tabindex are defined as being before all /// elements which don't have an explicit tabindex.</summary> /// <returns>True if anything happened.</returns> public bool TabNext() { // - From the current focused element, we check all elements after it to see which // has the 'closest' tabIndex to currentIndex. // - If there are no immediate matches, we wrap around the DOM and continue searching // - If there is no current focused element, we start at very top and don't wrap at all. // These track the current best found element. int bestSoFar = int.MaxValue; HtmlElement best = null; // Get the current focused element: HtmlElement focused = htmlActiveElement; if (focused == null) { // Haven't got one - hunt for a node with a tabindex first: body.SearchChildFocusable(null, true, 0, ref bestSoFar, ref best); if (best == null) { // Find the first focusable node: body.SearchChildFocusable(null, true, -1, ref bestSoFar, ref best); } } else { best = focused.GetFocusedNext(); } if (best != null) { // Focus it now: best.focus(); return(true); } return(false); }
/// <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 }