/// <summary>Gets the renderable data at the given point (may be null).</summary> public RenderableData Get(float x, float y) { InputGridEntry ige = Front; while (ige != null) { // Get the render data: RenderableData renderData = ige.RenderData; // Get the zone: ScreenRegion screenBox = renderData.OnScreenRegion; // Is this node visible and is the point within it? if (screenBox != null && screenBox.Contains(x, y)) { // At this point, the mouse could still be outside it. // This happens with inline elements - their clipping boundary can contain multiple sub-boxes. // So, time to check how many boxes it has, then the individual boxes if we've got more than one. LayoutBox box = renderData.FirstBox; if (box != null && box.NextInElement != null) { // Multiple boxes. Must be contained in one of them to win. while (box != null) { if (box.Contains(x, y)) { // Ok! return(renderData); } // Advance to the next one: box = box.NextInElement; } } else { // Yep! return(renderData); } } ige = ige.Previous; } return(null); }
/// <summary>Gets a grid entry for the given private InputGridEntry GetEntry() { InputGridEntry ige = PooledCell_; if (ige == null) { return(new InputGridEntry()); } // Update pool: PooledCell_ = ige.Previous; ige.Previous = null; return(ige); }
/// <summary>Empties the grid into the pool.</summary> public void Empty() { if (Grid == null) { return; } // For each grid cell.. for (int i = 0; i < Grid.Length; i++) { InputGridCell cell = Grid[i]; if (cell.Back != null) { // Pool: cell.Back.Previous = PooledCell_; PooledCell_ = cell.Front; // Clear: cell.Back = null; cell.Front = null; } } }
/// <summary>Pushes the given renderable data into the grid now.</summary> public void Push(RenderableData renderData) { // Node must be an element: if (renderData == null || !(renderData.Node is Dom.Element)) { return; } // Get the region: ScreenRegion region = renderData.OnScreenRegion; if (region == null) { return; } // Get the bounds: int minX = (int)(region.ScreenMinX / CellSize); int minY = (int)(region.ScreenMinY / CellSize); if (minX >= Width || minY >= Height) { // ..It's offscreen? return; } if (minX < 0) { minX = 0; } if (minY < 0) { minY = 0; } // Max (inclusive): int maxX = (int)(region.ScreenMaxX / CellSize); int maxY = (int)(region.ScreenMaxY / CellSize); if (maxX < 0 || maxY < 0) { // ..It's offscreen? return; } if (maxX >= Width) { maxX = Width - 1; } if (maxY >= Height) { maxY = Height - 1; } // Generating a bunch of entries now! int cellIndex = minY * Width; // Only having one box means we don't need to do multiline testing. // It also means we can optimise "central" boxes too (see below about that). bool oneBox = (renderData.FirstBox != null && renderData.FirstBox.NextInElement == null); for (int y = minY; y <= maxY; y++) { // Central boxes are on *both* middle X and middle Y // The content in the cell already is being completely overlapped // so we can simply empty it out (and recycle the entries too). bool centralBox = (oneBox && y != minY && y != maxY); for (int x = minX; x <= maxX; x++) { // Get the target cell: InputGridCell cell = Grid[cellIndex + x]; if (centralBox && x != minX && x != maxX) { // Overwrite! Pool the cell: if (cell.Back != null) { // Pool: cell.Back.Previous = PooledCell_; PooledCell_ = cell.Front; // Clear: cell.Back = null; cell.Front = null; } } // Get an entry (typically from the pool): InputGridEntry ige = GetEntry(); // Set it up: ige.RenderData = renderData; if (cell.Front == null) { cell.Back = ige; cell.Front = ige; } else { // Stack on the front: ige.Previous = cell.Front; cell.Front = ige; } } cellIndex += Width; } }