Files
Terminal.Gui/Terminal.Gui/App/CWP/CWPPropertyHelper.cs
Copilot fc9c40c2a0 Fixes #4150 - Finish implementing Line View (#4260)
* Initial plan

* Add comprehensive analysis of Line implementation status

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Complete Line implementation with documentation, example, and tests

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Add PR summary documenting Line implementation completion

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Add comprehensive completion report for Issue 4150

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Fix Line rendering: use SuperView's LineCanvas instead of own

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Redesign Line to use Border instead of manual LineCanvas

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Add Line.Style property to avoid BorderStyle conflict

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Add SetWidth/SetHeight methods to preserve dimensions on Orientation change

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Implement CWP events for Width/Height properties; update Line to use events

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* WIP: Updating Line.
Cleaned up Layout tests.

* Made Height/Width non-nullable

* Add doWork stage to CWPPropertyHelper to execute between Changing and Changed events

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Move ViewLayoutEventTests to parallelizable tests without AutoInitShutdown

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Replace tracking fields with Length property for thread-safe Line implementation

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Fix orientation handling to preserve user-set dimensions in object initializers

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Simplify orientation handling with dimension swapping - all tests passing

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Add Length backing field and fix object initializer dimension handling

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Use CWP OnChanging events to manage dimensions instead of OnChanged

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Move LineTests to parallelizable; simplify tests with GetAnchor; fix Length property

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Code cleanup.

* Code cleanup.

* Update Terminal.Gui/ViewBase/View.Layout.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update Terminal.Gui/ViewBase/View.Layout.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update Terminal.Gui/ViewBase/View.Layout.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update Terminal.Gui/ViewBase/View.Layout.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fixed nullable warning in test

* Removed PR files and updated copilot guidance

* Reverted .gitignore change

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Tig <tig@users.noreply.github.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-03 12:26:45 -07:00

109 lines
4.6 KiB
C#

namespace Terminal.Gui.App;
#nullable enable
/// <summary>
/// Provides helper methods for executing property change workflows in the Cancellable Work Pattern (CWP).
/// </summary>
/// <remarks>
/// <para>
/// Used for workflows where a property value is modified, such as in <see cref="OrientationHelper"/> or
/// <see cref="View.SchemeName"/>, allowing pre- and post-change events to customize or cancel the change.
/// </para>
/// </remarks>
/// <seealso cref="ValueChangingEventArgs{T}"/>
/// <seealso cref="ValueChangedEventArgs{T}"/>
public static class CWPPropertyHelper
{
/// <summary>
/// Executes a CWP workflow for a property change, with pre- and post-change events.
/// </summary>
/// <typeparam name="T">
/// The type of the property value, which may be a nullable reference type (e.g., <see cref="string"/>
/// ?).
/// </typeparam>
/// <param name="currentValue">The current property value, which may be null for nullable types.</param>
/// <param name="newValue">The proposed new property value, which may be null for nullable types.</param>
/// <param name="onChanging">The virtual method invoked before the change, returning true to cancel.</param>
/// <param name="changingEvent">The pre-change event raised to allow modification or cancellation.</param>
/// <param name="doWork">The action that performs the actual work of setting the property (e.g., updating backing field, calling related methods).</param>
/// <param name="onChanged">The virtual method invoked after the change.</param>
/// <param name="changedEvent">The post-change event raised to notify of the completed change.</param>
/// <param name="finalValue">
/// The final value after the workflow, reflecting any modifications, which may be null for
/// nullable types.
/// </param>
/// <returns>True if the property was changed, false if cancelled.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if <see cref="ValueChangingEventArgs{T}.NewValue"/> is null for non-nullable reference types after the
/// workflow.
/// </exception>
/// <example>
/// <code>
/// string? current = _schemeName;
/// string? proposed = "Base";
/// Func&lt;ValueChangingEventArgs&lt;string?&gt;, bool&gt; onChanging = OnSchemeNameChanging;
/// EventHandler&lt;ValueChangingEventArgs&lt;string?&gt;&gt;? changingEvent = SchemeNameChanging;
/// Action&lt;string?&gt; doWork = value => _schemeName = value;
/// Action&lt;ValueChangedEventArgs&lt;string?&gt;&gt;? onChanged = OnSchemeNameChanged;
/// EventHandler&lt;ValueChangedEventArgs&lt;string?&gt;&gt;? changedEvent = SchemeNameChanged;
/// bool changed = CWPPropertyHelper.ChangeProperty(
/// current, proposed, onChanging, changingEvent, doWork, onChanged, changedEvent, out string? final);
/// </code>
/// </example>
public static bool ChangeProperty<T> (
T currentValue,
T newValue,
Func<ValueChangingEventArgs<T>, bool> onChanging,
EventHandler<ValueChangingEventArgs<T>>? changingEvent,
Action<T> doWork,
Action<ValueChangedEventArgs<T>>? onChanged,
EventHandler<ValueChangedEventArgs<T>>? changedEvent,
out T finalValue
)
{
if (EqualityComparer<T>.Default.Equals (currentValue, newValue))
{
finalValue = currentValue;
return false;
}
ValueChangingEventArgs<T> args = new (currentValue, newValue);
bool cancelled = onChanging (args) || args.Handled;
if (cancelled)
{
finalValue = currentValue;
return false;
}
changingEvent?.Invoke (null, args);
if (args.Handled)
{
finalValue = currentValue;
return false;
}
// Validate NewValue for non-nullable reference types
if (args.NewValue is null && !typeof (T).IsValueType && !Nullable.GetUnderlyingType (typeof (T))?.IsValueType == true)
{
throw new InvalidOperationException ("NewValue cannot be null for non-nullable reference types.");
}
finalValue = args.NewValue;
// Do the work (set backing field, update related properties, etc.) BEFORE raising Changed events
doWork (finalValue);
ValueChangedEventArgs<T> changedArgs = new (currentValue, finalValue);
onChanged?.Invoke (changedArgs);
changedEvent?.Invoke (null, changedArgs);
return true;
}
}