mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-04 18:40:50 +08:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/main' into blz/issues/1390
# Conflicts: # src/Tests/Spectre.Console.Tests/Expectations/Widgets/Layout/Render_Layout_With_Three_And_One_Columns.Output.verified.txt
This commit is contained in:
		@@ -52,11 +52,19 @@ public static partial class AnsiConsoleExtensions
 | 
			
		||||
            {
 | 
			
		||||
                if (text.Length > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    var lastChar = text.Last();
 | 
			
		||||
                    text = text.Substring(0, text.Length - 1);
 | 
			
		||||
 | 
			
		||||
                    if (mask != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        console.Write("\b \b");
 | 
			
		||||
                        if (UnicodeCalculator.GetWidth(lastChar) == 1)
 | 
			
		||||
                        {
 | 
			
		||||
                            console.Write("\b \b");
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (UnicodeCalculator.GetWidth(lastChar) == 2)
 | 
			
		||||
                        {
 | 
			
		||||
                            console.Write("\b \b\b \b");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,10 +10,11 @@ public static class CalendarExtensions
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="calendar">The calendar to add the calendar event to.</param>
 | 
			
		||||
    /// <param name="date">The calendar event date.</param>
 | 
			
		||||
    /// <param name="customEventHighlightStyle">The calendar event custom highlight style.</param>
 | 
			
		||||
    /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
    public static Calendar AddCalendarEvent(this Calendar calendar, DateTime date)
 | 
			
		||||
    public static Calendar AddCalendarEvent(this Calendar calendar, DateTime date, Style? customEventHighlightStyle = null)
 | 
			
		||||
    {
 | 
			
		||||
        return AddCalendarEvent(calendar, string.Empty, date.Year, date.Month, date.Day);
 | 
			
		||||
        return AddCalendarEvent(calendar, string.Empty, date.Year, date.Month, date.Day, customEventHighlightStyle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -22,10 +23,11 @@ public static class CalendarExtensions
 | 
			
		||||
    /// <param name="calendar">The calendar to add the calendar event to.</param>
 | 
			
		||||
    /// <param name="description">The calendar event description.</param>
 | 
			
		||||
    /// <param name="date">The calendar event date.</param>
 | 
			
		||||
    /// <param name="customEventHighlightStyle">The calendar event custom highlight style.</param>
 | 
			
		||||
    /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
    public static Calendar AddCalendarEvent(this Calendar calendar, string description, DateTime date)
 | 
			
		||||
    public static Calendar AddCalendarEvent(this Calendar calendar, string description, DateTime date, Style? customEventHighlightStyle = null)
 | 
			
		||||
    {
 | 
			
		||||
        return AddCalendarEvent(calendar, description, date.Year, date.Month, date.Day);
 | 
			
		||||
        return AddCalendarEvent(calendar, description, date.Year, date.Month, date.Day, customEventHighlightStyle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -35,10 +37,11 @@ public static class CalendarExtensions
 | 
			
		||||
    /// <param name="year">The year of the calendar event.</param>
 | 
			
		||||
    /// <param name="month">The month of the calendar event.</param>
 | 
			
		||||
    /// <param name="day">The day of the calendar event.</param>
 | 
			
		||||
    /// <param name="customEventHighlightStyle">The calendar event custom highlight style.</param>
 | 
			
		||||
    /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
    public static Calendar AddCalendarEvent(this Calendar calendar, int year, int month, int day)
 | 
			
		||||
    public static Calendar AddCalendarEvent(this Calendar calendar, int year, int month, int day, Style? customEventHighlightStyle = null)
 | 
			
		||||
    {
 | 
			
		||||
        return AddCalendarEvent(calendar, string.Empty, year, month, day);
 | 
			
		||||
        return AddCalendarEvent(calendar, string.Empty, year, month, day, customEventHighlightStyle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -49,15 +52,16 @@ public static class CalendarExtensions
 | 
			
		||||
    /// <param name="year">The year of the calendar event.</param>
 | 
			
		||||
    /// <param name="month">The month of the calendar event.</param>
 | 
			
		||||
    /// <param name="day">The day of the calendar event.</param>
 | 
			
		||||
    /// <param name="customEventHighlightStyle">The calendar event custom highlight style.</param>
 | 
			
		||||
    /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
    public static Calendar AddCalendarEvent(this Calendar calendar, string description, int year, int month, int day)
 | 
			
		||||
    public static Calendar AddCalendarEvent(this Calendar calendar, string description, int year, int month, int day, Style? customEventHighlightStyle = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (calendar is null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(calendar));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        calendar.CalendarEvents.Add(new CalendarEvent(description, year, month, day));
 | 
			
		||||
        calendar.CalendarEvents.Add(new CalendarEvent(description, year, month, day, customEventHighlightStyle));
 | 
			
		||||
        return calendar;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -65,7 +69,7 @@ public static class CalendarExtensions
 | 
			
		||||
    /// Sets the calendar's highlight <see cref="Style"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="calendar">The calendar.</param>
 | 
			
		||||
    /// <param name="style">The highlight style.</param>
 | 
			
		||||
    /// <param name="style">The default highlight style.</param>
 | 
			
		||||
    /// <returns>The same instance so that multiple calls can be chained.</returns>
 | 
			
		||||
    public static Calendar HighlightStyle(this Calendar calendar, Style? style)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -98,7 +98,7 @@ internal sealed class HtmlEncoder : IAnsiConsoleEncoder
 | 
			
		||||
            css.Add("font-weight: bold");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((style.Decoration & Decoration.Bold) != 0)
 | 
			
		||||
        if ((style.Decoration & Decoration.Italic) != 0)
 | 
			
		||||
        {
 | 
			
		||||
            css.Add("font-style: italic");
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ internal sealed class ListPrompt<T>
 | 
			
		||||
 | 
			
		||||
    public async Task<ListPromptState<T>> Show(
 | 
			
		||||
        ListPromptTree<T> tree,
 | 
			
		||||
        Func<T, string> converter,
 | 
			
		||||
        SelectionMode selectionMode,
 | 
			
		||||
        bool skipUnselectableItems,
 | 
			
		||||
        bool searchEnabled,
 | 
			
		||||
@@ -41,7 +42,12 @@ internal sealed class ListPrompt<T>
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var nodes = tree.Traverse().ToList();
 | 
			
		||||
        var state = new ListPromptState<T>(nodes, _strategy.CalculatePageSize(_console, nodes.Count, requestedPageSize), wrapAround, selectionMode, skipUnselectableItems, searchEnabled);
 | 
			
		||||
        if (nodes.Count == 0)
 | 
			
		||||
        {
 | 
			
		||||
            throw new InvalidOperationException("Cannot show an empty selection prompt. Please call the AddChoice() method to configure the prompt.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var state = new ListPromptState<T>(nodes, converter, _strategy.CalculatePageSize(_console, nodes.Count, requestedPageSize), wrapAround, selectionMode, skipUnselectableItems, searchEnabled);
 | 
			
		||||
        var hook = new ListPromptRenderHook<T>(_console, () => BuildRenderable(state));
 | 
			
		||||
 | 
			
		||||
        using (new RenderHookScope(_console, hook))
 | 
			
		||||
 
 | 
			
		||||
@@ -9,4 +9,15 @@ internal sealed class ListPromptConstants
 | 
			
		||||
    public const string InstructionsMarkup = "[grey](Press <space> to select, <enter> to accept)[/]";
 | 
			
		||||
    public const string MoreChoicesMarkup = "[grey](Move up and down to reveal more choices)[/]";
 | 
			
		||||
    public const string SearchPlaceholderMarkup = "[grey](Type to search)[/]";
 | 
			
		||||
 | 
			
		||||
    public static string GetSelectedCheckbox(bool isGroup, SelectionMode mode, Style? style = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (style != null)
 | 
			
		||||
        {
 | 
			
		||||
            return "[[" + $"[{style.ToMarkup()}]X[/]" + "]]";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return isGroup && mode == SelectionMode.Leaf
 | 
			
		||||
            ? GroupSelectedCheckbox : SelectedCheckbox;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,8 @@ namespace Spectre.Console;
 | 
			
		||||
internal sealed class ListPromptState<T>
 | 
			
		||||
    where T : notnull
 | 
			
		||||
{
 | 
			
		||||
    private readonly Func<T, string> _converter;
 | 
			
		||||
 | 
			
		||||
    public int Index { get; private set; }
 | 
			
		||||
    public int ItemCount => Items.Count;
 | 
			
		||||
    public int PageSize { get; }
 | 
			
		||||
@@ -16,8 +18,15 @@ internal sealed class ListPromptState<T>
 | 
			
		||||
    public ListPromptItem<T> Current => Items[Index];
 | 
			
		||||
    public string SearchText { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public ListPromptState(IReadOnlyList<ListPromptItem<T>> items, int pageSize, bool wrapAround, SelectionMode mode, bool skipUnselectableItems, bool searchEnabled)
 | 
			
		||||
    public ListPromptState(
 | 
			
		||||
        IReadOnlyList<ListPromptItem<T>> items,
 | 
			
		||||
        Func<T, string> converter,
 | 
			
		||||
        int pageSize, bool wrapAround,
 | 
			
		||||
        SelectionMode mode,
 | 
			
		||||
        bool skipUnselectableItems,
 | 
			
		||||
        bool searchEnabled)
 | 
			
		||||
    {
 | 
			
		||||
        _converter = converter ?? throw new ArgumentNullException(nameof(converter));
 | 
			
		||||
        Items = items;
 | 
			
		||||
        PageSize = pageSize;
 | 
			
		||||
        WrapAround = wrapAround;
 | 
			
		||||
@@ -126,7 +135,11 @@ internal sealed class ListPromptState<T>
 | 
			
		||||
            if (!char.IsControl(keyInfo.KeyChar))
 | 
			
		||||
            {
 | 
			
		||||
                search = SearchText + keyInfo.KeyChar;
 | 
			
		||||
                var item = Items.FirstOrDefault(x => x.Data.ToString()?.Contains(search, StringComparison.OrdinalIgnoreCase) == true && (!x.IsGroup || Mode != SelectionMode.Leaf));
 | 
			
		||||
 | 
			
		||||
                var item = Items.FirstOrDefault(x =>
 | 
			
		||||
                    _converter.Invoke(x.Data).Contains(search, StringComparison.OrdinalIgnoreCase)
 | 
			
		||||
                    && (!x.IsGroup || Mode != SelectionMode.Leaf));
 | 
			
		||||
 | 
			
		||||
                if (item != null)
 | 
			
		||||
                {
 | 
			
		||||
                    index = Items.IndexOf(item);
 | 
			
		||||
@@ -140,7 +153,10 @@ internal sealed class ListPromptState<T>
 | 
			
		||||
                    search = search.Substring(0, search.Length - 1);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var item = Items.FirstOrDefault(x => x.Data.ToString()?.Contains(search, StringComparison.OrdinalIgnoreCase) == true && (!x.IsGroup || Mode != SelectionMode.Leaf));
 | 
			
		||||
                var item = Items.FirstOrDefault(x =>
 | 
			
		||||
                    _converter.Invoke(x.Data).Contains(search, StringComparison.OrdinalIgnoreCase) &&
 | 
			
		||||
                    (!x.IsGroup || Mode != SelectionMode.Leaf));
 | 
			
		||||
 | 
			
		||||
                if (item != null)
 | 
			
		||||
                {
 | 
			
		||||
                    index = Items.IndexOf(item);
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,8 @@ public sealed class MultiSelectionPrompt<T> : IPrompt<List<T>>, IListPromptStrat
 | 
			
		||||
    {
 | 
			
		||||
        // Create the list prompt
 | 
			
		||||
        var prompt = new ListPrompt<T>(console, this);
 | 
			
		||||
        var result = await prompt.Show(Tree, Mode, false, false, PageSize, WrapAround, cancellationToken).ConfigureAwait(false);
 | 
			
		||||
        var converter = Converter ?? TypeConverterHelper.ConvertToString;
 | 
			
		||||
        var result = await prompt.Show(Tree, converter, Mode, false, false, PageSize, WrapAround, cancellationToken).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
        if (Mode == SelectionMode.Leaf)
 | 
			
		||||
        {
 | 
			
		||||
@@ -256,8 +257,7 @@ public sealed class MultiSelectionPrompt<T> : IPrompt<List<T>>, IListPromptStrat
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var checkbox = item.Node.IsSelected
 | 
			
		||||
                ? (item.Node.IsGroup && Mode == SelectionMode.Leaf
 | 
			
		||||
                    ? ListPromptConstants.GroupSelectedCheckbox : ListPromptConstants.SelectedCheckbox)
 | 
			
		||||
                ? ListPromptConstants.GetSelectedCheckbox(item.Node.IsGroup, Mode, HighlightStyle)
 | 
			
		||||
                : ListPromptConstants.Checkbox;
 | 
			
		||||
 | 
			
		||||
            grid.AddRow(new Markup(indent + prompt + " " + checkbox + " " + text, style));
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,8 @@ public sealed class SelectionPrompt<T> : IPrompt<T>, IListPromptStrategy<T>
 | 
			
		||||
    {
 | 
			
		||||
        // Create the list prompt
 | 
			
		||||
        var prompt = new ListPrompt<T>(console, this);
 | 
			
		||||
        var result = await prompt.Show(_tree, Mode, true, SearchEnabled, PageSize, WrapAround, cancellationToken).ConfigureAwait(false);
 | 
			
		||||
        var converter = Converter ?? TypeConverterHelper.ConvertToString;
 | 
			
		||||
        var result = await prompt.Show(_tree, converter, Mode, true, SearchEnabled, PageSize, WrapAround, cancellationToken).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
        // Return the selected item
 | 
			
		||||
        return result.Items[result.Index].Data;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,22 +2,22 @@
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFrameworks>net8.0;net7.0;net6.0;netstandard2.0</TargetFrameworks>
 | 
			
		||||
    <Nullable>enable</Nullable>
 | 
			
		||||
    <IsPackable>true</IsPackable>
 | 
			
		||||
    <NoWarn>SA1633</NoWarn>
 | 
			
		||||
    <DefineConstants>$(DefineConstants)TRACE;WCWIDTH_VISIBILITY_INTERNAL</DefineConstants>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
 | 
			
		||||
    <EmbeddedResource Include="Widgets\Figlet\Fonts\Standard.flf" />
 | 
			
		||||
    <None Remove="Widgets\Figlet\Fonts\Standard.flf" />
 | 
			
		||||
    <None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
 | 
			
		||||
    <InternalsVisibleTo Include="$(AssemblyName).Tests" />
 | 
			
		||||
  <ItemGroup Label="REMOVE THIS">
 | 
			
		||||
    <InternalsVisibleTo Include="$(AssemblyName).Tests"/>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Memory" Version="4.5.5" />
 | 
			
		||||
    <PackageReference Include="Wcwidth.Sources" Version="2.0.0">
 | 
			
		||||
  <ItemGroup Label="Standard Figlet font">
 | 
			
		||||
    <EmbeddedResource Include="Widgets\Figlet\Fonts\Standard.flf"/>
 | 
			
		||||
    <None Remove="Widgets\Figlet\Fonts\Standard.flf"/>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup Label="Dependencies">
 | 
			
		||||
    <PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Memory"/>
 | 
			
		||||
    <PackageReference Include="Wcwidth.Sources">
 | 
			
		||||
      <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
    </PackageReference>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
@@ -28,17 +28,12 @@
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
 | 
			
		||||
    <PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" PrivateAssets="all" />
 | 
			
		||||
    <PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[$(AnnotatedReferenceAssemblyVersion)]" />
 | 
			
		||||
    <PackageReference Include="Nullable" Version="1.3.1">
 | 
			
		||||
    <PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" PrivateAssets="all"/>
 | 
			
		||||
    <PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[$(AnnotatedReferenceAssemblyVersion)]"/>
 | 
			
		||||
    <PackageReference Include="Nullable">
 | 
			
		||||
      <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
    </PackageReference>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <DefineConstants>$(DefineConstants)TRACE;WCWIDTH_VISIBILITY_INTERNAL</DefineConstants>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
 
 | 
			
		||||
@@ -196,9 +196,10 @@ public sealed class Calendar : JustInTimeRenderable, IHasCulture, IHasTableBorde
 | 
			
		||||
        {
 | 
			
		||||
            if (weekdays[currentDay - 1] == weekday)
 | 
			
		||||
            {
 | 
			
		||||
                if (_calendarEvents.Any(e => e.Month == Month && e.Day == currentDay))
 | 
			
		||||
                var todayEvent = _calendarEvents.LastOrDefault(e => e.Month == Month && e.Day == currentDay);
 | 
			
		||||
                if (todayEvent != null)
 | 
			
		||||
                {
 | 
			
		||||
                    row.Add(new Markup(currentDay.ToString(CultureInfo.InvariantCulture) + "*", _highlightStyle));
 | 
			
		||||
                    row.Add(new Markup(currentDay.ToString(CultureInfo.InvariantCulture) + "*", todayEvent.CustomHighlightStyle ?? _highlightStyle));
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,14 +25,20 @@ public sealed class CalendarEvent
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Day { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the custom highlight style of the calendar event.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Style? CustomHighlightStyle { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Initializes a new instance of the <see cref="CalendarEvent"/> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="year">The year of the calendar event.</param>
 | 
			
		||||
    /// <param name="month">The month of the calendar event.</param>
 | 
			
		||||
    /// <param name="day">The day of the calendar event.</param>
 | 
			
		||||
    public CalendarEvent(int year, int month, int day)
 | 
			
		||||
        : this(string.Empty, year, month, day)
 | 
			
		||||
    /// <param name="customHighlightStyle">The custom highlight style of the calendar event.</param>
 | 
			
		||||
    public CalendarEvent(int year, int month, int day, Style? customHighlightStyle = null)
 | 
			
		||||
        : this(string.Empty, year, month, day, customHighlightStyle)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -43,11 +49,13 @@ public sealed class CalendarEvent
 | 
			
		||||
    /// <param name="year">The year of the calendar event.</param>
 | 
			
		||||
    /// <param name="month">The month of the calendar event.</param>
 | 
			
		||||
    /// <param name="day">The day of the calendar event.</param>
 | 
			
		||||
    public CalendarEvent(string description, int year, int month, int day)
 | 
			
		||||
    /// <param name="customHighlightStyle">The custom highlight style of the calendar event.</param>
 | 
			
		||||
    public CalendarEvent(string description, int year, int month, int day, Style? customHighlightStyle = null)
 | 
			
		||||
    {
 | 
			
		||||
        Description = description ?? string.Empty;
 | 
			
		||||
        Year = year;
 | 
			
		||||
        Month = month;
 | 
			
		||||
        Day = day;
 | 
			
		||||
        CustomHighlightStyle = customHighlightStyle;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -150,9 +150,9 @@ internal static class TableRenderer
 | 
			
		||||
                result.Add(Segment.LineBreak);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Show row separator?
 | 
			
		||||
            // Show row separator, if headers are hidden show separator after the first row
 | 
			
		||||
            if (context.Border.SupportsRowSeparator && context.ShowRowSeparators
 | 
			
		||||
                                                    && !isFirstRow && !isLastRow)
 | 
			
		||||
                                                    && (!isFirstRow || (isFirstRow && !context.ShowHeaders)) && !isLastRow)
 | 
			
		||||
            {
 | 
			
		||||
                var hasVisibleFootes = context is { ShowFooters: true, HasFooters: true };
 | 
			
		||||
                var isNextLastLine = index == context.Rows.Count - 2;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ namespace Spectre.Console;
 | 
			
		||||
public sealed class Tree : Renderable, IHasTreeNodes
 | 
			
		||||
{
 | 
			
		||||
    private readonly TreeNode _root;
 | 
			
		||||
    private bool _expanded = true;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets or sets the tree style.
 | 
			
		||||
@@ -26,7 +27,15 @@ public sealed class Tree : Renderable, IHasTreeNodes
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets or sets a value indicating whether or not the tree is expanded or not.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool Expanded { get; set; } = true;
 | 
			
		||||
    public bool Expanded
 | 
			
		||||
    {
 | 
			
		||||
        get => _expanded;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            _expanded = value;
 | 
			
		||||
            _root.Expand(value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Initializes a new instance of the <see cref="Tree"/> class.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user