private TControl FindRemovedWithData <TControl, TData> (ref DenseList <Control> list, string key, TData data) where TControl : Control { // Look for a match in the controls we have previously removed for (int i = 0, c = list.Count; i < c; i++) { var tctl = EvaluateMatch <TControl, TData>(list[i], key, data); if (tctl == null) { continue; } list.RemoveAt(i); return(tctl); } return(null); }
internal ContainerBuilder(UIContext context, Control control) { if (control == null) { throw new ArgumentNullException("control"); } if (!(control is IControlContainer)) { throw new InvalidCastException("control must implement IControlContainer"); } Context = context; Control = control; Container = (IControlContainer)control; NextIndex = Container?.ChildrenToSkipWhenBuilding ?? 0; Children = Container.Children; PreviousRemovedControls = new DenseList <Control>(); CurrentRemovedControls = new DenseList <Control>(); ExtraLayoutFlags = default(ControlFlags); OverrideLayoutFlags = null; WaitingForFocusBeneficiary = null; }
public void ReplaceWith(ref DenseList <Control> newControls) { for (int i = 0; i < newControls.Count; i++) { var newControl = newControls[i]; if (newControl == null) { throw new ArgumentNullException($"newControls[{i}]"); } } for (int i = 0; i < newControls.Count; i++) { var newControl = newControls[i]; if (newControl == null) { throw new ArgumentNullException($"newControls[{i}]"); } var isNew = !IndexTable.TryGetValue(newControl, out int oldIndex); if (!isNew) { if (oldIndex == i) { continue; } // HACK: Erase the slot that previously held this control if (oldIndex >= 0) { Items[oldIndex] = null; } } if (i < Count) { var oldControl = Items[i]; // HACK: Flag this control as potentially dead if (oldControl != null) { IndexTable[oldControl] = -1; } Items[i] = newControl; } else { Items.Add(newControl); } IndexTable[newControl] = i; if (isNew) { newControl.SetParent(WeakHost); } } // Find dead controls that we removed and never re-added foreach (var kvp in IndexTable) { if (kvp.Value <= -1) { DeadControlScratchBuffer.Add(kvp.Key); } } while (Count > newControls.Count) { var i = Count - 1; var extraControl = Items[i]; // We may have moved this extra control, in which case we don't want to go through // the remove path that fires events. if ((extraControl != null) && (IndexTable[extraControl] == i)) { DeadControlScratchBuffer.Add(extraControl); } // We moved it, so we just need to remove whatever's at this location Items.RemoveAt(i); } #if DEBUG foreach (var item in Items) { if (item == null) { throw new Exception("Bad state"); } } #endif // Fire the relevant notifications for the dead controls and purge them from the table foreach (var deadControl in DeadControlScratchBuffer) { IndexTable.Remove(deadControl); FireControlRemoveEvents(deadControl); } #if DEBUG foreach (var kvp in IndexTable) { if (kvp.Value <= -1) { throw new Exception("Bad state"); } } #endif Invalidate(); DeadControlScratchBuffer.Clear(); }
private void RasterizeAcceleratorOverlay( UIOperationContext context, ref ImperativeRenderer labelRenderer, ref ImperativeRenderer targetRenderer, Control control, AbstractString label, bool showFocused = false, Control forControl = null ) { if (control == null) { return; } if (!showFocused && (control == Focused) && !label.IsNull && (label.Length > 0)) { return; } if (label.Length <= 0) { return; } var box = control.GetRect(); if ((box.Width <= 1) || (box.Height <= 1)) { return; } var decorator = Decorations.AcceleratorTarget; var settings = new Decorations.DecorationSettings { Box = box, ContentBox = box }; decorator.Rasterize(ref context, ref targetRenderer, settings); var outlinePadding = 1f; decorator = Decorations.AcceleratorLabel; Color?textColor = null; decorator.GetTextSettings(ref context, default(ControlStates), out Material material, ref textColor, out _); var layout = decorator.GlyphSource.LayoutString(label, buffer: AcceleratorOverlayBuffer); var textScale = 1f; if (layout.Size.X > (box.Width - decorator.Padding.X)) { textScale = Math.Max(0.25f, (box.Width - decorator.Padding.X) / layout.Size.X); } var scaledSize = layout.Size * textScale; var labelTraits = new DenseList <string> { "above" }; var labelPosition = box.Position - new Vector2(0, scaledSize.Y + decorator.Padding.Y + outlinePadding); if (labelPosition.Y <= 0) { labelTraits[0] = "inside"; labelPosition = box.Position; } labelPosition.X = Arithmetic.Clamp(labelPosition.X, 0, CanvasSize.X - scaledSize.X); labelPosition.Y = Math.Max(0, labelPosition.Y); var labelBox = new RectF( labelPosition, scaledSize + decorator.Padding.Size ); if (IsObstructedByAnyPreviousBox(ref labelBox, forControl)) { labelBox.Left = box.Extent.X - labelBox.Width; } if (IsObstructedByAnyPreviousBox(ref labelBox, forControl)) { labelTraits[0] = "below"; labelBox.Left = labelPosition.X; labelBox.Top = box.Extent.Y + 1; // FIXME: Why the +1? } while (IsObstructedByAnyPreviousBox(ref labelBox, forControl)) { labelTraits[0] = "stacked"; labelBox.Left = box.Left; labelBox.Width = box.Width; labelBox.Top = labelBox.Extent.Y + 0.5f; } // HACK var labelContentBox = new RectF( labelBox.Position + new Vector2(decorator.Padding.Left, decorator.Padding.Top), scaledSize ); settings = new Decorations.DecorationSettings { Box = labelBox, ContentBox = box, Traits = labelTraits }; decorator.Rasterize(ref context, ref labelRenderer, settings); labelRenderer.DrawMultiple(layout.DrawCalls, offset: labelContentBox.Position.Floor(), scale: new Vector2(textScale), layer: 1); RasterizedOverlayBoxes.Add(new RasterizedOverlayBox { Control = forControl, ControlBox = box, LabelBox = labelBox }); }