/// <summary> /// Metoda získá a vrátí seznam Child prvků daného prvku <paramref name="parentItem"/>. /// Seznam setřídí podle <see cref="IInteractiveItem.TabOrder"/> vzestupně (pro <paramref name="direction"/> == <see cref="Direction.Positive"/>) /// nebo sestupně (pro <paramref name="direction"/> == <see cref="Direction.Negative"/>). /// <para/> /// Seznam obsahuje pouze ty prvky, které jsou viditelné, které mají Enabled = true a které mají TabStop = true, vše podle požadavků v parametru <paramref name="requirements"/>. /// Seznam může volitelně obsahovat i explicitně zadaný Child prvek <paramref name="includeItem"/> bez ohledu na jeho Visible a TabStop (tedy pokud tento prvek je obsažen v seznamu Childs). /// Tato vlastnost slouží k zařazení určitého prvku pro hledání jeho Next prvků. /// <para/> /// Seznam obsahuje prvky bez ohledu na jejich vlastnost KeyboardInput, protože tuto vlastnost mohou mít až jejich vnořené Child prvky, a my chceme najít i nadřízené Containery. /// <para/> /// Pokud je na vstupu objekt <paramref name="parentItem"/>= null, nebo jeho <see cref="IInteractiveParent.Childs"/> je null, pak výstupem metody je null (nebudeme generovat new empty List). /// Dále pak proto, že vstupní objekt i jeho Childs připouštíme, že smí být null, ale nechceme nutit volajícího aby si to sám testoval, /// protože získání Childs může být výkonnostně náročnější = získáme to jen jednou a vyhodnotíme zde, volající ať si otestuje vrácený objekt, to je nenáročné. /// </summary> /// <param name="parentItem">Parent, jehož <see cref="IInteractiveParent.Childs"/> prvky budeme zpracovávat</param> /// <param name="direction">Směr třídění: <see cref="Direction.Positive"/> = podle TabOrder vzestupně; <see cref="Direction.Negative"/> = podle TabOrder sestupně</param> /// <param name="requirements">Požadavky na prohledávané prvky</param> /// <param name="includeItem">Prvek, který chceme do seznamu přidat bezpodmínečně (pokud v <see cref="IInteractiveParent.Childs"/> bude přítomen)</param> /// <returns></returns> private static List <IInteractiveItem> GetChildsSorted(IInteractiveParent parentItem, Direction direction, InteractiveFocusStateFlag requirements = InteractiveFocusStateFlag.Default, IInteractiveItem includeItem = null) { if (parentItem == null) { return(null); } var childs = parentItem.Childs; if (childs == null) { return(null); } List <IInteractiveItem> childList = childs.Where(i => IsItemValid(i, requirements, includeItem)).ToList(); if (childList.Count > 1) { switch (direction) { case Direction.Positive: childList.Sort(InteractiveObject.CompareByTabOrderAsc); break; case Direction.Negative: childList.Sort(InteractiveObject.CompareByTabOrderDesc); break; } } return(childList); }
/// <summary> /// Prověří, že daný prvek není null. Pokud je null, vyhodí chybu. /// </summary> /// <param name="item"></param> /// <param name="action"></param> protected static void CheckItem(IInteractiveParent item, string action) { if (item == null) { throw new Application.GraphLibCodeException("Nelze provést akci BoundsInfo." + action + "(), dodaný prvek je null."); } }
/// <summary> /// Metoda vrací relativní souřadnici bodu v daném containeru pro danou absolutní souřadnici. /// Metoda určí souřadný systém <see cref="BoundsInfo"/> uvnitř daného containeru, /// získá jeho <see cref="BoundsInfo.AbsolutePhysicalOriginPoint"/>, a vrátí rozdíl. /// </summary> /// <param name="container"></param> /// <param name="absolutePoint"></param> /// <returns></returns> public static Point GetRelativePointInContainer(IInteractiveParent container, Point absolutePoint) { if (container == null) { return(absolutePoint); } BoundsInfo boundsInfo = BoundsInfo.CreateForContainer(container); return(absolutePoint.Sub(boundsInfo.AbsolutePhysicalOriginPoint)); }
/// <summary> /// Metoda vrátí absolutní souřadnice prostoru, který je zadán jako relativní souřadnice v daném containeru. /// Pokud tedy například daný container je umístěn na (absolutní) souřadnici Bounds = { 100,20,200,50 }, a dané relativní souřadnice jsou { 5,5,10,10 }, /// pak výsledné absolutní souřadnice jsou { 105,25,10,10 }. /// </summary> /// <param name="container"></param> /// <param name="relativeBounds"></param> /// <param name="currentLayer">Vrstva, jejíž souřadnice řešíme. Každý prvek může mít souřadnice různé podle toho, o kterou vrstvu se jedná. /// To je důsledek procesu Drag and Drop, kdy ve standardní vrstvě se prvek nachází na výchozích souřadnicích Bounds, /// ale ve vrstvě <see cref="GInteractiveDrawLayer.Interactive"/> je na souřadnicích Drag.</param> /// <returns></returns> public static Rectangle GetAbsoluteBoundsInContainer(IInteractiveParent container, Rectangle relativeBounds, GInteractiveDrawLayer currentLayer) { if (container == null) { return(relativeBounds); } BoundsInfo boundsInfo = BoundsInfo.CreateForContainer(container, currentLayer); return(relativeBounds.Add(boundsInfo.AbsolutePhysicalOriginPoint)); }
/// <summary> /// Vrátí instanci třídy <see cref="BoundsInfo"/> pro daný IInteractiveParent parent. Ten může být i <see cref="IAutoScrollContainer"/>! /// </summary> /// <param name="parent"></param> /// <param name="layer">Vrstva grafiky. Ovlivňuje výběr Bounds z dodaného prvku: pro vrstvu <see cref="GInteractiveDrawLayer.Interactive"/> /// akceptuje souřadnice <see cref="IInteractiveItem.BoundsInteractive"/>, jinak bere standardně <see cref="IInteractiveItem.Bounds"/>.</param> /// <returns></returns> public static BoundsInfo CreateForParent(IInteractiveParent parent, GInteractiveDrawLayer layer = GInteractiveDrawLayer.Standard) { Coordinates physicalCoordinates = Coordinates.FromSize(parent.ClientSize); Coordinates virtualCoordinates = physicalCoordinates; if (parent is IAutoScrollContainer) { IAutoScrollContainer autoScrollContainer = parent as IAutoScrollContainer; if (autoScrollContainer.AutoScrollActive) { Point virtualOrigin = autoScrollContainer.VirtualOrigin; Rectangle virtualBounds = physicalCoordinates.GetVisibleBounds(autoScrollContainer.VirtualBounds); virtualCoordinates = Coordinates.FromOrigin(virtualOrigin, virtualBounds); } } return(new BoundsInfo(physicalCoordinates, virtualCoordinates, true, true, layer)); }
/// <summary> /// Vrací <see cref="BoundsInfo"/> pro daného parenta a danou vrstvu souřadnic /// </summary> /// <param name="forItem"></param> /// <param name="asContainer"></param> /// <param name="layer">Vrstva, jejíž souřadnice řešíme. Každý prvek může mít souřadnice různé podle toho, o kterou vrstvu se jedná. /// To je důsledek procesu Drag and Drop, kdy ve standardní vrstvě se prvek nachází na výchozích souřadnicích Bounds, /// ale ve vrstvě <see cref="GInteractiveDrawLayer.Interactive"/> je na souřadnicích Drag = <see cref="IInteractiveItem.BoundsInteractive"/>.</param> /// <returns></returns> private static BoundsInfo _CreateForItem(IInteractiveParent forItem, bool asContainer, GInteractiveDrawLayer layer) { // Nejprve si nastřádám řadu prvků počínaje daným prvkem/nebo jeho parentem, přes jeho Parenty, až k nejspodnějšímu prvku (který nemá parenta): List <IInteractiveParent> parents = new List <IInteractiveParent>(); Dictionary <uint, object> scanned = new Dictionary <uint, object>(); IInteractiveParent parent = (asContainer ? forItem : forItem.Parent); while (parent != null) { if (scanned.ContainsKey(parent.Id)) { break; // Zacyklení! } parents.Add(parent); parent = parent.Parent; // Krok na dalšího parenta } // Pak vytvořím postupně řadu BoundsInfo, počínaje od posledního v poli parents (=fyzický Control): int last = parents.Count - 1; // Pokud na vstupu byl předán container = fyzický Control, pak pole má 0 prvků a výstupní BoundsInfo je vytvořeno pro daný Control! if (last < 0) { return(BoundsInfo.CreateForParent(forItem)); } // Poslední prvek pole = parents[last] = fyzický Control: BoundsInfo boundsInfo = BoundsInfo.CreateForParent(parents[last], layer); for (int i = last - 1; i >= 0; i--) { IInteractiveItem it = parents[i] as IInteractiveItem; if (it == null) { continue; } boundsInfo = boundsInfo.GetChildsBoundsInfo(it, layer); } // Na závěr do výsledného boundsInfo vepíšu dodaný Child prvek (forItem) jako Current prvek: if (!asContainer && forItem is IInteractiveItem) { boundsInfo.CurrentItem = forItem as IInteractiveItem; } return(boundsInfo); }
/// <summary> /// Metoda zkusí najít první konkrétní prvek v rámci daného interaktivního parenta, do kterého lze umístit klávesový Focus. /// Pokud dodaný parent sám je interaktivní prvek <see cref="IInteractiveItem"/>, a pokud vyhovuje podmínkám Visible a TabStop, a podporuje KeyboardInput, pak je vrácen přímo tento prvek. /// Pokud není, nebo pokud nevyhovuje, pak se prověří jeho vlastní Childs prvky, a rekurzivně i jejich vnořené Childs prvky, a vyhledá se první vyhovující prvek. /// Pokud takový neexistuje, je vráceno false. /// </summary> /// <param name="parent">Vstupující prvek. On sám může být tím, kdo dostane focus, anebo některý z jeho Childs prvků (rekurzivně). /// Vstupující prvek může být celý Control (=Host), nebo jakýkoli container nebo konkrétní prvek.</param> /// <param name="direction">Směr hledání: <see cref="Direction.Positive"/>: hledá první vhodný prvek (od začátku); <see cref="Direction.Negative"/>: hledá poslední vhodný prvek (od konce)</param> /// <param name="foundItem">Out: nalezený prvek (this nebo některý z this.Childs[.Childs[...]])</param> /// <param name="requirements">Požadavky na prohledávané prvky</param> /// <returns>true = nalezeno / false = nenalezeno</returns> internal static bool TryGetOuterFocusItem(IInteractiveParent parent, Direction direction, out IInteractiveItem foundItem, InteractiveFocusStateFlag requirements = InteractiveFocusStateFlag.Default) { foundItem = null; if (parent == null) { return(false); // Null nebrat } // Pokud sám daný prvek je interaktivní, a pokud je vyhovující, pak jej akceptujeme: if (parent is IInteractiveItem) { IInteractiveItem item = parent as IInteractiveItem; if (IsItemValid(item, requirements) && item.Is.KeyboardInput) { foundItem = item; return(true); } } // Získáme seznam Childs prvky daného prvku, ve správném pořadí, a najdeme první vhodný prvek = cyklem (skrz Childs) a rekurzí (do téže metody): List <IInteractiveItem> childList = GetChildsSorted(parent, direction, requirements, null); if (childList == null) { return(false); } foreach (IInteractiveItem childItem in childList) { if (TryGetOuterFocusItem(childItem, direction, out foundItem, requirements)) { return(true); // childItem je IInteractiveItem, a protože IInteractiveItem je potomkem IInteractiveParent, vyvoláme přímou rekurzi... } } return(false); }
/// <summary> /// Pro daný prvek určí a vrátí jeho vlastní souřadný systém, který poskytuje svým Childs. /// Jde tedy o systém, do jehož <see cref="CurrentItem"/> lze vložit kterýkoli jeho Childs, a systém bude vracet jeho souřadnice. /// Vrácený prvek má property <see cref="BoundsInfo.CurrentItem"/> neobsazenou. /// </summary> /// <param name="currentContainer"></param> /// <param name="layer">Vrstva, jejíž souřadnice řešíme. Každý prvek může mít souřadnice různé podle toho, o kterou vrstvu se jedná. /// To je důsledek procesu Drag and Drop, kdy ve standardní vrstvě se prvek nachází na výchozích souřadnicích Bounds, /// ale ve vrstvě <see cref="GInteractiveDrawLayer.Interactive"/> je na souřadnicích Drag.</param> /// <returns></returns> public static BoundsInfo CreateForContainer(IInteractiveParent currentContainer, GInteractiveDrawLayer layer) { CheckItem(currentContainer, "CreateForContainer"); return(_CreateForItem(currentContainer, true, layer)); }
/// <summary> /// Konstruktor s parentem /// </summary> /// <param name="parent"></param> public GridArray(IInteractiveParent parent) : this() { this.Parent = parent; }
/// <summary> /// Konstruktor /// </summary> /// <param name="parent"></param> public TrackBar(IInteractiveParent parent) : this() { this.Parent = parent; }
/// <summary> /// Konstruktor s parentem /// </summary> /// <param name="parent"></param> public SizeAxis(IInteractiveParent parent) : this() { this.Parent = parent; }