ConditionalNode
Shows or hides content based on an observable boolean condition.
Basic Usage
csharp
// Show content when condition is true
new ConditionalNode(
ViewModel.IsVisibleChanged,
new TextNode("I'm visible!"))
// Using When helper
When.True(
ViewModel.IsVisibleChanged,
new TextNode("I'm visible!"))With Else Content
csharp
// Show different content based on condition
new ConditionalNode(
ViewModel.IsLoadingChanged,
thenNode: new SpinnerNode().WithLabel("Loading..."),
elseNode: new TextNode("Ready"))
// Using When helper
When.TrueElse(
ViewModel.IsLoadingChanged,
thenContent: new SpinnerNode().WithLabel("Loading..."),
elseContent: new TextNode("Ready"))Inverted Condition
csharp
// Show content when condition is false
When.False(
ViewModel.HasDataChanged,
new TextNode("No data available"))Common Patterns
Loading State
csharp
new ConditionalNode(
ViewModel.IsLoadingChanged,
thenNode: new SpinnerNode().WithLabel("Loading..."),
elseNode: contentPanel)Error Display
csharp
When.True(
ViewModel.HasErrorChanged,
new PanelNode()
.WithTitle("Error")
.WithBorderColor(Color.Red)
.WithContent(
ViewModel.ErrorMessageChanged
.Select(msg => new TextNode(msg).WithForeground(Color.Red))
.AsLayout()))Feature Toggle
csharp
When.True(
ViewModel.IsAdvancedModeChanged,
advancedOptionsPanel)Empty State
csharp
When.TrueElse(
ViewModel.ItemsChanged.Select(items => items.Count > 0),
thenContent: itemListPanel,
elseContent: new TextNode("No items yet")
.WithForeground(Color.Gray))Comparison with ReactiveLayoutNode
Both can be used for conditional rendering:
csharp
// Using ConditionalNode - cleaner for simple show/hide
When.True(condition, content)
// Using ReactiveLayoutNode - more flexible
condition
.Select(show => show ? content : new EmptyNode())
.AsLayout()Use ConditionalNode when:
- You have a simple true/false condition
- The content nodes are known at construction time
- You want cleaner, more readable code
Use ReactiveLayoutNode when:
- You need to transform values into different layouts
- You're building dynamic content based on observable values
API Reference
Constructors
csharp
public ConditionalNode(
IObservable<bool> condition,
ILayoutNode thenNode,
ILayoutNode? elseNode = null)When Helper Methods
csharp
// Show when true
When.True(IObservable<bool> condition, ILayoutNode content)
// Show when true, else show other content
When.TrueElse(
IObservable<bool> condition,
ILayoutNode thenContent,
ILayoutNode elseContent)
// Show when false
When.False(IObservable<bool> condition, ILayoutNode content)Source Code
View ConditionalNode implementation
csharp
// Copyright (c) Petabridge, LLC. All rights reserved.
// Licensed under the Apache 2.0 license. See LICENSE file in the project root for full license information.
using System.Reactive;
using System.Reactive.Subjects;
using Termina.Rendering;
namespace Termina.Layout;
/// <summary>
/// A layout node that conditionally shows content based on an observable boolean.
/// </summary>
public sealed class ConditionalNode : LayoutNode, IInvalidatingNode
{
private readonly IObservable<bool> _source;
private IDisposable? _subscription;
private readonly Subject<Unit> _invalidated = new();
private bool _condition;
private readonly ILayoutNode _thenNode;
private readonly ILayoutNode _elseNode;
private bool _isActive = true;
/// <inheritdoc />
public IObservable<Unit> Invalidated => _invalidated;
/// <summary>
/// Create a conditional node that shows/hides based on an observable condition.
/// </summary>
/// <param name="condition">Observable that emits true/false.</param>
/// <param name="thenNode">Node to show when condition is true.</param>
/// <param name="elseNode">Node to show when condition is false (optional).</param>
public ConditionalNode(IObservable<bool> condition, ILayoutNode thenNode, ILayoutNode? elseNode = null)
{
_source = condition;
_thenNode = thenNode;
_elseNode = elseNode ?? new EmptyNode();
_subscription = condition.Subscribe(
onNext: value =>
{
if (_condition != value)
{
_condition = value;
_invalidated.OnNext(Unit.Default);
}
},
onError: _ => { },
onCompleted: () => { });
}
private ILayoutNode ActiveNode => _condition ? _thenNode : _elseNode;
/// <inheritdoc />
public override Size Measure(Size available)
{
return ActiveNode.Measure(available);
}
/// <inheritdoc />
public override void Render(IRenderContext context, Rect bounds)
{
ActiveNode.Render(context, bounds);
}
/// <inheritdoc />
public override void OnActivate()
{
_isActive = true;
// If subscription was disposed during deactivation, recreate it
if (_subscription == null || _subscription is System.Reactive.Disposables.BooleanDisposable { IsDisposed: true })
{
_subscription = _source.Subscribe(
onNext: value =>
{
if (_condition != value)
{
_condition = value;
_invalidated.OnNext(Unit.Default);
}
},
onError: _ => { },
onCompleted: () => { });
}
// Activate both branches (both are always kept in memory)
if (_thenNode is LayoutNode thenLayoutNode)
{
thenLayoutNode.OnActivate();
}
if (_elseNode is LayoutNode elseLayoutNode)
{
elseLayoutNode.OnActivate();
}
base.OnActivate();
}
/// <inheritdoc />
public override void OnDeactivate()
{
_isActive = false;
// Deactivate both branches
if (_thenNode is LayoutNode thenLayoutNode)
{
thenLayoutNode.OnDeactivate();
}
if (_elseNode is LayoutNode elseLayoutNode)
{
elseLayoutNode.OnDeactivate();
}
// Dispose subscription to pause updates
_subscription?.Dispose();
_subscription = null;
base.OnDeactivate();
}
/// <inheritdoc />
public override void Dispose()
{
_subscription?.Dispose();
_invalidated.OnCompleted();
_invalidated.Dispose();
_thenNode.Dispose();
_elseNode.Dispose();
base.Dispose();
}
}
/// <summary>
/// Factory for conditional rendering.
/// </summary>
public static class When
{
/// <summary>
/// Show content when condition is true.
/// </summary>
public static ConditionalNode True(IObservable<bool> condition, ILayoutNode content)
{
return new ConditionalNode(condition, content);
}
/// <summary>
/// Show content when condition is true, otherwise show else content.
/// </summary>
public static ConditionalNode TrueElse(
IObservable<bool> condition,
ILayoutNode thenContent,
ILayoutNode elseContent)
{
return new ConditionalNode(condition, thenContent, elseContent);
}
/// <summary>
/// Show content when condition is false.
/// </summary>
public static ConditionalNode False(IObservable<bool> condition, ILayoutNode content)
{
return new ConditionalNode(
System.Reactive.Linq.Observable.Select(condition, c => !c),
content);
}
}