Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/SQLBI.Whiteboard.Core/Commands/CommandHistory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,31 @@ public void Undo(BoardDocument document)
}
}

/// <summary>
/// Several commands as one step of the history. Deleting a shape is the reason
/// it exists: what is removed and the connectors that have to let go of it are
/// two changes to the document and one thing that happened, so one undo puts
/// both back.
/// </summary>
public sealed record CompositeCommand(IReadOnlyList<IBoardCommand> Commands) : IBoardCommand
{
public void Execute(BoardDocument document)
{
foreach (var command in Commands)
{
command.Execute(document);
}
}

public void Undo(BoardDocument document)
{
for (var index = Commands.Count - 1; index >= 0; index--)
{
Commands[index].Undo(document);
}
}
}

public sealed record ReplaceObjectCommand(BoardObject Before, BoardObject After) : IBoardCommand
{
public void Execute(BoardDocument document) => document.ReplaceObject(After);
Expand Down
56 changes: 50 additions & 6 deletions src/SQLBI.Whiteboard.Core/Export/BoardPartitioner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ private static string FirstLine(string text) =>

private static List<Unit> BuildUnits(IReadOnlyList<BoardObject> objects)
{
var units = new List<Unit>();
var groups = new List<List<BoardObject>?>();
var groupByObject = new Dictionary<Guid, int>();
var strokesByContainer = objects
.OfType<InkStrokeObject>()
.Where(stroke => stroke.ContainerId is not null)
Expand All @@ -139,7 +140,8 @@ private static List<Unit> BuildUnits(IReadOnlyList<BoardObject> objects)
containerIds.Add(item.Id);
var members = new List<BoardObject> { item };
members.AddRange(strokesByContainer[item.Id]);
units.Add(Unit.Of(members));
groupByObject[item.Id] = groups.Count;
groups.Add(members);
}
}

Expand All @@ -148,15 +150,57 @@ private static List<Unit> BuildUnits(IReadOnlyList<BoardObject> objects)
if (item is InkStrokeObject stroke &&
(stroke.ContainerId is null || !containerIds.Contains(stroke.ContainerId.Value)))
{
units.Add(Unit.Of([stroke]));
groups.Add([stroke]);
}
else if (item is not InkStrokeObject && item is not IBoardContainer && item is not FrameBoardObject)
else if (item is not InkStrokeObject &&
item is not IBoardContainer &&
item is not FrameBoardObject &&
item is not ConnectorBoardObject)
{
units.Add(Unit.Of([item]));
groups.Add([item]);
}
}

return units;
// Connectors last, once there is something to join: one joins the unit
// of what it is bound to, and one bound at both ends brings those two
// units together, so a cut never falls between a shape and its arrow.
foreach (var connector in objects.OfType<ConnectorBoardObject>())
{
var first = GroupOf(groupByObject, connector.StartAnchor);
var second = GroupOf(groupByObject, connector.EndAnchor);
var target = first ?? second;
if (target is null)
{
groups.Add([connector]);
continue;
}

if (first is { } left && second is { } right && left != right)
{
Merge(groups, groupByObject, left, right);
}

groups[target.Value]!.Add(connector);
}

return groups.Where(group => group is not null).Select(group => Unit.Of(group!)).ToList();
}

private static int? GroupOf(IReadOnlyDictionary<Guid, int> groupByObject, ConnectorAnchor? anchor) =>
anchor is { } bound && groupByObject.TryGetValue(bound.ObjectId, out var group) ? group : null;

private static void Merge(
List<List<BoardObject>?> groups,
Dictionary<Guid, int> groupByObject,
int into,
int from)
{
groups[into]!.AddRange(groups[from]!);
groups[from] = null;
foreach (var id in groupByObject.Where(pair => pair.Value == from).Select(pair => pair.Key).ToArray())
{
groupByObject[id] = into;
}
}

private static void Split(List<Unit> units, ExportLayoutOptions options, List<Region> leaves)
Expand Down
Loading