/// <summary> /// Vrátí absolutní souřadnice (AbsoluteBounds) viditelné části pro daný Child prvek. /// Souřadný systém prvku volí podle toho, zda prvek je umístěn ve fyzických nebo ve virtuálních souřadnicích. /// </summary> /// <param name="item">Prvek</param> /// <param name="layer">Vrstva, ovlivní výběr typu souřadnic</param> /// <returns></returns> protected Rectangle GetAbsoluteVisibleBounds(IInteractiveItem item, GInteractiveDrawLayer layer) { Coordinates parentCoordinates = (item.Is.OnPhysicalBounds ? this._PhysicalCoordinates : this._VirtualCoordinates); Rectangle bounds = GetItemBounds(item, layer); return(parentCoordinates.GetVisibleBounds(parentCoordinates.GetAbsoluteBounds(bounds))); }
/// <summary> /// Vrátí danou barvu, s modifikovanou průhledností Opacity (<see cref="Color.A"/>). /// Zadaná Opacity je respektována, ale pokud celý graf má deklarovaou svoji průhlednost v <see cref="TimeGraph.GraphOpacity"/>, pak je akceptována rovněž. /// A dále, pokud se aktuálně kreslí do vrstvy <see cref="GInteractiveDrawLayer.Interactive"/>, pak je akceptována i hodnota <see cref="DragDropDrawInteractiveOpacity"/>. /// </summary> /// <param name="baseColor">Výchozí barva, typicky BackColor nebo ForeColor prvku grafu.</param> /// <param name="drawLayer">Vykreslovaná vrstva grafiky</param> /// <param name="drawMode">Režim kreslení předaný systémem</param> /// <param name="forGroup">true = pro barvu grupy / false = pro barvu prvku</param> /// <param name="forBackColor">true = pro barvu pozadí / false = pro barvu okrajů, čar a textů</param> /// <returns></returns> public Color?GetColorWithOpacity(Color?baseColor, GInteractiveDrawLayer drawLayer, DrawItemMode drawMode, bool forGroup, bool forBackColor) { if (!baseColor.HasValue) { return(null); } return(this.GetColorWithOpacity(baseColor.Value, drawLayer, drawMode, forGroup, forBackColor)); }
/// <summary> /// Vrací souřadnice daného prvku <paramref name="item"/> pro danou vrstvu <paramref name="layer"/>. /// Pro vrstvu <see cref="GInteractiveDrawLayer.Interactive"/> vrací interaktivní souřadnice <see cref="IInteractiveItem.BoundsInteractive"/>, pokud jsou zadány. /// Standardně vrací <see cref="IInteractiveItem.Bounds"/>. /// </summary> /// <param name="item"></param> /// <param name="layer"></param> /// <returns></returns> protected Rectangle GetItemBounds(IInteractiveItem item, GInteractiveDrawLayer layer) { if (item == null) { return(Rectangle.Empty); } if (layer == GInteractiveDrawLayer.Interactive && item.BoundsInteractive.HasValue) { return(item.BoundsInteractive.Value); } return(item.Bounds); }
/// <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> /// Vrátí danou barvu, s modifikovanou průhledností Opacity (<see cref="Color.A"/>). /// Zadaná Opacity je respektována, ale pokud celý graf má deklarovaou svoji průhlednost v <see cref="TimeGraph.GraphOpacity"/>, pak je akceptována rovněž. /// A dále, pokud se aktuálně kreslí do vrstvy <see cref="GInteractiveDrawLayer.Interactive"/>, pak je akceptována i hodnota <see cref="DragDropDrawInteractiveOpacity"/>. /// </summary> /// <param name="baseColor">Výchozí barva, typicky BackColor nebo ForeColor prvku grafu.</param> /// <param name="drawLayer">Vykreslovaná vrstva grafiky</param> /// <param name="drawMode">Režim kreslení předaný systémem</param> /// <param name="forGroup">true = pro barvu grupy / false = pro barvu prvku</param> /// <param name="forBackColor">true = pro barvu pozadí / false = pro barvu okrajů, čar a textů</param> /// <returns></returns> public Color GetColorWithOpacity(Color baseColor, GInteractiveDrawLayer drawLayer, DrawItemMode drawMode, bool forGroup, bool forBackColor) { if (!this.IsDragged) { // Běžný stav prvku, kdy tento pvek není přetahován jinam: return(this._GetColorWithOpacityStandard(baseColor, forGroup, forBackColor, null)); } else { // Drag and Drop tohoto prvku, kdy prvek je přemisťován jinam: if (drawLayer == GInteractiveDrawLayer.Standard) { return(this._GetColorWithOpacityDragOriginal(baseColor, forGroup, forBackColor)); } else { return(this._GetColorWithOpacityDragTarget(baseColor, forGroup, forBackColor)); } } }
/// <summary> /// Vrátí novou instanci <see cref="BoundsInfo"/> pro daný Child objekt, který od té chvíle bude Parent objektem pro další, v něm vnořené prvky. /// Respektuje přitom, že daný Child objekt se může pohybovat ve virtuálních anebo ve fyzických souřadnicích aktuálního systému. /// </summary> /// <param name="item">Prvek</param> /// <param name="layer">Vrstva, ovlivní výběr typu souřadnic</param> /// <returns></returns> protected BoundsInfo GetChildsBoundsInfo(IInteractiveItem item, GInteractiveDrawLayer layer) { // Child prvek je umístěn ve svých souřadnicích Bounds, a to buď ve fyzickém nebo ve virtuálním prostoru (koordinátech): Coordinates parentCoordinates = (item.Is.OnPhysicalBounds ? this._PhysicalCoordinates : this._VirtualCoordinates); // Určíme jeho absolutní souřadnice = jeho Bounds posunuté do odpovídajících (fyzické/virtuální) koordinátů: Rectangle bounds = GetItemBounds(item, layer); Rectangle absoluteBounds = parentCoordinates.GetAbsoluteBounds(bounds); // Prvek může zmenšit tento prostor o své vnitřní okraje. // Prostor okrajů patří do prvku (=prvek sám si je dokáže vykreslit), ale nepatří do prostoru, který prvek poskytuje svým child prvkům: // Toto jsou tedy absolutní souřadnice prostoru, ve kterém budou zobrazovány Child prvky: Rectangle clientBounds = absoluteBounds.Sub(item.ClientBorder); // Určíme viditelnou oblast (průsečík z prostoru pro childs s prostorem dosud viditelné oblasti): Rectangle visibleBounds = parentCoordinates.GetVisibleBounds(clientBounds); // V rámci těchto souřadnic prvek item může poskytovat svůj souřadný systém standardní (fyzický) anebo i virtuální: Coordinates physicalCoordinates = Coordinates.FromOrigin(clientBounds.Location, visibleBounds); Coordinates virtualCoordinates = physicalCoordinates; if (item is IAutoScrollContainer) { // Child prvek (item) je AutoScrollContainer: IAutoScrollContainer autoScrollContainer = item as IAutoScrollContainer; if (autoScrollContainer.AutoScrollActive) { Point virtualOrigin = parentCoordinates.GetAbsolutePoint(autoScrollContainer.VirtualOrigin); Rectangle virtualBounds = parentCoordinates.GetVisibleBounds(parentCoordinates.GetAbsoluteBounds(autoScrollContainer.VirtualBounds)); virtualCoordinates = Coordinates.FromOrigin(virtualOrigin, virtualBounds); } } bool isVisible = this.IsVisible && item.Is.Visible; bool isEnabled = this.IsEnabled && item.Is.Enabled; return(new BoundsInfo(physicalCoordinates, virtualCoordinates, isVisible, isEnabled, layer)); }
/// <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> /// 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> /// 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> /// Privátní konstruktor pro dané koordináty /// </summary> /// <param name="physicalCoordinates"></param> /// <param name="virtualCoordinates"></param> /// <param name="isVisible"></param> /// <param name="isEnabled"></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> private BoundsInfo(Coordinates physicalCoordinates, Coordinates virtualCoordinates, bool isVisible, bool isEnabled, GInteractiveDrawLayer layer) { this._PhysicalCoordinates = physicalCoordinates; this._VirtualCoordinates = virtualCoordinates; this._IsVisible = isVisible; this._IsEnabled = isEnabled; this._CurrentLayer = layer; }
/* * 1. ÚČEL * POZICE * Kreslící systém ve třídách ControlBuffered a ControlLayered (a následně interaktivní control InteractiveControl) * je postaven na vykreslování jednotlivých prvků (child itemů) do globální grafiky celého WinForm Controlu. * K tomu vykreslování je třeba znát absolutní pozici konkrétního child itemu vzhledem k WinForm Controlu. * Přitom samozřejmě pozicování child itemů (=jejich Bounds) je relativní výhradně k jejich Parentu, * kterým je jiný child item (=standardní vnořená hierarchie). * Jinými slovy, pokud přemístím určitý item na jiné (relativní) souřadnice, pak nemusím měnit souřadnice jeho child itemů. * A tyto child itemy se budou nacházet na stejné relativní souřadnici Bounds, ale fyzicky jsou na jiné absolutní souřadnici v rámci WinForm Controlu. * (To samé, co platí o vykreslování, je platné i pro interaktivitu podmíněnou akcemi myši.) * * Účelem třídy BoundsInfo je tedy vypočítat absolutní souřadnice konkrétního child itemu vzhledem k fyzickému WinForm Controlu. * * VIDITELNOST * Dalším úkolem je určit, jaká část itemu je fyzicky viditelná. * Pokud určitý child item leží částečně mimo zobrazovanou oblast WinForm Controlu, anebo mimo souřadnice Bounds svého parent itemu, * pak child item může být částečně nebo zcela neviditelný. * To má vliv na vykreslování (nebudeme vykreslovat item, který je zcela mimo viditelnou oblast), * i na interaktivitu (prvek item nemůže zachytávat akce myši v oblasti, kde není viditelný třeba proto, že leží mimo prostor svého parenta). * * 2. DALŠÍ FUNKCIONALITA = AutoScroll prvky * Systém prvků dovoluje implementovat AutoScroll = postup, který detekuje rozmístění child itemů, určuje tak potřebný prostor, * porovnává jej s disponibilním prostorem v parent prvku (ať je to WinForm Control nebo běžný parent item); * a pokud je disponibilní prostor menší než je třeba, pak aktivuje AutoScroll režim (=zobrazí se ScrollBary). * Tím se stává prostor hostitelského prvku "virtuálním" = jeho souřadnice se posouvají vlivem scrollování. * I tuto věc řeší třída BoundsInfo. * * 3. POSTUP ŘEŠENÍ - pro směr Parent => Child * a) Vstupem do souřadného systému je WinForm Control * b) Souřadný systém zajišťuje přepočet relativní souřadnice určitého child itemu (jeho Bounds) do absolutní pozice v rámci WinForm Controlu * (fyzicky jde o pozici bodu počátku, který se přičte k souřadnici počátku child itemu Bounds.Location, * a výsledkem je fyzická souřadnice na Controlu) * c) Kvůli AutoScrollu máme dva souřadné systémy: Fyzický souřadný systém (FSS) a Virtuální (VSS) * d) Většina child itemů (běžné) má souřadnice vztažené k VSS, a tedy při AutoScrollu se pohybují ve svém Parentu * e) Některé child itemy (ScrollBary od AutoScrollu) mají svoje souřadnice vztažené k FSS, při provádění AutoScrollu se ve svém parentu nepohybují * f) Child itemy si tedy pro určení sých Absolute Bounds vyberou "svůj" souřadný systém, jeho bod počátku a podle něj určí svoje absolutní souřadnice; * a podle něj určí i své AbsoluteVisibleBounds * f) Při vytváření BoundsInfo pro vnořené child itemy se bude vycházet z aktuálních AbsoluteBounds prvku (containeru), * * * * * * * * * * * * * * * * * * * */ #endregion #region Metody pro směr Parent to Child #region Konstruktory /// <summary> /// Vrátí instanci třídy <see cref="BoundsInfo"/> pro daný WinForm Control. Ten může být i <see cref="IAutoScrollContainer"/>! /// </summary> /// <param name="control"></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 CreateForControl(System.Windows.Forms.Control control, GInteractiveDrawLayer layer = GInteractiveDrawLayer.Standard) { Coordinates physicalCoordinates = Coordinates.FromSize(control.ClientSize); Coordinates virtualCoordinates = physicalCoordinates; if (control is IAutoScrollContainer) { IAutoScrollContainer autoScrollContainer = control 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)); }