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
61 changes: 60 additions & 1 deletion Terminal.Gui/Views/ScrollBar/ScrollSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Terminal.Gui.Views;
/// Can be dragged with the mouse, constrained by the size of the Viewport of it's superview. Can be
/// oriented either vertically or horizontally.
/// </summary>
public sealed class ScrollSlider : View, IOrientation, IDesignable
public sealed class ScrollSlider : View, IOrientation, IDesignable, IValue<int>
{
/// <summary>
/// Initializes a new instance.
Expand Down Expand Up @@ -208,6 +208,23 @@ internal void MoveToPosition (int position)

private void RaisePositionChangeEvents (int newPosition)
{
int oldPosition = _position;

// Raise IValue<int> ValueChanging event
ValueChangingEventArgs<int> valueChangingArgs = new (oldPosition, newPosition);

if (OnValueChanging (valueChangingArgs) || valueChangingArgs.Handled)
{
return;
}

ValueChanging?.Invoke (this, valueChangingArgs);

if (valueChangingArgs.Handled)
{
return;
}

CancelEventArgs<int> args = new (ref _position, ref newPosition);
PositionChanging?.Invoke (this, args);

Expand All @@ -223,6 +240,12 @@ private void RaisePositionChangeEvents (int newPosition)

PositionChanged?.Invoke (this, new (in _position));

// Raise IValue<int> ValueChanged and ValueChangedUntyped events
ValueChangedEventArgs<int> valueChangedArgs = new (oldPosition, _position);
OnValueChanged (valueChangedArgs);
ValueChanged?.Invoke (this, valueChangedArgs);
ValueChangedUntyped?.Invoke (this, new ValueChangedEventArgs<object?> (oldPosition, _position));

Scrolled?.Invoke (this, new (in distance));

RaiseActivating (new CommandContext (Command.Activate, new WeakReference<View> (this), new CommandBinding ([Command.Activate], null, distance)));
Expand All @@ -240,6 +263,42 @@ private void RaisePositionChangeEvents (int newPosition)
/// <summary>Raised when the <see cref="Position"/> has changed. Indicates how much to scroll.</summary>
public event EventHandler<EventArgs<int>>? Scrolled;

#region IValue<int> Implementation

/// <summary>
/// Gets or sets the position of the ScrollSlider. This is an alias for <see cref="Position"/>
/// provided to satisfy the <see cref="IValue{TValue}"/> interface.
/// </summary>
public int Value
{
get => Position;
set => Position = value;
}

/// <inheritdoc/>
public event EventHandler<ValueChangingEventArgs<int>>? ValueChanging;

/// <inheritdoc/>
public event EventHandler<ValueChangedEventArgs<int>>? ValueChanged;

/// <inheritdoc/>
public event EventHandler<ValueChangedEventArgs<object?>>? ValueChangedUntyped;

/// <summary>
/// Called when <see cref="Value"/> is changing. Return <see langword="true"/> to cancel the change.
/// </summary>
/// <param name="args">The event arguments containing old and new values.</param>
/// <returns><see langword="true"/> to cancel the change; otherwise <see langword="false"/>.</returns>
private bool OnValueChanging (ValueChangingEventArgs<int> args) => false;

Comment thread
tig marked this conversation as resolved.
/// <summary>
/// Called when <see cref="Value"/> has changed.
/// </summary>
/// <param name="args">The event arguments containing old and new values.</param>
private void OnValueChanged (ValueChangedEventArgs<int> args) { }

Comment thread
tig marked this conversation as resolved.
#endregion

/// <inheritdoc/>
protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attribute currentAttribute)
{
Expand Down
146 changes: 146 additions & 0 deletions Tests/UnitTestsParallelizable/Views/ScrollSliderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,4 +1010,150 @@ public void Draws_Correctly (int superViewportWidth, int superViewportHeight, in

_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output, driver);
}

// Copilot - IValue<int> tests

[Fact]
public void Value_Gets_And_Sets_Position ()
{
ScrollSlider slider = new () { VisibleContentSize = 100, Size = 10 };
Assert.Equal (0, slider.Value);

slider.Value = 5;
Assert.Equal (5, slider.Value);
Assert.Equal (5, slider.Position);

slider.Position = 10;
Assert.Equal (10, slider.Value);
Assert.Equal (10, slider.Position);

slider.Dispose ();
}

[Fact]
public void ValueChanging_Event_Is_Raised ()
{
ScrollSlider slider = new () { VisibleContentSize = 100, Size = 10 };
var eventRaised = false;
int oldValue = -1;
int newValue = -1;

slider.ValueChanging += (_, e) =>
{
eventRaised = true;
oldValue = e.CurrentValue;
newValue = e.NewValue;
};

slider.Value = 5;

Assert.True (eventRaised);
Assert.Equal (0, oldValue);
Assert.Equal (5, newValue);

slider.Dispose ();
}

[Fact]
public void ValueChanging_Can_Cancel_Change ()
{
ScrollSlider slider = new () { VisibleContentSize = 100, Size = 10 };

slider.ValueChanging += (_, e) => e.Handled = true;

slider.Value = 5;

Assert.Equal (0, slider.Value);

slider.Dispose ();
}

[Fact]
public void ValueChanged_Event_Is_Raised ()
{
ScrollSlider slider = new () { VisibleContentSize = 100, Size = 10 };
var eventRaised = false;
int oldValue = -1;
int newValue = -1;

slider.ValueChanged += (_, e) =>
{
eventRaised = true;
oldValue = e.OldValue;
newValue = e.NewValue;
};

slider.Value = 5;

Assert.True (eventRaised);
Assert.Equal (0, oldValue);
Assert.Equal (5, newValue);

slider.Dispose ();
}

[Fact]
public void ValueChangedUntyped_Event_Is_Raised ()
{
ScrollSlider slider = new () { VisibleContentSize = 100, Size = 10 };
var eventRaised = false;
object? oldValue = null;
object? newValue = null;

slider.ValueChangedUntyped += (_, e) =>
{
eventRaised = true;
oldValue = e.OldValue;
newValue = e.NewValue;
};

slider.Value = 5;

Assert.True (eventRaised);
Assert.Equal (0, oldValue);
Assert.Equal (5, newValue);

slider.Dispose ();
}

[Fact]
public void GetValue_Returns_Boxed_Position ()
{
ScrollSlider slider = new () { VisibleContentSize = 100, Size = 10 };
slider.Value = 7;

IValue ivalue = slider;
Assert.Equal (7, ivalue.GetValue ());

slider.Dispose ();
}

[Fact]
public void TrySetValueFromString_Sets_Position ()
{
ScrollSlider slider = new () { VisibleContentSize = 100, Size = 10 };

IValue ivalue = slider;
bool result = ivalue.TrySetValueFromString ("5");

Assert.True (result);
Assert.Equal (5, slider.Position);
Assert.Equal (5, slider.Value);

slider.Dispose ();
}

[Fact]
public void TrySetValueFromString_Returns_False_For_Invalid_Input ()
{
ScrollSlider slider = new () { VisibleContentSize = 100, Size = 10 };

IValue ivalue = slider;
bool result = ivalue.TrySetValueFromString ("not-a-number");

Assert.False (result);
Assert.Equal (0, slider.Value);

slider.Dispose ();
}
}
Loading