Skip to content

Commit 2d330f5

Browse files
Fix #1307: split-created document docks are WorkspaceContentDock
Overrides `WorkspaceDockFactory.CreateDocumentDock` so Dock's `NewHorizontalDocumentDock` / `NewVerticalDocumentDock` split paths mint `WorkspaceContentDock` instances instead of plain `Dock.Model.Mvvm.Controls.DocumentDock`. Previously the plain type matched only the generic `IDocumentDock` fallback template in `DockDataTemplates.axaml`, which has no `HeaderTemplate`, so tabs in split-created regions rendered with Dock.Avalonia's default header — no favicon and no `MaxWidth=180` / `CharacterEllipsis` constraint. Assigns a fresh GUID `Id` per instance to prevent `FactoryBase` from copying the source dock's id on split. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 07579a5 commit 2d330f5

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

Phantom.Workspaces.Tests/MainWindowDockTemplateTests.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,62 @@ public void WorkspacePaneDockControl_InnerDockControl_HasDocumentDockTemplate()
268268
Assert.NotNull(matching);
269269
}
270270

271+
// ── Regression tests for #1307 ────────────────────────────────────────────
272+
// Split-created document docks (via NewHorizontalDocumentDock / NewVerticalDocumentDock)
273+
// must be WorkspaceContentDock so tabs they host match the rich header template
274+
// (favicon + MaxWidth=180 + CharacterEllipsis) instead of the bare IDocumentDock fallback.
275+
276+
[AvaloniaFact(Timeout = 15_000)]
277+
public async Task WorkspaceDockFactory_CreateDocumentDock_ReturnsWorkspaceContentDock()
278+
{
279+
await using var viewModel = CreateBootedMainWindowViewModel();
280+
await viewModel.InitializeAsync();
281+
var factory = GetDockFactory(viewModel);
282+
283+
var created = factory.CreateDocumentDock();
284+
285+
Assert.IsType<WorkspaceContentDock>(created);
286+
}
287+
288+
[AvaloniaFact(Timeout = 15_000)]
289+
public async Task WorkspaceDockFactory_CreateDocumentDock_AssignsFreshUniqueId()
290+
{
291+
await using var viewModel = CreateBootedMainWindowViewModel();
292+
await viewModel.InitializeAsync();
293+
var factory = GetDockFactory(viewModel);
294+
295+
var a = factory.CreateDocumentDock();
296+
var b = factory.CreateDocumentDock();
297+
298+
Assert.False(string.IsNullOrEmpty(a.Id));
299+
Assert.False(string.IsNullOrEmpty(b.Id));
300+
Assert.NotEqual(a.Id, b.Id);
301+
}
302+
303+
[AvaloniaFact(Timeout = 15_000)]
304+
public async Task WorkspaceDockFactory_CreateDocumentDock_MatchesRichHeaderTemplate_InInnerPaneDockControl()
305+
{
306+
// #1307: the split-created dock must be picked up by the rich WorkspaceContentDock
307+
// template ahead of the generic IDocumentDock fallback in the inner workspace-pane
308+
// DockControl.DataTemplates scope.
309+
await using var viewModel = CreateBootedMainWindowViewModel();
310+
await viewModel.InitializeAsync();
311+
var factory = GetDockFactory(viewModel);
312+
313+
var splitCreated = factory.CreateDocumentDock();
314+
var innerDockControl = BuildInnerWorkspacePaneDockControl();
315+
316+
var matching = innerDockControl.DataTemplates
317+
.OfType<IDataTemplate>()
318+
.FirstOrDefault(t => t.Match(splitCreated));
319+
320+
Assert.NotNull(matching);
321+
// Ensure it is the WorkspaceContentDock-specific template (matches WorkspaceContentDock
322+
// but does NOT match a plain DocumentDock), not the generic IDocumentDock fallback.
323+
Assert.True(matching!.Match(new WorkspaceContentDock()));
324+
Assert.False(matching!.Match(new DocumentDock()));
325+
}
326+
271327
[AvaloniaFact(Timeout = 15_000)]
272328
public void DockDataTemplates_ProportionalDockSplitter_ResolvesProportionalStackPanelSplitter()
273329
{

Phantom.Workspaces/ViewModels/WorkspaceDockFactory.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,24 @@ public override IRootDock CreateLayout()
107107
return root;
108108
}
109109

110+
/// <summary>
111+
/// #1307: split-created docks (via Dock's ``NewHorizontalDocumentDock`` /
112+
/// ``NewVerticalDocumentDock`` paths) must be ``WorkspaceContentDock`` so tabs
113+
/// they host match the rich header template in ``DockDataTemplates.axaml``
114+
/// (favicon + ``MaxWidth=180`` + ``CharacterEllipsis``). A plain ``DocumentDock``
115+
/// would fall through to the generic ``IDocumentDock`` template, which has no
116+
/// ``HeaderTemplate`` and would render an unbounded, icon-less tab title.
117+
/// A fresh ``Id`` is assigned to prevent ``FactoryBase`` from copying the source
118+
/// dock's ``Id`` on split (which would create duplicate ids in the layout).
119+
/// </summary>
120+
public override IDocumentDock CreateDocumentDock()
121+
{
122+
return new WorkspaceContentDock
123+
{
124+
Id = Guid.NewGuid().ToString(),
125+
};
126+
}
127+
110128
/// <summary>
111129
/// Creates a dock layout for workspace content (entity tabs, agent sessions, etc.)
112130
/// Uses ItemsSource wired to <see cref="WorkspacePaneViewModel.Tabs"/> so that

0 commit comments

Comments
 (0)