Console ItemPreview, select item after delete
This commit is contained in:
@@ -5,4 +5,6 @@ namespace FileTime.App.Core.ViewModels.ItemPreview;
|
|||||||
public interface IElementPreviewViewModel : IItemPreviewViewModel
|
public interface IElementPreviewViewModel : IItemPreviewViewModel
|
||||||
{
|
{
|
||||||
ItemPreviewMode Mode { get; }
|
ItemPreviewMode Mode { get; }
|
||||||
|
string TextContent { get; }
|
||||||
|
string TextEncoding { get; }
|
||||||
}
|
}
|
||||||
@@ -6,20 +6,17 @@ namespace FileTime.App.Core.Services;
|
|||||||
|
|
||||||
public class ItemPreviewService : IItemPreviewService
|
public class ItemPreviewService : IItemPreviewService
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _serviceProvider;
|
|
||||||
private readonly IEnumerable<IItemPreviewProvider> _itemPreviewProviders;
|
private readonly IEnumerable<IItemPreviewProvider> _itemPreviewProviders;
|
||||||
public IDeclarativeProperty<IItemPreviewViewModel?> ItemPreview { get; }
|
public IDeclarativeProperty<IItemPreviewViewModel?> ItemPreview { get; }
|
||||||
|
|
||||||
public ItemPreviewService(
|
public ItemPreviewService(
|
||||||
IAppState appState,
|
IAppState appState,
|
||||||
IServiceProvider serviceProvider,
|
|
||||||
IEnumerable<IItemPreviewProvider> itemPreviewProviders)
|
IEnumerable<IItemPreviewProvider> itemPreviewProviders)
|
||||||
{
|
{
|
||||||
_serviceProvider = serviceProvider;
|
|
||||||
_itemPreviewProviders = itemPreviewProviders;
|
_itemPreviewProviders = itemPreviewProviders;
|
||||||
ItemPreview = appState
|
ItemPreview = appState
|
||||||
.SelectedTab
|
.SelectedTab
|
||||||
.Map(t => t.CurrentSelectedItem)
|
.Map(t => t?.CurrentSelectedItem)
|
||||||
.Switch()
|
.Switch()
|
||||||
.Debounce(TimeSpan.FromMilliseconds(250))
|
.Debounce(TimeSpan.FromMilliseconds(250))
|
||||||
.Map(async (item, _) =>
|
.Map(async (item, _) =>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
using FileTime.App.CommandPalette.ViewModels;
|
using FileTime.App.CommandPalette.ViewModels;
|
||||||
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.App.Core.ViewModels.Timeline;
|
using FileTime.App.Core.ViewModels.Timeline;
|
||||||
using FileTime.App.FrequencyNavigation.ViewModels;
|
using FileTime.App.FrequencyNavigation.ViewModels;
|
||||||
@@ -20,5 +21,6 @@ public interface IRootViewModel
|
|||||||
ITimelineViewModel TimelineViewModel { get; }
|
ITimelineViewModel TimelineViewModel { get; }
|
||||||
IDeclarativeProperty<VolumeSizeInfo?> VolumeSizeInfo { get; }
|
IDeclarativeProperty<VolumeSizeInfo?> VolumeSizeInfo { get; }
|
||||||
IFrequencyNavigationViewModel FrequencyNavigation { get; }
|
IFrequencyNavigationViewModel FrequencyNavigation { get; }
|
||||||
|
IItemPreviewService ItemPreviewService { get; }
|
||||||
event Action<IInputElement>? FocusReadInputElement;
|
event Action<IInputElement>? FocusReadInputElement;
|
||||||
}
|
}
|
||||||
@@ -117,6 +117,8 @@ public class App : IApplication
|
|||||||
|
|
||||||
Thread.Sleep(10);
|
Thread.Sleep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task.Run(async () => await _lifecycleService.ExitAsync()).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Render() => _applicationContext.RenderEngine.Run();
|
private void Render() => _applicationContext.RenderEngine.Run();
|
||||||
|
|||||||
110
src/ConsoleApp/FileTime.ConsoleUI.App/Controls/ItemPreviews.cs
Normal file
110
src/ConsoleApp/FileTime.ConsoleUI.App/Controls/ItemPreviews.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using FileTime.App.Core.Models;
|
||||||
|
using FileTime.App.Core.Services;
|
||||||
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
|
using FileTime.ConsoleUI.App.Styling;
|
||||||
|
using TerminalUI.Controls;
|
||||||
|
using TerminalUI.Extensions;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
using TerminalUI.ViewExtensions;
|
||||||
|
|
||||||
|
namespace FileTime.ConsoleUI.App.Controls;
|
||||||
|
|
||||||
|
public class ItemPreviews
|
||||||
|
{
|
||||||
|
private readonly ITheme _theme;
|
||||||
|
|
||||||
|
public ItemPreviews(ITheme theme)
|
||||||
|
{
|
||||||
|
_theme = theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IView<IRootViewModel> View()
|
||||||
|
{
|
||||||
|
var view = new Grid<IRootViewModel>()
|
||||||
|
{
|
||||||
|
ChildInitializer =
|
||||||
|
{
|
||||||
|
new TextBlock<IRootViewModel>
|
||||||
|
{
|
||||||
|
TextAlignment = TextAlignment.Center,
|
||||||
|
Text = "Empty",
|
||||||
|
Foreground = _theme.ErrorForegroundColor,
|
||||||
|
}.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.AppState.SelectedTab.Value.SelectedsChildren.Value.Count == 0,
|
||||||
|
t => t.IsVisible,
|
||||||
|
fallbackValue: false)),
|
||||||
|
ElementPreviews()
|
||||||
|
.WithDataContextBinding<IRootViewModel, IElementPreviewViewModel>(
|
||||||
|
dc => (IElementPreviewViewModel)dc.ItemPreviewService.ItemPreview.Value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IView<IElementPreviewViewModel> ElementPreviews()
|
||||||
|
{
|
||||||
|
var view = new Grid<IElementPreviewViewModel>
|
||||||
|
{
|
||||||
|
ChildInitializer =
|
||||||
|
{
|
||||||
|
new TextBlock<IElementPreviewViewModel>
|
||||||
|
{
|
||||||
|
TextAlignment = TextAlignment.Center,
|
||||||
|
Text = "Don't know how to preview this item.",
|
||||||
|
}.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.Mode == ItemPreviewMode.Unknown,
|
||||||
|
t => t.IsVisible,
|
||||||
|
v => v,
|
||||||
|
fallbackValue: false)),
|
||||||
|
new TextBlock<IElementPreviewViewModel>
|
||||||
|
{
|
||||||
|
TextAlignment = TextAlignment.Center,
|
||||||
|
Text = "Empty",
|
||||||
|
}.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.Mode == ItemPreviewMode.Empty,
|
||||||
|
t => t.IsVisible,
|
||||||
|
v => v,
|
||||||
|
fallbackValue: false)),
|
||||||
|
new Grid<IElementPreviewViewModel>
|
||||||
|
{
|
||||||
|
RowDefinitionsObject = "* Auto",
|
||||||
|
ChildInitializer =
|
||||||
|
{
|
||||||
|
new TextBlock<IElementPreviewViewModel>()
|
||||||
|
.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.TextContent,
|
||||||
|
t => t.Text)),
|
||||||
|
new TextBlock<IElementPreviewViewModel>
|
||||||
|
{
|
||||||
|
Extensions = {new GridPositionExtension(0, 1)}
|
||||||
|
}.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.TextEncoding,
|
||||||
|
t => t.Text,
|
||||||
|
v => $"Encoding: {v}"))
|
||||||
|
}
|
||||||
|
}.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.Mode == ItemPreviewMode.Text,
|
||||||
|
t => t.IsVisible,
|
||||||
|
v => v,
|
||||||
|
fallbackValue: false)),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
view.Bind(
|
||||||
|
view,
|
||||||
|
dc => dc.Name == ElementPreviewViewModel.PreviewName,
|
||||||
|
v => v.IsVisible,
|
||||||
|
v => v
|
||||||
|
);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ public class MainWindow
|
|||||||
private readonly FrequencyNavigation _frequencyNavigation;
|
private readonly FrequencyNavigation _frequencyNavigation;
|
||||||
private readonly Dialogs _dialogs;
|
private readonly Dialogs _dialogs;
|
||||||
private readonly Timeline _timeline;
|
private readonly Timeline _timeline;
|
||||||
|
private readonly ItemPreviews _itemPreviews;
|
||||||
private readonly Lazy<IView> _root;
|
private readonly Lazy<IView> _root;
|
||||||
|
|
||||||
|
|
||||||
@@ -44,7 +45,8 @@ public class MainWindow
|
|||||||
CommandPalette commandPalette,
|
CommandPalette commandPalette,
|
||||||
FrequencyNavigation frequencyNavigation,
|
FrequencyNavigation frequencyNavigation,
|
||||||
Dialogs dialogs,
|
Dialogs dialogs,
|
||||||
Timeline timeline)
|
Timeline timeline,
|
||||||
|
ItemPreviews itemPreviews)
|
||||||
{
|
{
|
||||||
_rootViewModel = rootViewModel;
|
_rootViewModel = rootViewModel;
|
||||||
_applicationContext = applicationContext;
|
_applicationContext = applicationContext;
|
||||||
@@ -53,6 +55,7 @@ public class MainWindow
|
|||||||
_frequencyNavigation = frequencyNavigation;
|
_frequencyNavigation = frequencyNavigation;
|
||||||
_dialogs = dialogs;
|
_dialogs = dialogs;
|
||||||
_timeline = timeline;
|
_timeline = timeline;
|
||||||
|
_itemPreviews = itemPreviews;
|
||||||
_root = new Lazy<IView>(Initialize);
|
_root = new Lazy<IView>(Initialize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +144,18 @@ public class MainWindow
|
|||||||
{
|
{
|
||||||
ParentsItemsView().WithExtension(new GridPositionExtension(0, 0)),
|
ParentsItemsView().WithExtension(new GridPositionExtension(0, 0)),
|
||||||
SelectedItemsView().WithExtension(new GridPositionExtension(1, 0)),
|
SelectedItemsView().WithExtension(new GridPositionExtension(1, 0)),
|
||||||
SelectedsItemsView().WithExtension(new GridPositionExtension(2, 0)),
|
new Grid<IRootViewModel>
|
||||||
|
{
|
||||||
|
Extensions =
|
||||||
|
{
|
||||||
|
new GridPositionExtension(2, 0)
|
||||||
|
},
|
||||||
|
ChildInitializer =
|
||||||
|
{
|
||||||
|
SelectedsItemsView(),
|
||||||
|
_itemPreviews.View()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new ItemsControl<IRootViewModel, string>
|
new ItemsControl<IRootViewModel, string>
|
||||||
@@ -347,15 +361,21 @@ public class MainWindow
|
|||||||
{
|
{
|
||||||
var list = new ListView<IRootViewModel, IItemViewModel>
|
var list = new ListView<IRootViewModel, IItemViewModel>
|
||||||
{
|
{
|
||||||
ListPadding = 8
|
ListPadding = 8,
|
||||||
|
ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions())
|
||||||
};
|
};
|
||||||
|
|
||||||
list.ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions());
|
|
||||||
|
|
||||||
list.Bind(
|
list.Bind(
|
||||||
list,
|
list,
|
||||||
root => root.AppState.SelectedTab.Value.SelectedsChildren.Value,
|
dc => dc.AppState.SelectedTab.Value.SelectedsChildren.Value.Count > 0,
|
||||||
v => v.ItemsSource);
|
l => l.IsVisible,
|
||||||
|
fallbackValue: false);
|
||||||
|
|
||||||
|
list.Bind(
|
||||||
|
list,
|
||||||
|
dc => dc.AppState.SelectedTab.Value.SelectedsChildren.Value,
|
||||||
|
v => v.ItemsSource,
|
||||||
|
fallbackValue: null);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -364,14 +384,13 @@ public class MainWindow
|
|||||||
{
|
{
|
||||||
var list = new ListView<IRootViewModel, IItemViewModel>
|
var list = new ListView<IRootViewModel, IItemViewModel>
|
||||||
{
|
{
|
||||||
ListPadding = 8
|
ListPadding = 8,
|
||||||
|
ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions())
|
||||||
};
|
};
|
||||||
|
|
||||||
list.ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions());
|
|
||||||
|
|
||||||
list.Bind(
|
list.Bind(
|
||||||
list,
|
list,
|
||||||
root => root.AppState.SelectedTab.Value.ParentsChildren.Value,
|
dc => dc.AppState.SelectedTab.Value.ParentsChildren.Value,
|
||||||
v => v.ItemsSource);
|
v => v.ItemsSource);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
using FileTime.App.CommandPalette.ViewModels;
|
using FileTime.App.CommandPalette.ViewModels;
|
||||||
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.App.Core.ViewModels.Timeline;
|
using FileTime.App.Core.ViewModels.Timeline;
|
||||||
using FileTime.App.FrequencyNavigation.ViewModels;
|
using FileTime.App.FrequencyNavigation.ViewModels;
|
||||||
@@ -17,6 +18,7 @@ public class RootViewModel : IRootViewModel
|
|||||||
public IConsoleAppState AppState { get; }
|
public IConsoleAppState AppState { get; }
|
||||||
public ICommandPaletteViewModel CommandPalette { get; }
|
public ICommandPaletteViewModel CommandPalette { get; }
|
||||||
public IFrequencyNavigationViewModel FrequencyNavigation { get; }
|
public IFrequencyNavigationViewModel FrequencyNavigation { get; }
|
||||||
|
public IItemPreviewService ItemPreviewService { get; }
|
||||||
public IDialogService DialogService { get; }
|
public IDialogService DialogService { get; }
|
||||||
public ITimelineViewModel TimelineViewModel { get; }
|
public ITimelineViewModel TimelineViewModel { get; }
|
||||||
public IDeclarativeProperty<VolumeSizeInfo?> VolumeSizeInfo { get;}
|
public IDeclarativeProperty<VolumeSizeInfo?> VolumeSizeInfo { get;}
|
||||||
@@ -29,7 +31,8 @@ public class RootViewModel : IRootViewModel
|
|||||||
ICommandPaletteViewModel commandPalette,
|
ICommandPaletteViewModel commandPalette,
|
||||||
IDialogService dialogService,
|
IDialogService dialogService,
|
||||||
ITimelineViewModel timelineViewModel,
|
ITimelineViewModel timelineViewModel,
|
||||||
IFrequencyNavigationViewModel frequencyNavigation)
|
IFrequencyNavigationViewModel frequencyNavigation,
|
||||||
|
IItemPreviewService itemPreviewService)
|
||||||
{
|
{
|
||||||
AppState = appState;
|
AppState = appState;
|
||||||
PossibleCommands = possibleCommands;
|
PossibleCommands = possibleCommands;
|
||||||
@@ -37,6 +40,7 @@ public class RootViewModel : IRootViewModel
|
|||||||
DialogService = dialogService;
|
DialogService = dialogService;
|
||||||
TimelineViewModel = timelineViewModel;
|
TimelineViewModel = timelineViewModel;
|
||||||
FrequencyNavigation = frequencyNavigation;
|
FrequencyNavigation = frequencyNavigation;
|
||||||
|
ItemPreviewService = itemPreviewService;
|
||||||
|
|
||||||
DialogService.ReadInput.PropertyChanged += (o, e) =>
|
DialogService.ReadInput.PropertyChanged += (o, e) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public static class Startup
|
|||||||
services.TryAddSingleton<Dialogs>();
|
services.TryAddSingleton<Dialogs>();
|
||||||
services.TryAddSingleton<Timeline>();
|
services.TryAddSingleton<Timeline>();
|
||||||
services.TryAddSingleton<FrequencyNavigation>();
|
services.TryAddSingleton<FrequencyNavigation>();
|
||||||
|
services.TryAddSingleton<ItemPreviews>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using CircularBuffer;
|
using CircularBuffer;
|
||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
@@ -14,6 +15,8 @@ namespace FileTime.Core.Services;
|
|||||||
|
|
||||||
public class Tab : ITab
|
public class Tab : ITab
|
||||||
{
|
{
|
||||||
|
private record LastItemSelectingContext(IContainer? CurrentLocationValue);
|
||||||
|
|
||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly ITabEvents _tabEvents;
|
private readonly ITabEvents _tabEvents;
|
||||||
private readonly DeclarativeProperty<IContainer?> _currentLocation = new();
|
private readonly DeclarativeProperty<IContainer?> _currentLocation = new();
|
||||||
@@ -22,9 +25,11 @@ public class Tab : ITab
|
|||||||
private readonly ObservableCollection<ItemFilter> _itemFilters = new();
|
private readonly ObservableCollection<ItemFilter> _itemFilters = new();
|
||||||
private readonly CircularBuffer<FullName> _history = new(20);
|
private readonly CircularBuffer<FullName> _history = new(20);
|
||||||
private readonly CircularBuffer<FullName> _future = new(20);
|
private readonly CircularBuffer<FullName> _future = new(20);
|
||||||
|
private readonly List<AbsolutePath> _selectedItemCandidates = new();
|
||||||
private AbsolutePath? _currentSelectedItemCached;
|
private AbsolutePath? _currentSelectedItemCached;
|
||||||
private PointInTime _currentPointInTime;
|
private PointInTime _currentPointInTime;
|
||||||
private CancellationTokenSource? _setCurrentLocationCancellationTokenSource;
|
private CancellationTokenSource? _setCurrentLocationCancellationTokenSource;
|
||||||
|
private LastItemSelectingContext? _lastItemSelectingContext;
|
||||||
|
|
||||||
public IDeclarativeProperty<IContainer?> CurrentLocation { get; }
|
public IDeclarativeProperty<IContainer?> CurrentLocation { get; }
|
||||||
public IDeclarativeProperty<ObservableCollection<IItem>?> CurrentItems { get; }
|
public IDeclarativeProperty<ObservableCollection<IItem>?> CurrentItems { get; }
|
||||||
@@ -81,47 +86,32 @@ public class Tab : ITab
|
|||||||
return Task.FromResult(items);
|
return Task.FromResult(items);
|
||||||
}
|
}
|
||||||
).CombineLatest(
|
).CombineLatest(
|
||||||
Ordering,
|
Ordering.Map(ordering =>
|
||||||
|
{
|
||||||
|
var (itemComparer, order) = ordering switch
|
||||||
|
{
|
||||||
|
ItemOrdering.Name => ((Expression<Func<IItem, IComparable>>) (i => i.DisplayName), ListSortDirection.Ascending),
|
||||||
|
ItemOrdering.NameDesc => (i => i.DisplayName, ListSortDirection.Descending),
|
||||||
|
ItemOrdering.CreationDate => (i => i.CreatedAt ?? DateTime.MinValue, ListSortDirection.Ascending),
|
||||||
|
ItemOrdering.CreationDateDesc => (i => i.CreatedAt ?? DateTime.MinValue, ListSortDirection.Descending),
|
||||||
|
ItemOrdering.LastModifyDate => (i => i.ModifiedAt ?? DateTime.MinValue, ListSortDirection.Ascending),
|
||||||
|
ItemOrdering.LastModifyDateDesc => (i => i.ModifiedAt ?? DateTime.MinValue, ListSortDirection.Descending),
|
||||||
|
ItemOrdering.Size => (i => GetSize(i), ListSortDirection.Ascending),
|
||||||
|
ItemOrdering.SizeDesc => (i => GetSize(i), ListSortDirection.Descending),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
|
||||||
|
return (itemComparer, order);
|
||||||
|
}),
|
||||||
(items, ordering) =>
|
(items, ordering) =>
|
||||||
{
|
{
|
||||||
if (items is null) return Task.FromResult<ObservableCollection<IItem>?>(null);
|
if (items is null) return Task.FromResult<ObservableCollection<IItem>?>(null);
|
||||||
|
|
||||||
ObservableCollection<IItem>? orderedItems = ordering switch
|
var (itemComparer, order) = ordering;
|
||||||
{
|
|
||||||
ItemOrdering.Name =>
|
ObservableCollection<IItem>? orderedItems = items
|
||||||
items
|
.Ordering(i => i.Type)
|
||||||
.Ordering(i => i.Type)
|
.ThenOrdering(itemComparer, order);
|
||||||
.ThenOrdering(i => i.DisplayName),
|
|
||||||
ItemOrdering.NameDesc =>
|
|
||||||
items
|
|
||||||
.Ordering(i => i.Type)
|
|
||||||
.ThenOrdering(i => i.DisplayName, ListSortDirection.Descending),
|
|
||||||
ItemOrdering.CreationDate =>
|
|
||||||
items
|
|
||||||
.Ordering(i => i.Type)
|
|
||||||
.ThenOrdering(i => i.CreatedAt),
|
|
||||||
ItemOrdering.CreationDateDesc =>
|
|
||||||
items
|
|
||||||
.Ordering(i => i.Type)
|
|
||||||
.ThenOrdering(i => i.CreatedAt, ListSortDirection.Descending),
|
|
||||||
ItemOrdering.LastModifyDate =>
|
|
||||||
items
|
|
||||||
.Ordering(i => i.Type)
|
|
||||||
.ThenOrdering(i => i.ModifiedAt),
|
|
||||||
ItemOrdering.LastModifyDateDesc =>
|
|
||||||
items
|
|
||||||
.Ordering(i => i.Type)
|
|
||||||
.ThenOrdering(i => i.ModifiedAt, ListSortDirection.Descending),
|
|
||||||
ItemOrdering.Size =>
|
|
||||||
items
|
|
||||||
.Ordering(i => i.Type)
|
|
||||||
.ThenOrdering(i => GetSize(i)),
|
|
||||||
ItemOrdering.SizeDesc =>
|
|
||||||
items
|
|
||||||
.Ordering(i => i.Type)
|
|
||||||
.ThenOrdering(i => GetSize(i), ListSortDirection.Descending),
|
|
||||||
_ => throw new NotImplementedException()
|
|
||||||
};
|
|
||||||
|
|
||||||
return Task.FromResult(orderedItems);
|
return Task.FromResult(orderedItems);
|
||||||
}
|
}
|
||||||
@@ -132,8 +122,25 @@ public class Tab : ITab
|
|||||||
_currentRequestItem.DistinctUntilChanged(),
|
_currentRequestItem.DistinctUntilChanged(),
|
||||||
(items, selected) =>
|
(items, selected) =>
|
||||||
{
|
{
|
||||||
if (selected != null && (items?.Any(i => i.FullName == selected.Path) ?? true)) return Task.FromResult<AbsolutePath?>(selected);
|
var itemSelectingContext = new LastItemSelectingContext(CurrentLocation.Value);
|
||||||
|
var lastItemSelectingContext = _lastItemSelectingContext;
|
||||||
|
_lastItemSelectingContext = itemSelectingContext;
|
||||||
if (items == null || items.Count == 0) return Task.FromResult<AbsolutePath?>(null);
|
if (items == null || items.Count == 0) return Task.FromResult<AbsolutePath?>(null);
|
||||||
|
if (selected != null)
|
||||||
|
{
|
||||||
|
if (items.Any(i => i.FullName == selected.Path))
|
||||||
|
return Task.FromResult<AbsolutePath?>(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastItemSelectingContext != null
|
||||||
|
&& itemSelectingContext == lastItemSelectingContext)
|
||||||
|
{
|
||||||
|
var candidate = _selectedItemCandidates.FirstOrDefault(c => items.Any(i => i.FullName?.Path == c.Path.Path));
|
||||||
|
if (candidate != null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Task.FromResult(GetSelectedItemByItems(items));
|
return Task.FromResult(GetSelectedItemByItems(items));
|
||||||
}).DistinctUntilChanged();
|
}).DistinctUntilChanged();
|
||||||
@@ -141,8 +148,31 @@ public class Tab : ITab
|
|||||||
CurrentSelectedItem.Subscribe(async (s, _) =>
|
CurrentSelectedItem.Subscribe(async (s, _) =>
|
||||||
{
|
{
|
||||||
_currentSelectedItemCached = s;
|
_currentSelectedItemCached = s;
|
||||||
|
|
||||||
await _currentRequestItem.SetValue(s);
|
await _currentRequestItem.SetValue(s);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DeclarativePropertyHelpers.CombineLatest(
|
||||||
|
CurrentItems,
|
||||||
|
CurrentSelectedItem,
|
||||||
|
(items, selected) =>
|
||||||
|
{
|
||||||
|
if(items is null || selected is null) return Task.FromResult<IEnumerable<AbsolutePath>?>(null);
|
||||||
|
var primaryCandidates = items.SkipWhile(i => i.FullName is {Path: var p} && p != selected.Path.Path).Skip(1);
|
||||||
|
var secondaryCandidates = items.TakeWhile(i => i.FullName is {Path: var p} && p != selected.Path.Path).Reverse();
|
||||||
|
var candidates = primaryCandidates
|
||||||
|
.Concat(secondaryCandidates)
|
||||||
|
.Select(c => new AbsolutePath(_timelessContentProvider, c));
|
||||||
|
|
||||||
|
return Task.FromResult(candidates);
|
||||||
|
})
|
||||||
|
.Subscribe(candidates =>
|
||||||
|
{
|
||||||
|
if(candidates is null) return;
|
||||||
|
|
||||||
|
_selectedItemCandidates.Clear();
|
||||||
|
_selectedItemCandidates.AddRange(candidates);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long GetSize(IItem item)
|
private static long GetSize(IItem item)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
|
xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
|
||||||
xmlns:interactions="using:FileTime.Core.Interactions"
|
xmlns:interactions="using:FileTime.Core.Interactions"
|
||||||
xmlns:itemPreview="clr-namespace:FileTime.App.Core.ViewModels.ItemPreview;assembly=FileTime.App.Core"
|
xmlns:itemPreview="clr-namespace:FileTime.App.Core.ViewModels.ItemPreview;assembly=FileTime.App.Core"
|
||||||
|
xmlns:itemPreviewInterface="clr-namespace:FileTime.App.Core.ViewModels.ItemPreview;assembly=FileTime.App.Core.Abstraction"
|
||||||
xmlns:local="using:FileTime.GuiApp.App.Views"
|
xmlns:local="using:FileTime.GuiApp.App.Views"
|
||||||
xmlns:local1="clr-namespace:FileTime.Providers.Local;assembly=FileTime.Providers.Local.Abstractions"
|
xmlns:local1="clr-namespace:FileTime.Providers.Local;assembly=FileTime.Providers.Local.Abstractions"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
@@ -557,7 +558,7 @@
|
|||||||
</TextBlock>
|
</TextBlock>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid IsVisible="{Binding AppState.SelectedTab.Value.SelectedsChildren.Value, Converter={x:Static ObjectConverters.IsNull}, ConverterParameter=0, FallbackValue=False}" RowDefinitions="Auto, Auto">
|
<Grid IsVisible="{Binding AppState.SelectedTab.Value.CurrentSelectedItem.Value.BaseItem.Exceptions.Count, Converter={StaticResource GreaterThanConverter}, ConverterParameter=0, FallbackValue=False}" RowDefinitions="Auto, Auto">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Foreground="{DynamicResource ErrorBrush}"
|
Foreground="{DynamicResource ErrorBrush}"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@@ -579,7 +580,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid DataContext="{Binding ItemPreviewService.ItemPreview.Value}" IsVisible="{Binding FallbackValue=false, Converter={x:Static ObjectConverters.IsNotNull}}">
|
<Grid DataContext="{Binding ItemPreviewService.ItemPreview.Value}" IsVisible="{Binding FallbackValue=false, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
<Grid IsVisible="{Binding Name, FallbackValue=false, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static itemPreview:ElementPreviewViewModel.PreviewName}}">
|
<Grid IsVisible="{Binding Name, FallbackValue=false, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static itemPreview:ElementPreviewViewModel.PreviewName}}">
|
||||||
<Grid x:DataType="itemPreview:ElementPreviewViewModel">
|
<Grid x:DataType="itemPreviewInterface:IElementPreviewViewModel">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Unknown}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}"
|
IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Unknown}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}"
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ public abstract class ChildCollectionView<TConcrete, T>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void AddChild(IView child)
|
||||||
|
{
|
||||||
|
base.AddChild(child);
|
||||||
|
_children.Add(child);
|
||||||
|
}
|
||||||
|
|
||||||
public override TChild AddChild<TChild>(TChild child)
|
public override TChild AddChild<TChild>(TChild child)
|
||||||
{
|
{
|
||||||
child = base.AddChild(child);
|
child = base.AddChild(child);
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using TerminalUI.Extensions;
|
||||||
|
|
||||||
namespace TerminalUI.Controls;
|
namespace TerminalUI.Controls;
|
||||||
|
|
||||||
public record ChildWithDataContextMapper<TSourceDataContext, TTargetDataContext>(IView<TTargetDataContext> Child, Func<TSourceDataContext?, TTargetDataContext?> DataContextMapper);
|
public record ChildWithDataContextMapper<TSourceDataContext, TTargetDataContext>(IView<TTargetDataContext> Child, Func<TSourceDataContext?, TTargetDataContext?> DataContextMapper);
|
||||||
|
public record ChildWithDataContextBinding<TSourceDataContext, TTargetDataContext>(IView<TTargetDataContext> Child, Expression<Func<TSourceDataContext?, TTargetDataContext?>> DataContextExpression);
|
||||||
|
|
||||||
public class ChildInitializer<T> : IEnumerable<IView>
|
public class ChildInitializer<T> : IEnumerable<IView>
|
||||||
{
|
{
|
||||||
@@ -18,6 +21,16 @@ public class ChildInitializer<T> : IEnumerable<IView>
|
|||||||
public void Add<TDataContext>(ChildWithDataContextMapper<T, TDataContext> item)
|
public void Add<TDataContext>(ChildWithDataContextMapper<T, TDataContext> item)
|
||||||
=> _childContainer.AddChild(item.Child, item.DataContextMapper);
|
=> _childContainer.AddChild(item.Child, item.DataContextMapper);
|
||||||
|
|
||||||
|
public void Add<TDataContext>(ChildWithDataContextBinding<T, TDataContext> item)
|
||||||
|
{
|
||||||
|
item.Child.Bind(
|
||||||
|
_childContainer,
|
||||||
|
item.DataContextExpression,
|
||||||
|
c => c.DataContext
|
||||||
|
);
|
||||||
|
_childContainer.AddChild(item.Child);
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerator<IView> GetEnumerator() => _childContainer.Children.GetEnumerator();
|
public IEnumerator<IView> GetEnumerator() => _childContainer.Children.GetEnumerator();
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public interface IView<T> : IView
|
|||||||
TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
||||||
where TChild : IView<TDataContext>, new();
|
where TChild : IView<TDataContext>, new();
|
||||||
|
|
||||||
|
void AddChild(IView child);
|
||||||
TChild AddChild<TChild>(TChild child) where TChild : IView<T>;
|
TChild AddChild<TChild>(TChild child) where TChild : IView<T>;
|
||||||
|
|
||||||
TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||||
|
|||||||
@@ -26,12 +26,14 @@ public sealed partial class TextBlock<T> : View<TextBlock<T>, T>, IDisplayView
|
|||||||
[Notify] private string? _text = string.Empty;
|
[Notify] private string? _text = string.Empty;
|
||||||
[Notify] private TextAlignment _textAlignment = TextAlignment.Left;
|
[Notify] private TextAlignment _textAlignment = TextAlignment.Left;
|
||||||
[Notify] private ITextFormat? _textFormat;
|
[Notify] private ITextFormat? _textFormat;
|
||||||
|
[Notify] private int _textStartIndex;
|
||||||
|
|
||||||
public TextBlock()
|
public TextBlock()
|
||||||
{
|
{
|
||||||
RerenderProperties.Add(nameof(Text));
|
RerenderProperties.Add(nameof(Text));
|
||||||
RerenderProperties.Add(nameof(TextAlignment));
|
RerenderProperties.Add(nameof(TextAlignment));
|
||||||
RerenderProperties.Add(nameof(TextFormat));
|
RerenderProperties.Add(nameof(TextFormat));
|
||||||
|
RerenderProperties.Add(nameof(TextStartIndex));
|
||||||
|
|
||||||
((INotifyPropertyChanged) this).PropertyChanged += (o, e) =>
|
((INotifyPropertyChanged) this).PropertyChanged += (o, e) =>
|
||||||
{
|
{
|
||||||
@@ -78,7 +80,17 @@ public sealed partial class TextBlock<T> : View<TextBlock<T>, T>, IDisplayView
|
|||||||
|
|
||||||
SetStyleColor(renderContext, foreground, background, _textFormat);
|
SetStyleColor(renderContext, foreground, background, _textFormat);
|
||||||
|
|
||||||
RenderText(_textLines, renderContext, position, size, skipRender, TransformText);
|
var textLines = _textLines;
|
||||||
|
if (_textStartIndex < _textLines.Length)
|
||||||
|
{
|
||||||
|
textLines = _textLines[_textStartIndex..];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_textStartIndex = _textLines.Length - size.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderText(textLines, renderContext, position, size, skipRender, TransformText);
|
||||||
|
|
||||||
return !skipRender;
|
return !skipRender;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -376,6 +376,12 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void AddChild(IView child)
|
||||||
|
{
|
||||||
|
Debug.Assert(child != null);
|
||||||
|
SetupNewChild(child, null);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
public virtual TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||||
where TChild : IView<TDataContext>
|
where TChild : IView<TDataContext>
|
||||||
{
|
{
|
||||||
@@ -387,15 +393,18 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupNewChild(IView child, IDisposable dataContextmapper)
|
private void SetupNewChild(IView child, IDisposable? dataContextMapper)
|
||||||
{
|
{
|
||||||
child.ApplicationContext = ApplicationContext;
|
child.ApplicationContext = ApplicationContext;
|
||||||
child.Attached = Attached;
|
child.Attached = Attached;
|
||||||
child.VisualParent = this;
|
child.VisualParent = this;
|
||||||
VisualChildren.Add(child);
|
VisualChildren.Add(child);
|
||||||
|
|
||||||
AddDisposable(dataContextmapper);
|
if (dataContextMapper is not null)
|
||||||
child.AddDisposable(dataContextmapper);
|
{
|
||||||
|
AddDisposable(dataContextMapper);
|
||||||
|
child.AddDisposable(dataContextMapper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void RemoveChild<TDataContext>(IView<TDataContext> child)
|
public virtual void RemoveChild<TDataContext>(IView<TDataContext> child)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Linq.Expressions;
|
using System.Collections;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
namespace TerminalUI.ExpressionTrackers;
|
namespace TerminalUI.ExpressionTrackers;
|
||||||
|
|
||||||
@@ -26,6 +27,10 @@ public class BinaryTracker : ExpressionTrackerBase
|
|||||||
{
|
{
|
||||||
ExpressionType.Equal => (v1, v2) => Equals(v1, v2),
|
ExpressionType.Equal => (v1, v2) => Equals(v1, v2),
|
||||||
ExpressionType.NotEqual => (v1, v2) => !Equals(v1, v2),
|
ExpressionType.NotEqual => (v1, v2) => !Equals(v1, v2),
|
||||||
|
ExpressionType.GreaterThan => (v1, v2) => Comparer.Default.Compare(v1, v2) > 0,
|
||||||
|
ExpressionType.GreaterThanOrEqual => (v1, v2) => Comparer.Default.Compare(v1, v2) >= 0,
|
||||||
|
ExpressionType.LessThan => (v1, v2) => Comparer.Default.Compare(v1, v2) < 0,
|
||||||
|
ExpressionType.LessThanOrEqual => (v1, v2) => Comparer.Default.Compare(v1, v2) <= 0,
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ public static class ViewExtensions
|
|||||||
Func<TSourceDataContext?, TTargetDataContext?> dataContextMapper)
|
Func<TSourceDataContext?, TTargetDataContext?> dataContextMapper)
|
||||||
=> new(view, dataContextMapper);
|
=> new(view, dataContextMapper);
|
||||||
|
|
||||||
|
public static ChildWithDataContextBinding<TSourceDataContext, TTargetDataContext> WithDataContextBinding<TSourceDataContext, TTargetDataContext>(
|
||||||
|
this IView<TTargetDataContext> view,
|
||||||
|
Expression<Func<TSourceDataContext?, TTargetDataContext?>> dataContextMapper)
|
||||||
|
=> new(view, dataContextMapper);
|
||||||
|
|
||||||
public static TView Setup<TView>(this TView view, Action<TView> action)
|
public static TView Setup<TView>(this TView view, Action<TView> action)
|
||||||
{
|
{
|
||||||
action(view);
|
action(view);
|
||||||
|
|||||||
Reference in New Issue
Block a user