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
19 changes: 19 additions & 0 deletions src/TextForge.Core/Documents/Document.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,25 @@ private static bool RemoveRecursive(IList<Module> list, Module target)
#endregion

#region Reordering Operations
public void DetachModule(Module module)
{
ArgumentNullException.ThrowIfNull(module);

if (module.Parent is not null)
{
// 1. Remove from parent collection (auto-sets module.Parent = null)
module.Parent.SubModules.Remove(module);

// 2. Add to root document collection
Modules.Add(module);
}

// 3. Update state
module.Detach();

// 4. Trigger document change pipeline
NotifyChanged();
}

/// <summary>
/// Moves a module one position earlier within its current sibling list (root level or nested level).
Expand Down
3 changes: 3 additions & 0 deletions src/TextForge.Core/Engine/DocumentEngine.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Linq;
using TextForge.Core.Documents;
using TextForge.Core.Modules;
using TextForge.Core.Templates;
Expand All @@ -14,6 +15,7 @@ public RenderTree Evaluate(Document document, DefaultTemplate template)
ArgumentNullException.ThrowIfNull(template);

var rootNodes = document.Modules
.Where(module => module.ConnectionState == ModuleConnectionState.Connected) // Skip detached root modules
.Select(module => EvaluateModule(module, template))
.ToList();

Expand All @@ -34,6 +36,7 @@ private RenderNode EvaluateModule(Module module, DefaultTemplate template)
var resolvedLayout = template.ResolveLayout(module);

var children = module.SubModules
.Where(child => child.ConnectionState == ModuleConnectionState.Connected) // Skip detached child modules
.Select(child => EvaluateModule(child, template))
.ToList();

Expand Down
26 changes: 26 additions & 0 deletions src/TextForge.Core/Modules/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class Module : INotifyPropertyChanged
private bool _isExpanded;
private Module? _parent;

private ModuleConnectionState _connectionState = ModuleConnectionState.Connected;
public event PropertyChangedEventHandler? PropertyChanged;

public Guid Id { get; init; } = Guid.NewGuid();
Expand Down Expand Up @@ -109,6 +110,31 @@ private void WireSubModulesCollection()
SubModules.CollectionChanged += OnSubModulesChanged;
}

/// <summary>
/// Dictates whether the module is actively part of the document flow or temporarily detached.
/// Detached modules retain all data and children but are skipped during PDF rendering.
/// </summary>
public ModuleConnectionState ConnectionState
{
get => _connectionState;
set
{
if (_connectionState != value)
{
_connectionState = value;
OnPropertyChanged();
OnPropertyChanged(nameof(IsDetached));
}
}
}

[JsonIgnore]
public bool IsDetached => ConnectionState == ModuleConnectionState.Detached;

// Helper toggle methods
public void Detach() => ConnectionState = ModuleConnectionState.Detached;
public void Connect() => ConnectionState = ModuleConnectionState.Connected;

private void OnSubModulesChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
Expand Down
17 changes: 17 additions & 0 deletions src/TextForge.Core/Modules/ModuleConnectionState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace TextForge.Core.Modules;

/// <summary>
/// Dictates whether a module participates in the document layout.
/// </summary>
public enum ModuleConnectionState
{
/// <summary>
/// The module is part of the layout and will be rendered.
/// </summary>
Connected,

/// <summary>
/// The module is preserved in the document model but excluded from layout and rendering.
/// </summary>
Detached
}
8 changes: 4 additions & 4 deletions src/TextForge.Desktop/Views/Components/ModuleEditorView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@
Background="Transparent" Foreground="#EF4444"
ToolTip.Tip="Delete Module"
Tag="{Binding}" Click="DeleteModuleButton_Click" />
<Button Content="+" FontSize="14" FontWeight="Bold" Width="22"
<Button Content="" FontSize="14" FontWeight="Bold" Width="22"
Height="22" Padding="0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Background="Transparent" Foreground="#10B981"
ToolTip.Tip="Duplicate Module"
Tag="{Binding}" Click="DuplicateModuleButton_Click" />
Background="Transparent" Foreground="#F59E0B"
ToolTip.Tip="Detach from layout"
Tag="{Binding}" Click="DetachModuleButton_Click" />

</StackPanel>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
public event EventHandler<Module>? ModuleMoveDownRequested;
public event EventHandler<Module>? ModuleDeleteRequested;
public event EventHandler<Module>? ModuleDuplicateRequested;
public event EventHandler<Module>? ModuleDetachRequested;
public event EventHandler<Module>? ModuleReconnectRequested;

Check warning on line 18 in src/TextForge.Desktop/Views/Components/ModuleEditorView.axaml.cs

View workflow job for this annotation

GitHub Actions / ci

The event 'ModuleEditorView.ModuleReconnectRequested' is never used

Check warning on line 18 in src/TextForge.Desktop/Views/Components/ModuleEditorView.axaml.cs

View workflow job for this annotation

GitHub Actions / ci

The event 'ModuleEditorView.ModuleReconnectRequested' is never used

public ModuleEditorView()
{
Expand Down Expand Up @@ -51,6 +53,13 @@
ModuleDuplicateRequested?.Invoke(this, module);
}
}
private void DetachModuleButton_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button { Tag: Module module })
{
ModuleDetachRequested?.Invoke(this, module);
}
}

/// <summary>
/// Binds the editor to a document instance.
Expand Down
11 changes: 10 additions & 1 deletion src/TextForge.Desktop/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ private void BindModuleList()
moduleEditor.ModuleMoveDownRequested += ModuleEditor_ModuleMoveDownRequested;
moduleEditor.ModuleDeleteRequested += ModuleEditor_ModuleDeleteRequested;
moduleEditor.ModuleDuplicateRequested += ModuleEditor_ModuleDuplicateRequested;

moduleEditor.ModuleDetachRequested += ModuleEditor_ModuleDetachRequested;

var moduleListBox = moduleEditor.FindControl<ListBox>("ModuleListBox");
if (moduleListBox is not null)
Expand Down Expand Up @@ -206,6 +206,15 @@ private void ModuleEditor_ModuleDuplicateRequested(object? sender, Module module
}
}

private void ModuleEditor_ModuleDetachRequested(object? sender, Module module)
{
if (_currentDocument is not null)
{
_currentDocument.DetachModule(module);
RefreshModuleEditorList();
}
}

#endregion

#region Document & Preview Synchronization
Expand Down
Loading