internal override AddRemoveLayoutResult RemoveItem(object changedItem, object addRemoveItem, int addRemoveItemIndex) { int removedItemsCount = 1; int removedLinesCount = 0; bool isVisible = true; GroupInfo changedGroupInfo = this.GetGroupInfo(changedItem); bool changedGroupIsRoot = changedGroupInfo == null; int flatSlot = -1; int rowSlot = -1; int level = changedGroupIsRoot ? 0 : changedGroupInfo.Level + 1; IGroup removedGroup = addRemoveItem as IGroup; // We added/removed group so we need to index all subgroups. if (removedGroup != null && changedItem != addRemoveItem) { GroupInfo groupInfo = this.GetGroupInfo(addRemoveItem); System.Diagnostics.Debug.Assert(groupInfo != null, "Cannot remove group that are not indexed."); flatSlot = groupInfo.Index; isVisible = groupInfo.IsVisible(); removedItemsCount = groupInfo.GetLineSpan(); removedLinesCount = removedItemsCount; if (!removedGroup.HasItems) { foreach (var nextGroupIndex in this.GroupHeadersTable.GetIndexes(flatSlot + 1)) { GroupInfo nextGroupInfo = this.GroupHeadersTable.GetValueAt(nextGroupIndex); if (nextGroupInfo.Level <= groupInfo.Level) { break; } this.RemoveGroupInfo(nextGroupInfo); } } this.UpdateGroupHeadersTable(groupInfo.Index, -removedItemsCount); this.itemInfoTable.Remove(removedGroup); this.GroupHeadersTable.RemoveIndexesAndValues(flatSlot, removedItemsCount); if (isVisible) { if (groupInfo.IsExpanded) { int collapsedItems = removedItemsCount - this.GetCollapsedSlotsCount(flatSlot, flatSlot + removedItemsCount - 1); this.VisibleLineCount -= collapsedItems; } else { this.VisibleLineCount -= 1; } } this.collapsedSlotsTable.RemoveIndexesAndValues(flatSlot, removedItemsCount); if (changedGroupInfo != null) { // Update the group last slot. changedGroupInfo.LastSubItemSlot -= removedItemsCount; } } else { // We added new item (not group) into root group. if (changedGroupIsRoot) { if (isVisible) { removedLinesCount = this.GetRemovedSlots(this.ItemsSource.Count, removedItemsCount); this.VisibleLineCount -= removedLinesCount; flatSlot = addRemoveItemIndex; } // slot should be already correct. // No need to correct collapsed slots. They should be empty (e.g. no groups to collapse). System.Diagnostics.Debug.Assert(this.GroupHeadersTable.IsEmpty, "GroupHeaders table should be empty."); System.Diagnostics.Debug.Assert(this.collapsedSlotsTable.IsEmpty, "CollapsedSlots table should be empty since we don't have groups."); } else { var children = (changedGroupInfo.Item as IGroup).Items.Count; // Be carefull that this represents lines/slots rather than items. Also consider if collapsed table should use this removedLinesCount = this.GetRemovedSlots(children, removedItemsCount); flatSlot = changedGroupInfo.Index + 1 + addRemoveItemIndex; // Update the group last slot. changedGroupInfo.LastSubItemSlot -= removedItemsCount; this.GroupHeadersTable.RemoveIndex(flatSlot); isVisible = changedGroupInfo.IsExpanded && changedGroupInfo.IsVisible(); if (isVisible) { this.collapsedSlotsTable.RemoveIndexes(flatSlot, removedLinesCount); this.VisibleLineCount -= removedLinesCount; } else { this.collapsedSlotsTable.RemoveIndexesAndValues(flatSlot, removedLinesCount); } this.UpdateGroupHeadersTable(changedGroupInfo.Index, -removedItemsCount); } } rowSlot = this.GetRowSlotFromFlatSlot(flatSlot); // Update parent groups last slot. CompactLayout.UpdateParentGroupInfosLastSlot(-removedItemsCount, changedGroupInfo); // Update TotalCount. this.TotalSlotCount -= removedLinesCount; this.RemoveFromRenderInfo(removedLinesCount, rowSlot); var layoutResult = new AddRemoveLayoutResult(rowSlot, removedLinesCount); foreach (var strategy in this.LayoutStrategies) { strategy.OnItemRemoved(layoutResult); } // return result indexes so that changed can be reflected to UI. return(layoutResult); }
public abstract void OnItemRemoved(AddRemoveLayoutResult layoutResult);
internal override AddRemoveLayoutResult RemoveItem(object changedItem, object addRemoveItem, int addRemoveItemIndex) { int count = 1; bool isVisible = true; GroupInfo changedGroupInfo = this.GetGroupInfo(changedItem); bool changedGroupIsRoot = changedGroupInfo == null; int slot = addRemoveItemIndex; int level = changedGroupIsRoot ? 0 : changedGroupInfo.Level + 1; IGroup removedGroup = addRemoveItem as IGroup; // We added/removed group so we need to index all subgroups. if (removedGroup != null && changedItem != addRemoveItem) { GroupInfo groupInfo = this.GetGroupInfo(addRemoveItem); System.Diagnostics.Debug.Assert(groupInfo != null, "Cannot remove group that are not indexed."); slot = groupInfo.Index; isVisible = groupInfo.IsVisible(); count = groupInfo.GetLineSpan(); if (!removedGroup.HasItems) { foreach (var nextGroupIndex in this.groupHeadersTable.GetIndexes(slot + 1)) { GroupInfo nextGroupInfo = this.groupHeadersTable.GetValueAt(nextGroupIndex); if (nextGroupInfo.Level <= groupInfo.Level) { break; } this.RemoveGroupInfo(nextGroupInfo); } } this.UpdateGroupHeadersTable(groupInfo.Index, -count); this.itemInfoTable.Remove(removedGroup); this.groupHeadersTable.RemoveIndexesAndValues(slot, count); if (isVisible) { if (groupInfo.IsExpanded) { int collapsedItems = count - this.GetCollapsedSlotsCount(slot, slot + count - 1); this.visibleLineCount -= collapsedItems; } else { this.visibleLineCount -= 1; } } this.collapsedSlotsTable.RemoveIndexesAndValues(slot, count); if (changedGroupInfo != null) { // Update the group last slot. changedGroupInfo.LastSubItemSlot -= count; } } else { // We added new item (not group) into root group. if (changedGroupIsRoot) { if (isVisible) { this.visibleLineCount -= count; } // slot should be already correct. // No need to correct collapsed slots. They should be empty (e.g. no groups to collapse). System.Diagnostics.Debug.Assert(this.groupHeadersTable.IsEmpty, "GroupHeaders table should be empty."); System.Diagnostics.Debug.Assert(this.collapsedSlotsTable.IsEmpty, "CollapsedSlots table should be empty since we don't have groups."); } else { // We added new item (not group) into existing bottom level group (not root group). slot = changedGroupInfo.Index + 1 + addRemoveItemIndex; // Update the group last slot. changedGroupInfo.LastSubItemSlot -= count; this.groupHeadersTable.RemoveIndex(slot); isVisible = changedGroupInfo.IsExpanded && changedGroupInfo.IsVisible(); if (isVisible) { this.collapsedSlotsTable.RemoveIndexes(slot, count); this.visibleLineCount -= count; } else { this.collapsedSlotsTable.RemoveIndexesAndValues(slot, count); } this.UpdateGroupHeadersTable(changedGroupInfo.Index, -count); } } // Update parent groups last slot. UpdateParentGroupInfosLastSlot(-count, changedGroupInfo); // Update TotalCount. this.totalCount -= count; // Insert new records into IndexTree. this.RenderInfo.RemoveRange(slot, count); var layoutResult = new AddRemoveLayoutResult(slot, count); foreach (var strategy in this.LayoutStrategies) { strategy.OnItemRemoved(layoutResult); } // return result indexes so that changed can be reflected to UI. return(layoutResult); }
internal override AddRemoveLayoutResult AddItem(object changedItem, object addRemoveItem, int addRemoveItemIndex) { int addedItemsCount = 1; bool isVisible = true; var groupInfo = this.GetGroupInfo(changedItem); bool changedGroupIsRoot = groupInfo == null; int flatSlot = -1; // = addRemoveItemIndex; int rowSlot = -1; int addedLinesCount = 0; int level = changedGroupIsRoot ? 0 : groupInfo.Level + 1; // We added/removed group so we need to index all subgroups. if (addRemoveItem is IGroup && changedItem != addRemoveItem) { flatSlot = this.GetInsertedGroupSlot(changedItem, addRemoveItemIndex); isVisible = groupInfo != null ? (groupInfo.IsExpanded && groupInfo.IsVisible()) : true; // We give a list in which to insert groups so that we can manually correct the indexes in groupHeadersTable. List <GroupInfo> insertedGroups = new List <GroupInfo>(); addedItemsCount = this.CountAndPopulateTables(addRemoveItem, flatSlot, level, this.GroupLevels, groupInfo, false, insertedGroups); addedLinesCount = addedItemsCount; bool first = true; int innerMostSlot = flatSlot; foreach (var newGroupInfo in insertedGroups) { int groupInfoToInsertSpan = newGroupInfo.GetLineSpan(); if (first) { first = false; this.GroupHeadersTable.InsertIndexes(newGroupInfo.Index, groupInfoToInsertSpan); if (isVisible) { this.collapsedSlotsTable.InsertIndexes(flatSlot, addedItemsCount); this.VisibleLineCount += addedItemsCount; } else { this.collapsedSlotsTable.InsertIndexesAndValues(flatSlot, addedItemsCount, false); } } this.GroupHeadersTable.AddValue(newGroupInfo.Index, newGroupInfo); // We get the inner most group so that we update groups after inner most (the upper one are newly created and their index and last slot count are correct. // We get it only if the change is in the root group. In existing groups we know the correct slot. if (changedGroupIsRoot) { innerMostSlot = newGroupInfo.Index; } } // Update groups after current one. this.UpdateGroupHeadersTable(innerMostSlot, addedItemsCount); if (groupInfo != null) { // Update the group last slot. groupInfo.LastSubItemSlot += addedItemsCount; } } else { // We added new item (not group) into root group. if (changedGroupIsRoot) { if (isVisible) { addedLinesCount = this.GetAddedSlots(this.ItemsSource.Count, addedItemsCount); this.VisibleLineCount += addedLinesCount; flatSlot = addRemoveItemIndex; } // slot should be already correct. // No need to correct collapsed slots. They should be empty (e.g. no groups to collapse). System.Diagnostics.Debug.Assert(this.GroupHeadersTable.IsEmpty, "GroupHeaders table should be empty."); System.Diagnostics.Debug.Assert(this.collapsedSlotsTable.IsEmpty, "CollapsedSlots table should be empty since we don't have groups."); } else { // We added new item (not group) into existing bottom level group (not root group). var children = (groupInfo.Item as IGroup).Items.Count; addedLinesCount = this.GetAddedSlots(children, addedItemsCount); flatSlot = groupInfo.Index + 1 + addRemoveItemIndex; // We give a list in which to insert groups so that we can manually correct the indexes in groupHeadersTable. // Update the group last slot. groupInfo.LastSubItemSlot += addedItemsCount; if (addedItemsCount > 0) { this.GroupHeadersTable.InsertIndex(flatSlot); } isVisible = groupInfo.IsExpanded && groupInfo.IsVisible(); if (addedItemsCount > 0) { if (isVisible) { this.collapsedSlotsTable.InsertIndexes(flatSlot, addedItemsCount); this.VisibleLineCount += addedLinesCount; } else { this.collapsedSlotsTable.InsertIndexesAndValues(flatSlot, addedItemsCount, false); } } this.UpdateGroupHeadersTable(groupInfo.Index, addedItemsCount); } } rowSlot = this.GetRowSlotFromFlatSlot(flatSlot); // Update parent groups last slot. CompactLayout.UpdateParentGroupInfosLastSlot(addedItemsCount, groupInfo); // Update TotalCount. this.TotalSlotCount += addedLinesCount; // Update Visible line count if not collapsed. double length = isVisible ? this.averageItemLength : 0; if (addedLinesCount >= 0) { this.InsertToRenderInfo(rowSlot, null, addedLinesCount); } var layoutResult = new AddRemoveLayoutResult(rowSlot, addedLinesCount); foreach (var strategy in this.LayoutStrategies) { strategy.OnItemAdded(layoutResult); } // return result indexes so that changed can be reflected to UI. return(layoutResult); }
internal override AddRemoveLayoutResult RemoveItem(object changedItem, object addRemoveItem, int addRemoveItemIndex) { int totalRemovedLines = 0; int count = 1; bool isVisible = true; GroupInfo changedGroupInfo = this.GetGroupInfo(changedItem); bool changedGroupIsRoot = changedGroupInfo == null; int slot = addRemoveItemIndex; int level = changedGroupIsRoot ? 0 : changedGroupInfo.Level + 1; IGroup removedGroup = addRemoveItem as IGroup; // We added/removed group so we need to index all subgroups. if (removedGroup != null && changedItem != addRemoveItem) { GroupInfo groupInfo = this.GetGroupInfo(addRemoveItem); System.Diagnostics.Debug.Assert(groupInfo != null, "Cannot remove group that are not indexed."); slot = groupInfo.Index; isVisible = groupInfo.IsVisible(); count = groupInfo.GetLineSpan(); // TODO: VVG -- fix this, we should take into account the fact that there is an int number of items in a row totalRemovedLines = (int)Math.Ceiling((this.VisibleLineCount * this.DefaultItemOppositeLength) / this.availableOppositeLength) - (int)Math.Ceiling(((this.VisibleLineCount - count) * this.DefaultItemOppositeLength) / this.availableOppositeLength); if (!removedGroup.HasItems) { foreach (var nextGroupIndex in this.groupHeadersTable.GetIndexes(slot + 1)) { GroupInfo nextGroupInfo = this.groupHeadersTable.GetValueAt(nextGroupIndex); if (nextGroupInfo.Level <= groupInfo.Level) { break; } this.RemoveGroupInfo(nextGroupInfo); } } this.UpdateGroupHeadersTable(groupInfo.Index, -count); this.groupInfoTable.Remove(removedGroup); this.groupHeadersTable.RemoveIndexesAndValues(slot, count); if (isVisible) { if (groupInfo.IsExpanded) { int collapsedItems = count - this.GetCollapsedSlotsCount(slot, slot + count - 1); this.removedItemsLength += this.ColumnSlotsRenderInfo.ValueForIndex(slot); if (this.availableOppositeLength - this.removedItemsLength < this.DefaultItemOppositeLength) { this.removedItemsLength = 0; } } else { // TODO: Deal with this when implementing collapsible groups. // count = 1; } totalRemovedLines = count; } if (changedGroupInfo != null) { // Update the group last slot. changedGroupInfo.LastSubItemSlot -= count; } } else { // We removed an item (not group) from root group. if (changedGroupIsRoot) { if (isVisible) { var columnsCount = Math.Floor(this.availableOppositeLength / this.DefaultItemOppositeLength); totalRemovedLines = (int)Math.Ceiling(this.totalItemsCount / (double)columnsCount) - (int)Math.Ceiling((this.totalItemsCount - count) / columnsCount); } // slot should be already correct. // No need to correct collapsed slots. They should be empty (e.g. no groups to collapse). System.Diagnostics.Debug.Assert(this.groupHeadersTable.IsEmpty, "GroupHeaders table should be empty."); //// System.Diagnostics.Debug.Assert(this.collapsedSlotsTable.IsEmpty, "CollapsedSlots table should be empty since we don't have groups."); } else { // We added new item (not group) into existing bottom level group (not root group). slot = changedGroupInfo.Index + 1 + addRemoveItemIndex; // Update the group last slot. changedGroupInfo.LastSubItemSlot -= count; this.groupHeadersTable.RemoveIndex(slot); isVisible = changedGroupInfo.IsExpanded && changedGroupInfo.IsVisible(); if (isVisible) { var changedGroupItemsCount = changedGroupInfo.LastSubItemSlot + count - changedGroupInfo.Index; int itemsPerRow = (int)(this.availableOppositeLength / this.DefaultItemOppositeLength); if (changedGroupItemsCount % itemsPerRow == count) { totalRemovedLines++; } } this.UpdateGroupHeadersTable(changedGroupInfo.Index, -count); } } // Update parent groups last slot. UpdateParentGroupInfosLastSlot(-count, changedGroupInfo); // Update TotalCount. this.VisibleLineCount -= totalRemovedLines; this.totalItemsCount -= count; this.TotalSlotCount -= totalRemovedLines; if (this.RenderInfo.Count > 0 && totalRemovedLines > 0) { var removeRowIndex = this.CalculateNextRowPosition(slot); this.RenderInfo.RemoveRange(removeRowIndex, totalRemovedLines); } this.ColumnSlotsRenderInfo.RemoveRange(slot, count); this.OnAvailableLengthChanged(this.AvailableOppositeLength, this.AvailableOppositeLength); var layoutResult = new AddRemoveLayoutResult(slot, count); foreach (var strategy in this.LayoutStrategies) { strategy.OnItemRemoved(layoutResult); } // return result indexes so that changed can be reflected to UI. return(layoutResult); }
internal override AddRemoveLayoutResult AddItem(object changedItem, object addRemoveItem, int addRemoveItemIndex) { int count = 1; int totalLines = 0; bool isVisible = true; var groupInfo = this.GetGroupInfo(changedItem); bool changedGroupIsRoot = groupInfo == null; int index = addRemoveItemIndex; int level = changedGroupIsRoot ? 0 : groupInfo.Level + 1; // We added/removed group so we need to index all subgroups. if (addRemoveItem is IGroup && changedItem != addRemoveItem) { index = this.GetInsertedGroupSlot(changedItem, addRemoveItemIndex); isVisible = groupInfo != null ? (groupInfo.IsExpanded && groupInfo.IsVisible()) : true; // We give a list in which to insert groups so that we can manually correct the indexes in groupHeadersTable. List <GroupInfo> insertedGroups = new List <GroupInfo>(); count = this.CountAndPopulateTables(addRemoveItem, index, level, this.GroupLevels, groupInfo, false, insertedGroups, ref totalLines); bool first = true; int innerMostSlot = index; foreach (var newGroupInfo in insertedGroups) { int groupInfoToInsertSpan = newGroupInfo.GetLineSpan(); if (first) { first = false; this.groupHeadersTable.InsertIndexes(newGroupInfo.Index, groupInfoToInsertSpan); if (isVisible) { this.ColumnSlotsRenderInfo.InsertRange(index, IndexStorage.UnknownItemLength, count); } } this.groupHeadersTable.AddValue(newGroupInfo.Index, newGroupInfo); // We get the inner most group so that we update groups after inner most (the upper one are newly created and their index and last slot count are correct. // We get it only if the change is in the root group. In existing groups we know the correct slot. if (changedGroupIsRoot) { innerMostSlot = newGroupInfo.Index; } } // Update groups after current one. this.UpdateGroupHeadersTable(innerMostSlot, count); if (groupInfo != null) { // Update the group last slot. groupInfo.LastSubItemSlot += count; } } else { // We added new item in flat scenario. if (changedGroupIsRoot) { if (isVisible) { var columnsCount = Math.Floor(this.availableOppositeLength / this.DefaultItemOppositeLength); totalLines = (int)Math.Ceiling((this.totalItemsCount + count) / columnsCount) - (int)Math.Ceiling(this.totalItemsCount / columnsCount); this.ColumnSlotsRenderInfo.InsertRange(index, this.DefaultItemOppositeLength, count); } // slot should be already correct. // No need to correct collapsed slots. They should be empty (e.g. no groups to collapse). System.Diagnostics.Debug.Assert(this.groupHeadersTable.IsEmpty, "GroupHeaders table should be empty."); //// System.Diagnostics.Debug.Assert(this.collapsedSlotsTable.IsEmpty, "CollapsedSlots table should be empty since we don't have groups."); } else { // We added new item (not group) into existing bottom level group (not root group). index = groupInfo.Index + 1 + addRemoveItemIndex; // Update the group last slot. groupInfo.LastSubItemSlot += count; this.groupHeadersTable.InsertIndex(index); isVisible = groupInfo.IsExpanded && groupInfo.IsVisible(); if (isVisible) { if (groupInfo.Level == this.GroupLevels - 1) { var columnsCount = Math.Floor(this.availableOppositeLength / this.DefaultItemOppositeLength); // get children count. var childrenCount = (groupInfo.Item as IGroup).Items.Count; totalLines = (int)Math.Ceiling(childrenCount / columnsCount) - (int)Math.Ceiling((childrenCount - count) / columnsCount); } } this.UpdateGroupHeadersTable(groupInfo.Index, count); this.ColumnSlotsRenderInfo.InsertRange(index, this.DefaultItemOppositeLength, count); } } // Update parent groups last slot. UpdateParentGroupInfosLastSlot(count, groupInfo); // Update TotalCount. this.VisibleLineCount += totalLines; this.TotalSlotCount += totalLines; this.totalItemsCount += count; // Update Visible line count if not collapsed. double length = isVisible ? this.averageItemLength : 0; // Insert new records into IndexTree. var insertRowIndex = this.CalculateNextRowPosition(index); this.RenderInfo.InsertRange(insertRowIndex, IndexStorage.UnknownItemLength, totalLines); this.OnAvailableLengthChanged(this.AvailableOppositeLength, this.AvailableOppositeLength); var layoutResult = new AddRemoveLayoutResult(index, count); foreach (var strategy in this.LayoutStrategies) { strategy.OnItemAdded(layoutResult); } // return result indexes so that changed can be reflected to UI. return(layoutResult); }
public override void OnItemRemoved(AddRemoveLayoutResult layoutResult) { }
public override void OnItemRemoved(AddRemoveLayoutResult layoutResult) { // throw new NotImplementedException(); }
public override void OnItemAdded(AddRemoveLayoutResult layoutResult) { var start = Math.Min(this.slotsToBuild.LowerBound, layoutResult.StartSlot); this.slotsToBuild = new Range <bool>(start, this.slotsToBuild.UpperBound + layoutResult.SlotsCount, true); }