Refactor, Design, ContextMenu
This commit is contained in:
@@ -2,58 +2,11 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:FileTime.Avalonia"
|
xmlns:local="using:FileTime.Avalonia"
|
||||||
xmlns:converters="using:FileTime.Avalonia.Converters"
|
xmlns:converters="using:FileTime.Avalonia.Converters"
|
||||||
|
xmlns:views="using:FileTime.Avalonia.Views"
|
||||||
x:Class="FileTime.Avalonia.App">
|
x:Class="FileTime.Avalonia.App">
|
||||||
<Application.DataTemplates>
|
<Application.DataTemplates>
|
||||||
</Application.DataTemplates>
|
</Application.DataTemplates>
|
||||||
|
|
||||||
<Application.Styles>
|
|
||||||
<FluentTheme Mode="Light"/>
|
|
||||||
|
|
||||||
<Style Selector="TextBlock">
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}"/>
|
|
||||||
</Style>
|
|
||||||
<Style Selector="TextBlock.SmallText">
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
</Style>
|
|
||||||
<Style Selector="ListBox.ContentListView">
|
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
|
||||||
<Setter Property="ItemTemplate">
|
|
||||||
<Setter.Value>
|
|
||||||
<DataTemplate>
|
|
||||||
<Grid ColumnDefinitions="Auto,*">
|
|
||||||
<Image
|
|
||||||
Grid.RowSpan="2"
|
|
||||||
Width="20"
|
|
||||||
Height="20"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Source="{SvgImage /Assets/material/folder.svg}" />
|
|
||||||
|
|
||||||
<ItemsControl
|
|
||||||
Grid.Column="1"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Items="{Binding DisplayName}">
|
|
||||||
<ItemsControl.ItemsPanel>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<StackPanel Orientation="Horizontal" />
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</ItemsControl.ItemsPanel>
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Grid>
|
|
||||||
<TextBlock
|
|
||||||
Text="{Binding Text}" />
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</Application.Styles>
|
|
||||||
|
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<Color x:Key="AppBackgroundColor">#073642</Color>
|
<Color x:Key="AppBackgroundColor">#073642</Color>
|
||||||
@@ -64,6 +17,8 @@
|
|||||||
<Color x:Key="SelectedItemBackgroundColor">#93a1a1</Color>
|
<Color x:Key="SelectedItemBackgroundColor">#93a1a1</Color>
|
||||||
|
|
||||||
<Color x:Key="ForegroundColor">#93a1a1</Color>
|
<Color x:Key="ForegroundColor">#93a1a1</Color>
|
||||||
|
<Color x:Key="AccentForegroundColor">#268bd2</Color>
|
||||||
|
<Color x:Key="LightForegroundColor">#7793a1a1</Color>
|
||||||
<Color x:Key="AlternativeForegroundColor">#93a1a1</Color>
|
<Color x:Key="AlternativeForegroundColor">#93a1a1</Color>
|
||||||
<Color x:Key="SelectedItemForegroundColor">#073642</Color>
|
<Color x:Key="SelectedItemForegroundColor">#073642</Color>
|
||||||
|
|
||||||
@@ -97,6 +52,12 @@
|
|||||||
<SolidColorBrush
|
<SolidColorBrush
|
||||||
x:Key="ForegroundBrush"
|
x:Key="ForegroundBrush"
|
||||||
Color="{DynamicResource ForegroundColor}" />
|
Color="{DynamicResource ForegroundColor}" />
|
||||||
|
<SolidColorBrush
|
||||||
|
x:Key="AccentForegroundBrush"
|
||||||
|
Color="{DynamicResource AccentForegroundColor}" />
|
||||||
|
<SolidColorBrush
|
||||||
|
x:Key="LightForegroundBrush"
|
||||||
|
Color="{DynamicResource LightForegroundColor}" />
|
||||||
<SolidColorBrush
|
<SolidColorBrush
|
||||||
x:Key="AlternativeForegroundBrush"
|
x:Key="AlternativeForegroundBrush"
|
||||||
Color="{DynamicResource AlternativeForegroundColor}" />
|
Color="{DynamicResource AlternativeForegroundColor}" />
|
||||||
@@ -108,11 +69,48 @@
|
|||||||
x:Key="ErrorBrush"
|
x:Key="ErrorBrush"
|
||||||
Color="{DynamicResource ErrorColor}" />
|
Color="{DynamicResource ErrorColor}" />
|
||||||
|
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="SystemControlHighlightListAccentLowBrush" Color="{DynamicResource SelectedItemBackgroundColor}" />
|
||||||
|
|
||||||
<converters:FormatSizeConverter x:Key="FormatSizeConverter"/>
|
<converters:FormatSizeConverter x:Key="FormatSizeConverter"/>
|
||||||
<converters:CompareConverter x:Key="EqualityConverter"/>
|
<converters:CompareConverter x:Key="EqualityConverter"/>
|
||||||
<converters:CompareConverter x:Key="NotEqualsConverter" ComparisonCondition="{x:Static converters:ComparisonCondition.NotEqual}"/>
|
<converters:CompareConverter x:Key="NotEqualsConverter" ComparisonCondition="{x:Static converters:ComparisonCondition.NotEqual}"/>
|
||||||
|
<converters:SplitStringConverter x:Key="SplitStringConverter" />
|
||||||
|
<converters:ItemViewModeToBrushConverter
|
||||||
|
x:Key="ItemViewModeToForegroundConverter"
|
||||||
|
DefaultBrush="{StaticResource ForegroundBrush}"
|
||||||
|
AlternativeBrush="{StaticResource AlternativeForegroundBrush}"
|
||||||
|
SelectedBrush="{StaticResource SelectedItemForegroundBrush}"/>
|
||||||
|
<converters:ItemViewModeToBrushConverter
|
||||||
|
x:Key="ItemViewModeToBackgroundConverter"
|
||||||
|
DefaultBrush="{StaticResource ItemBackgroundBrush}"
|
||||||
|
AlternativeBrush="{StaticResource AlternativeItemBackgroundBrush}"
|
||||||
|
SelectedBrush="{StaticResource SelectedItemBackgroundBrush}"/>
|
||||||
|
<converters:ContextMenuGenerator x:Key="ContextMenuGenerator"/>
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme Mode="Light"/>
|
||||||
|
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}"/>
|
||||||
|
<Setter Property="FontSize" Value="16"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TextBlock.SmallText">
|
||||||
|
<Setter Property="FontSize" Value="16"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.ContentListView">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox.ContentListView > ListBoxItem">
|
||||||
|
<Setter Property="Margin" Value="0"/>
|
||||||
|
<Setter Property="Padding" Value="0"/>
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="ContextMenu">
|
||||||
|
<ContextMenu Items="{Binding Converter={StaticResource ContextMenuGenerator}}"/>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Application.Styles>
|
||||||
</Application>
|
</Application>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using MvvmGen;
|
using MvvmGen;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace FileTime.Avalonia.Application
|
namespace FileTime.Avalonia.Application
|
||||||
@@ -10,8 +11,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
public partial class AppState
|
public partial class AppState
|
||||||
{
|
{
|
||||||
[Property]
|
[Property]
|
||||||
[PropertyCallMethod(nameof(TabsChanged))]
|
private ObservableCollection<TabContainer> _tabs = new ObservableCollection<TabContainer>();
|
||||||
private List<TabContainer> _tabs = new List<TabContainer>();
|
|
||||||
|
|
||||||
[Property]
|
[Property]
|
||||||
private TabContainer _selectedTab;
|
private TabContainer _selectedTab;
|
||||||
@@ -22,9 +22,9 @@ namespace FileTime.Avalonia.Application
|
|||||||
[Property]
|
[Property]
|
||||||
private string _rapidTravelText = "";
|
private string _rapidTravelText = "";
|
||||||
|
|
||||||
private void TabsChanged()
|
partial void OnInitialize()
|
||||||
{
|
{
|
||||||
SelectedTab ??= Tabs[0];
|
_tabs.CollectionChanged += (o, e) => SelectedTab ??= Tabs.Count > 0 ? Tabs[0] : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ namespace FileTime.Avalonia.Application
|
|||||||
[Property]
|
[Property]
|
||||||
private ContainerViewModel _childContainer;
|
private ContainerViewModel _childContainer;
|
||||||
|
|
||||||
|
[Property]
|
||||||
|
private int _tabNumber;
|
||||||
|
|
||||||
|
[Property]
|
||||||
|
private bool _isSelected;
|
||||||
|
|
||||||
private IItemViewModel? _selectedItem;
|
private IItemViewModel? _selectedItem;
|
||||||
|
|
||||||
public IItemViewModel? SelectedItem
|
public IItemViewModel? SelectedItem
|
||||||
@@ -36,7 +42,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
get => _selectedItem;
|
get => _selectedItem;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_selectedItem != value && value != null)
|
if (_selectedItem != value)// && value != null
|
||||||
{
|
{
|
||||||
_selectedItem = value;
|
_selectedItem = value;
|
||||||
OnPropertyChanged("SelectedItem");
|
OnPropertyChanged("SelectedItem");
|
||||||
@@ -45,8 +51,9 @@ namespace FileTime.Avalonia.Application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Init()
|
public async Task Init(int tabNumber)
|
||||||
{
|
{
|
||||||
|
TabNumber = tabNumber;
|
||||||
Tab.CurrentLocationChanged.Add(Tab_CurrentLocationChanged);
|
Tab.CurrentLocationChanged.Add(Tab_CurrentLocationChanged);
|
||||||
Tab.CurrentSelectedItemChanged.Add(Tab_CurrentSelectedItemChanged);
|
Tab.CurrentSelectedItemChanged.Add(Tab_CurrentSelectedItemChanged);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using FileTime.Avalonia.Services;
|
||||||
|
using FileTime.Avalonia.ViewModels;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.Converters
|
||||||
|
{
|
||||||
|
public class ContextMenuGenerator : IValueConverter
|
||||||
|
{
|
||||||
|
private readonly IContextMenuProvider _contextMenuProvider;
|
||||||
|
|
||||||
|
public ContextMenuGenerator()
|
||||||
|
{
|
||||||
|
_contextMenuProvider = App.ServiceProvider.GetService<IContextMenuProvider>() ?? throw new Exception($"No {nameof(IContextMenuProvider)} is registered.");
|
||||||
|
}
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is ContainerViewModel containerViewModel)
|
||||||
|
{
|
||||||
|
return _contextMenuProvider.GetContextMenuForFolder(containerViewModel.Container);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new object[] { new MenuItem() { Header = "asd" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using FileTime.Avalonia.ViewModels;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.Converters
|
||||||
|
{
|
||||||
|
public class ItemViewModeToBrushConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public Brush? DefaultBrush { get; set; }
|
||||||
|
public Brush? AlternativeBrush { get; set; }
|
||||||
|
public Brush? SelectedBrush { get; set; }
|
||||||
|
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is ItemViewMode viewMode)
|
||||||
|
{
|
||||||
|
return viewMode switch
|
||||||
|
{
|
||||||
|
ItemViewMode.Default => DefaultBrush,
|
||||||
|
ItemViewMode.Alternative => AlternativeBrush,
|
||||||
|
ItemViewMode.Selected => SelectedBrush,
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.Converters
|
||||||
|
{
|
||||||
|
public class SplitStringConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is string path && parameter is string separator)
|
||||||
|
{
|
||||||
|
return path.Split(separator);
|
||||||
|
}
|
||||||
|
else if (value is string path2 && parameter is char separator2)
|
||||||
|
{
|
||||||
|
return path2.Split(separator2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.Services
|
||||||
|
{
|
||||||
|
public interface IContextMenuProvider
|
||||||
|
{
|
||||||
|
List<object> GetContextMenuForFolder(IContainer container);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,7 @@ using FileTime.Avalonia.Application;
|
|||||||
using FileTime.Avalonia.Models;
|
using FileTime.Avalonia.Models;
|
||||||
using FileTime.Avalonia.ViewModels;
|
using FileTime.Avalonia.ViewModels;
|
||||||
using MvvmGen;
|
using MvvmGen;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace FileTime.Avalonia.Services
|
namespace FileTime.Avalonia.Services
|
||||||
{
|
{
|
||||||
@@ -35,8 +33,7 @@ namespace FileTime.Avalonia.Services
|
|||||||
nameParts.Add(new ItemNamePart(before));
|
nameParts.Add(new ItemNamePart(before));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: underline
|
nameParts.Add(new ItemNamePart(rapidTravel) { TextDecorations = TextDecorations.Underline });
|
||||||
nameParts.Add(new ItemNamePart(rapidTravel) { /*TextDecorations = new TextDecorationCollection() {new TextDecoration() }*/ });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nameLeft.Length > 0)
|
if (nameLeft.Length > 0)
|
||||||
|
|||||||
@@ -0,0 +1,267 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Providers.Local;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
#pragma warning disable CA1416
|
||||||
|
namespace FileTime.Avalonia.Services
|
||||||
|
{
|
||||||
|
public class WindowsContextMenuProvider : IContextMenuProvider
|
||||||
|
{
|
||||||
|
public List<object> GetContextMenuForFolder(IContainer container)
|
||||||
|
{
|
||||||
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) throw new NotSupportedException();
|
||||||
|
|
||||||
|
var menuItems = new List<object>();
|
||||||
|
|
||||||
|
if (container is LocalFolder localFolder)
|
||||||
|
{
|
||||||
|
ProcessKey(Registry.ClassesRoot.OpenSubKey("Directory"), menuItems, localFolder.Directory.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessKey(RegistryKey? contextMenuContainer, List<object> menuItems, string folderPath)
|
||||||
|
{
|
||||||
|
var shell = contextMenuContainer?.OpenSubKey("shell");
|
||||||
|
if (shell == null) return;
|
||||||
|
|
||||||
|
var shellSubKeys = shell.GetSubKeyNames();
|
||||||
|
|
||||||
|
foreach (var shellKey in shellSubKeys.Select(k => shell.OpenSubKey(k)).OfType<RegistryKey>())
|
||||||
|
{
|
||||||
|
var textBase = shellKey.GetValue(null) as string ?? shellKey.GetValue("MUIVerb") as string;
|
||||||
|
|
||||||
|
if (textBase == null) continue;
|
||||||
|
|
||||||
|
string? text = null;
|
||||||
|
if (textBase.StartsWith("@"))
|
||||||
|
{
|
||||||
|
var parts = textBase[1..].Split(',');
|
||||||
|
if (parts.Length == 2 && long.TryParse(parts[1], out var parsedResourceId))
|
||||||
|
{
|
||||||
|
if (parsedResourceId < 0) parsedResourceId *= -1;
|
||||||
|
|
||||||
|
text = GetStringResource(parts[0], (uint)parsedResourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text = textBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text != null)
|
||||||
|
{
|
||||||
|
text = text.Replace("&", "");
|
||||||
|
|
||||||
|
if (shellKey.GetSubKeyNames().Contains("command") && shellKey.OpenSubKey("command")?.GetValue(null) is string commandString)
|
||||||
|
{
|
||||||
|
var item = new MenuItem() { Header = text };
|
||||||
|
item.Click += (o, e) => MenuItemClick(folderPath, commandString);
|
||||||
|
menuItems.Add(item);
|
||||||
|
}
|
||||||
|
else if (shellKey.GetValue("ExtendedSubCommandsKey") is string extendedCommands)
|
||||||
|
{
|
||||||
|
var rootMenu = new MenuItem() { Header = text };
|
||||||
|
var rootMenuItems = new List<object>();
|
||||||
|
|
||||||
|
ProcessKey(Registry.ClassesRoot.OpenSubKey(extendedCommands), rootMenuItems, folderPath);
|
||||||
|
|
||||||
|
rootMenu.Items = rootMenuItems.ToArray();
|
||||||
|
menuItems.Add(rootMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MenuItemClick(string folderPath, string commandString)
|
||||||
|
{
|
||||||
|
var commandPartsWithoutAp = commandString.Split('\"').ToList();
|
||||||
|
var commandParts = new List<List<string>>();
|
||||||
|
|
||||||
|
for (var i = 0; i < commandPartsWithoutAp.Count; i++)
|
||||||
|
{
|
||||||
|
if (i % 2 == 0)
|
||||||
|
{
|
||||||
|
commandParts.Add(commandPartsWithoutAp[i].Split(' ').ToList());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
commandParts.Add(new List<string> { commandPartsWithoutAp[i] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < commandParts.Count; i++)
|
||||||
|
{
|
||||||
|
for (var i2 = 0; i2 < commandParts[i].Count; i2++)
|
||||||
|
{
|
||||||
|
/*var commandPart = commandParts[i][i2];
|
||||||
|
|
||||||
|
if (commandPart == "%1" || commandPart == "%V") commandParts[i][i2] = folderPath;*/
|
||||||
|
|
||||||
|
commandParts[i][i2] = commandParts[i][i2].Replace("%1", folderPath).Replace("%V", folderPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandPartsWithoutEmpty = commandParts.SelectMany(c => c).Where(c => !string.IsNullOrWhiteSpace(c)).ToList();
|
||||||
|
|
||||||
|
if (commandPartsWithoutEmpty.Count == 1)
|
||||||
|
{
|
||||||
|
Process.Start(commandPartsWithoutEmpty[0]);
|
||||||
|
}
|
||||||
|
else if (commandPartsWithoutEmpty.Count > 1)
|
||||||
|
{
|
||||||
|
var paramStartIndex1 = -1;
|
||||||
|
var paramStartIndex2 = -1;
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
for (var x = 0; x < commandParts.Count && paramStartIndex1 == -1; x++)
|
||||||
|
{
|
||||||
|
for (var y = 0; y < commandParts[x].Count; y++)
|
||||||
|
{
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
paramStartIndex1 = x;
|
||||||
|
paramStartIndex2 = y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandParts[x][y] == commandPartsWithoutEmpty[0])
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var arguments = SumList(commandParts, paramStartIndex1, paramStartIndex2);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var process = new Process();
|
||||||
|
process.StartInfo.FileName = commandPartsWithoutEmpty[0];
|
||||||
|
process.StartInfo.Arguments = arguments;
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (commandParts[0].Count > 0)
|
||||||
|
{
|
||||||
|
var executable = "";
|
||||||
|
var lastExecutablePart = 0;
|
||||||
|
for (lastExecutablePart = 0; !File.Exists(executable) && lastExecutablePart < commandParts[0].Count; lastExecutablePart++)
|
||||||
|
{
|
||||||
|
executable += (lastExecutablePart == 0 ? "" : " ") + commandParts[0][lastExecutablePart];
|
||||||
|
}
|
||||||
|
|
||||||
|
lastExecutablePart--;
|
||||||
|
|
||||||
|
if (File.Exists(executable))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var (paramStartX, paramStartY) = GetCoordinatesFrom(commandParts, 1, 0, lastExecutablePart);
|
||||||
|
arguments = SumList(commandParts, paramStartX, paramStartY);
|
||||||
|
|
||||||
|
var process = new Process();
|
||||||
|
process.StartInfo.FileName = executable;
|
||||||
|
process.StartInfo.Arguments = arguments;
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
//TODO: error message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO: ELSE error message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SumList(List<List<string>> data, int paramStartIndex1, int paramStartIndex2)
|
||||||
|
{
|
||||||
|
var result = "";
|
||||||
|
|
||||||
|
for (var x = paramStartIndex1; x < data.Count; x++)
|
||||||
|
{
|
||||||
|
if (x % 2 == 1) result += "\"";
|
||||||
|
|
||||||
|
result += string.Join(
|
||||||
|
' ',
|
||||||
|
x == paramStartIndex1
|
||||||
|
? data[x].Skip(paramStartIndex2)
|
||||||
|
: data[x]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (x % 2 == 1) result += "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (int, int) GetCoordinatesFrom(List<List<string>> data, int skip, int startX = 0, int startY = 0)
|
||||||
|
{
|
||||||
|
int skipping = 0;
|
||||||
|
var x = startX;
|
||||||
|
var y = startY;
|
||||||
|
|
||||||
|
var resultX = -1;
|
||||||
|
var resultY = -1;
|
||||||
|
for (; x < data.Count && resultX == -1; x++, y = 0)
|
||||||
|
{
|
||||||
|
for (; y < data[x].Count; y++)
|
||||||
|
{
|
||||||
|
if (skipping == skip)
|
||||||
|
{
|
||||||
|
resultX = x;
|
||||||
|
resultY = y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipping++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (resultX, resultY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetStringResource(string fileName, uint resourceId)
|
||||||
|
{
|
||||||
|
IntPtr? handle = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
handle = NativeMethods.LoadLibrary(fileName);
|
||||||
|
StringBuilder buffer = new(8192); //Buffer for output from LoadString()
|
||||||
|
int length = NativeMethods.LoadString(handle.Value, resourceId, buffer, buffer.Capacity);
|
||||||
|
return buffer.ToString(0, length); //Return the part of the buffer that was used.
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (handle is IntPtr validHandle)
|
||||||
|
{
|
||||||
|
NativeMethods.FreeLibrary(validHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NativeMethods
|
||||||
|
{
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
|
||||||
|
internal static extern IntPtr LoadLibrary(string lpLibFileName);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
|
||||||
|
internal static extern int LoadString(IntPtr hInstance, uint wID, StringBuilder lpBuffer, int nBufferMax);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern int FreeLibrary(IntPtr hLibModule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore CA1416
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using FileTime.Avalonia.Application;
|
using System.Runtime.InteropServices;
|
||||||
|
using FileTime.Avalonia.Application;
|
||||||
using FileTime.Avalonia.Services;
|
using FileTime.Avalonia.Services;
|
||||||
using FileTime.Avalonia.ViewModels;
|
using FileTime.Avalonia.ViewModels;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -15,9 +16,20 @@ namespace FileTime.Avalonia
|
|||||||
}
|
}
|
||||||
internal static IServiceCollection AddServices(this IServiceCollection serviceCollection)
|
internal static IServiceCollection AddServices(this IServiceCollection serviceCollection)
|
||||||
{
|
{
|
||||||
return serviceCollection
|
serviceCollection = serviceCollection
|
||||||
.AddLogging()
|
.AddLogging()
|
||||||
.AddSingleton<ItemNameConverterService>();
|
.AddSingleton<ItemNameConverterService>();
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
serviceCollection.AddSingleton<IContextMenuProvider, WindowsContextMenuProvider>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new System.Exception("TODO: implement linux contextmenu provider");
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,6 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
[Property]
|
[Property]
|
||||||
private List<RootDriveInfo> _rootDriveInfos;
|
private List<RootDriveInfo> _rootDriveInfos;
|
||||||
|
|
||||||
[Property]
|
|
||||||
private ObservableCollection<TabControlViewModel> _tabs = new ObservableCollection<TabControlViewModel>();
|
|
||||||
|
|
||||||
public Action? FocusDefaultElement { get; set; }
|
public Action? FocusDefaultElement { get; set; }
|
||||||
|
|
||||||
async partial void OnInitialize()
|
async partial void OnInitialize()
|
||||||
@@ -70,13 +67,9 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
await tab.Init(LocalContentProvider);
|
await tab.Init(LocalContentProvider);
|
||||||
|
|
||||||
var tabContainer = new TabContainer(tab, LocalContentProvider, ItemNameConverterService);
|
var tabContainer = new TabContainer(tab, LocalContentProvider, ItemNameConverterService);
|
||||||
await tabContainer.Init();
|
await tabContainer.Init(1);
|
||||||
AppState.Tabs = new List<TabContainer>()
|
tabContainer.IsSelected = true;
|
||||||
{
|
AppState.Tabs.Add(tabContainer);
|
||||||
tabContainer
|
|
||||||
};
|
|
||||||
|
|
||||||
_tabs.Add(new TabControlViewModel(1, tabContainer));
|
|
||||||
|
|
||||||
var driveInfos = new List<RootDriveInfo>();
|
var driveInfos = new List<RootDriveInfo>();
|
||||||
foreach (var drive in DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.Fixed))
|
foreach (var drive in DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.Fixed))
|
||||||
@@ -91,8 +84,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: order by
|
RootDriveInfos = driveInfos.OrderBy(d => d.Name).ToList();
|
||||||
RootDriveInfos = driveInfos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IContainer> GetContainerForWindowsDrive(DriveInfo drive)
|
private async Task<IContainer> GetContainerForWindowsDrive(DriveInfo drive)
|
||||||
@@ -203,30 +195,29 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
|
|
||||||
public async Task SwitchToTab(int number)
|
public async Task SwitchToTab(int number)
|
||||||
{
|
{
|
||||||
var tab = _tabs.FirstOrDefault(t => t.TabNumber == number);
|
var tabContainer = AppState.Tabs.FirstOrDefault(t => t.TabNumber == number);
|
||||||
|
|
||||||
if (number == -1)
|
if (number == -1)
|
||||||
{
|
{
|
||||||
var greatestNumber = _tabs.Select(t => t.TabNumber).Max();
|
var greatestNumber = AppState.Tabs.Select(t => t.TabNumber).Max();
|
||||||
tab = _tabs.FirstOrDefault(t => t.TabNumber == greatestNumber);
|
tabContainer = AppState.Tabs.FirstOrDefault(t => t.TabNumber == greatestNumber);
|
||||||
}
|
}
|
||||||
else if (tab == null)
|
else if (tabContainer == null)
|
||||||
{
|
{
|
||||||
var newContainer = await AppState.SelectedTab.CurrentLocation.Container.Clone();
|
var newContainer = await AppState.SelectedTab.CurrentLocation.Container.Clone();
|
||||||
|
|
||||||
var newTab = new Tab();
|
var newTab = new Tab();
|
||||||
await newTab.Init(newContainer);
|
await newTab.Init(newContainer);
|
||||||
|
|
||||||
var tabContainer = new TabContainer(newTab, LocalContentProvider, ItemNameConverterService);
|
tabContainer = new TabContainer(newTab, LocalContentProvider, ItemNameConverterService);
|
||||||
await tabContainer.Init();
|
await tabContainer.Init(number);
|
||||||
|
|
||||||
tab = new TabControlViewModel(number, tabContainer);
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
for (i = 0; i < Tabs.Count; i++)
|
for (i = 0; i < AppState.Tabs.Count; i++)
|
||||||
{
|
{
|
||||||
if (Tabs[i].TabNumber > number) break;
|
if (AppState.Tabs[i].TabNumber > number) break;
|
||||||
}
|
}
|
||||||
Tabs.Insert(i, tab);
|
AppState.Tabs.Insert(i, tabContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AppState.ViewMode == ViewMode.RapidTravel)
|
if (AppState.ViewMode == ViewMode.RapidTravel)
|
||||||
@@ -234,28 +225,29 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
await ExitRapidTravelMode();
|
await ExitRapidTravelMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
AppState.SelectedTab = tab.Tab;
|
AppState.SelectedTab = tabContainer;
|
||||||
|
|
||||||
foreach (var tab2 in Tabs)
|
foreach (var tab2 in AppState.Tabs)
|
||||||
{
|
{
|
||||||
tab2.IsSelected = tab2.TabNumber == tab.TabNumber;
|
tab2.IsSelected = tab2.TabNumber == tabContainer!.TabNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CloseTab()
|
public async Task CloseTab()
|
||||||
{
|
{
|
||||||
if (_tabs.Count > 1)
|
var tabs = AppState.Tabs;
|
||||||
|
if (tabs.Count > 1)
|
||||||
{
|
{
|
||||||
var currentTab = _tabs.FirstOrDefault(t => t.Tab == AppState.SelectedTab);
|
var currentTab = tabs.FirstOrDefault(t => t == AppState.SelectedTab);
|
||||||
|
|
||||||
if (currentTab != null)
|
if (currentTab != null)
|
||||||
{
|
{
|
||||||
_tabs.Remove(currentTab);
|
tabs.Remove(currentTab);
|
||||||
var tabNumber = _tabs[0].TabNumber;
|
var tabNumber = tabs[0].TabNumber;
|
||||||
for (var i = 0; i < Tabs.Count; i++)
|
for (var i = 0; i < tabs.Count; i++)
|
||||||
{
|
{
|
||||||
tabNumber = _tabs[i].TabNumber;
|
tabNumber = tabs[i].TabNumber;
|
||||||
if (Tabs[i].TabNumber > currentTab.TabNumber) break;
|
if (tabs[i].TabNumber > currentTab.TabNumber) break;
|
||||||
}
|
}
|
||||||
await SwitchToTab(tabNumber);
|
await SwitchToTab(tabNumber);
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/GuiApp/FileTime.Avalonia/Views/ItemView.axaml
Normal file
49
src/GuiApp/FileTime.Avalonia/Views/ItemView.axaml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="FileTime.Avalonia.Views.ItemView"
|
||||||
|
x:Name="ItemRoot" Background="{Binding ViewMode,Converter={StaticResource ItemViewModeToBackgroundConverter}}">
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto" Margin="3">
|
||||||
|
<Grid.Styles>
|
||||||
|
<Style Selector="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="{Binding DataContext.ViewMode,Converter={StaticResource ItemViewModeToForegroundConverter},ElementName=ItemRoot}"/>
|
||||||
|
</Style>
|
||||||
|
</Grid.Styles>
|
||||||
|
<Image
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
Width="18"
|
||||||
|
Height="18"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Source="{SvgImage /Assets/material/folder.svg}" />
|
||||||
|
|
||||||
|
<ItemsControl
|
||||||
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Items="{Binding DisplayName}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Text}"
|
||||||
|
TextDecorations="{Binding TextDecorations}"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
|
<Grid Grid.Column="2" IsVisible="{Binding ShowAttributes,ElementName=ItemRoot}">
|
||||||
|
<Grid ColumnDefinitions="180,40">
|
||||||
|
<TextBlock Text="{Binding Item.CreatedAt}"/>
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding Item.Attributes}"/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
27
src/GuiApp/FileTime.Avalonia/Views/ItemView.axaml.cs
Normal file
27
src/GuiApp/FileTime.Avalonia/Views/ItemView.axaml.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.Views
|
||||||
|
{
|
||||||
|
public partial class ItemView : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<bool> ShowAttributesProperty = AvaloniaProperty.Register<ItemView, bool>(nameof(ShowAttributes), true);
|
||||||
|
|
||||||
|
public bool ShowAttributes
|
||||||
|
{
|
||||||
|
get => GetValue(ShowAttributesProperty);
|
||||||
|
set => SetValue(ShowAttributesProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="using:FileTime.Avalonia.ViewModels"
|
xmlns:vm="using:FileTime.Avalonia.ViewModels"
|
||||||
|
xmlns:local="using:FileTime.Avalonia.Views"
|
||||||
Title="FileTime.Avalonia"
|
Title="FileTime.Avalonia"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
@@ -88,19 +89,25 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid Grid.Column="1" RowDefinitions="Auto,*,Auto">
|
<Grid Grid.Column="1" RowDefinitions="40,*,Auto">
|
||||||
<Grid ColumnDefinitions="*,Auto">
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
|
||||||
<Grid>
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="10,5"
|
Margin="10,5"
|
||||||
Text="{Binding AppState.SelectedTab.CurrentLocation.Container.FullName}" />
|
Text="{Binding AppState.SelectedTab.TabNumber,StringFormat=({0})}" />
|
||||||
</Grid>
|
|
||||||
|
<local:PathPresenter Margin="10,5,0,5" DataContext="{Binding AppState.SelectedTab.CurrentLocation.Container.FullName}"/>
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,5,10,5"
|
||||||
|
Text="{Binding AppState.SelectedTab.SelectedItem.Item.Name}" Foreground="{StaticResource AccentForegroundBrush}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Items="{Binding Tabs}">
|
Items="{Binding AppState.Tabs}">
|
||||||
<ItemsControl.ItemsPanel>
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<StackPanel Orientation="Horizontal" />
|
<StackPanel Orientation="Horizontal" />
|
||||||
@@ -108,38 +115,17 @@
|
|||||||
</ItemsControl.ItemsPanel>
|
</ItemsControl.ItemsPanel>
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<StackPanel Orientation="Horizontal"
|
<Grid RowDefinitions="Auto,1">
|
||||||
Margin="10,0,0,0">
|
<StackPanel Orientation="Horizontal" Margin="20,0,20,0">
|
||||||
<!--i:Interaction.Behaviors>
|
|
||||||
<ic:DataTriggerBehavior
|
|
||||||
Binding="{Binding IsSelected}"
|
|
||||||
Value="True">
|
|
||||||
<ic:ChangePropertyAction
|
|
||||||
PropertyName="Background"
|
|
||||||
Value="{DynamicResource ForegroundBrush}" />
|
|
||||||
<ic:ChangePropertyAction
|
|
||||||
PropertyName="Foreground"
|
|
||||||
TargetObject="{Binding ElementName=Text}"
|
|
||||||
Value="{DynamicResource ContainerBackgroundBrush}" />
|
|
||||||
</ic:DataTriggerBehavior>
|
|
||||||
<ic:DataTriggerBehavior
|
|
||||||
Binding="{Binding IsSelected}"
|
|
||||||
Value="False">
|
|
||||||
<ic:ChangePropertyAction
|
|
||||||
PropertyName="Background"
|
|
||||||
Value="{DynamicResource ContainerBackgroundBrush}" />
|
|
||||||
<ic:ChangePropertyAction
|
|
||||||
PropertyName="Foreground"
|
|
||||||
TargetObject="{Binding ElementName=Text}"
|
|
||||||
Value="{DynamicResource ForegroundBrush}" />
|
|
||||||
</ic:DataTriggerBehavior>
|
|
||||||
</i:Interaction.Behaviors-->
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center" Text="{Binding TabNumber}" />
|
VerticalAlignment="Center" Text="{Binding TabNumber,StringFormat=({0})}" />
|
||||||
<TextBlock Margin="5,0,0,0"
|
|
||||||
VerticalAlignment="Center" Text="{Binding Tab.CurrentLocation.Container.FullName}" />
|
<local:PathPresenter Margin="5,0,0,0" DataContext="{Binding CurrentLocation.Container.FullName}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<Rectangle Fill="{DynamicResource ForegroundBrush}" Grid.Row="1" IsVisible="{Binding IsSelected}"/>
|
||||||
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
@@ -147,7 +133,7 @@
|
|||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Margin="20">
|
Margin="20,0,0,0">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="15*" />
|
<ColumnDefinition Width="15*" />
|
||||||
@@ -161,7 +147,13 @@
|
|||||||
<ListBox
|
<ListBox
|
||||||
Classes="ContentListView"
|
Classes="ContentListView"
|
||||||
Items="{Binding AppState.SelectedTab.Parent.Items}"
|
Items="{Binding AppState.SelectedTab.Parent.Items}"
|
||||||
SelectedItem="{Binding AppState.SelectedTab.CurrentLocation}" />
|
SelectedItem="{Binding AppState.SelectedTab.CurrentLocation}">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<local:ItemView ShowAttributes="False"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Rectangle
|
<Rectangle
|
||||||
@@ -180,13 +172,20 @@
|
|||||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Hidden"
|
ScrollViewer.VerticalScrollBarVisibility="Hidden"
|
||||||
SelectedItem="{Binding AppState.SelectedTab.SelectedItem, Mode=TwoWay}"
|
SelectedItem="{Binding AppState.SelectedTab.SelectedItem, Mode=TwoWay}"
|
||||||
Classes="ContentListView" />
|
Classes="ContentListView">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<local:ItemView/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="CurrentEmpty"
|
x:Name="CurrentEmpty"
|
||||||
Margin="10"
|
Margin="10"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
|
Foreground="{DynamicResource ErrorBrush}"
|
||||||
IsVisible="{Binding AppState.SelectedTab.CurrentLocation.Items.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
|
IsVisible="{Binding AppState.SelectedTab.CurrentLocation.Items.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
|
||||||
Empty
|
Empty
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
@@ -205,7 +204,13 @@
|
|||||||
Classes="ContentListView"
|
Classes="ContentListView"
|
||||||
x:Name="ChildItems"
|
x:Name="ChildItems"
|
||||||
Items="{Binding AppState.SelectedTab.ChildContainer.Items}"
|
Items="{Binding AppState.SelectedTab.ChildContainer.Items}"
|
||||||
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}" />
|
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<local:ItemView/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="ChildEmpty"
|
x:Name="ChildEmpty"
|
||||||
|
|||||||
23
src/GuiApp/FileTime.Avalonia/Views/PathPresenter.axaml
Normal file
23
src/GuiApp/FileTime.Avalonia/Views/PathPresenter.axaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:coremodels="using:FileTime.Core.Models"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="FileTime.Avalonia.Views.PathPresenter">
|
||||||
|
<ItemsControl Items="{Binding Converter={StaticResource SplitStringConverter}, ConverterParameter={x:Static coremodels:Constants.SeparatorChar}}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="{Binding}"/>
|
||||||
|
<TextBlock Text="/" Margin="5,0,5,0" Foreground="{DynamicResource LightForegroundBrush}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</UserControl>
|
||||||
19
src/GuiApp/FileTime.Avalonia/Views/PathPresenter.axaml.cs
Normal file
19
src/GuiApp/FileTime.Avalonia/Views/PathPresenter.axaml.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.Views
|
||||||
|
{
|
||||||
|
public partial class PathPresenter : UserControl
|
||||||
|
{
|
||||||
|
public PathPresenter()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,7 @@ namespace FileTime.Providers.Local
|
|||||||
{
|
{
|
||||||
public class LocalFile : IElement
|
public class LocalFile : IElement
|
||||||
{
|
{
|
||||||
private readonly FileInfo _file;
|
public FileInfo File { get; }
|
||||||
|
|
||||||
public FileInfo File => _file;
|
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
@@ -18,26 +16,46 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
public IContentProvider Provider { get; }
|
public IContentProvider Provider { get; }
|
||||||
|
|
||||||
public bool IsHidden => (_file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
|
public bool IsHidden => (File.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
|
||||||
public bool IsSpecial =>
|
public bool IsSpecial =>
|
||||||
RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
|
RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
|
||||||
&& (new UnixFileInfo(_file.FullName).FileAccessPermissions & FileAccessPermissions.UserExecute) == FileAccessPermissions.UserExecute;
|
&& (new UnixFileInfo(File.FullName).FileAccessPermissions & FileAccessPermissions.UserExecute) == FileAccessPermissions.UserExecute;
|
||||||
|
|
||||||
|
public string Attributes => GetAttributes();
|
||||||
|
|
||||||
|
public DateTime CreatedAt => File.CreationTime;
|
||||||
|
|
||||||
public LocalFile(FileInfo file, IContentProvider contentProvider)
|
public LocalFile(FileInfo file, IContentProvider contentProvider)
|
||||||
{
|
{
|
||||||
_file = file;
|
File = file;
|
||||||
|
|
||||||
Name = file.Name;
|
Name = file.Name;
|
||||||
FullName = file.FullName;
|
FullName = file.FullName;
|
||||||
Provider = contentProvider;
|
Provider = contentProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetPrimaryAttributeText() => _file.Length.ToSizeString();
|
public string GetPrimaryAttributeText() => File.Length.ToSizeString();
|
||||||
|
|
||||||
public Task Delete()
|
public Task Delete()
|
||||||
{
|
{
|
||||||
_file.Delete();
|
File.Delete();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetAttributes()
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "-"
|
||||||
|
+ ((File.Attributes & FileAttributes.Archive) == FileAttributes.Archive ? "a" : "-")
|
||||||
|
+ ((File.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly ? "r" : "-")
|
||||||
|
+ ((File.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden ? "h" : "-")
|
||||||
|
+ ((File.Attributes & FileAttributes.System) == FileAttributes.System ? "s" : "-");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
using AsyncEvent;
|
using AsyncEvent;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Providers;
|
using FileTime.Core.Providers;
|
||||||
@@ -21,6 +22,9 @@ namespace FileTime.Providers.Local
|
|||||||
public string FullName { get; }
|
public string FullName { get; }
|
||||||
|
|
||||||
public AsyncEventHandler Refreshed { get; } = new();
|
public AsyncEventHandler Refreshed { get; } = new();
|
||||||
|
public string Attributes => GetAttributes();
|
||||||
|
|
||||||
|
public DateTime CreatedAt => Directory.CreationTime;
|
||||||
|
|
||||||
public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer? parent)
|
public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer? parent)
|
||||||
{
|
{
|
||||||
@@ -111,5 +115,21 @@ namespace FileTime.Providers.Local
|
|||||||
Directory.Delete(true);
|
Directory.Delete(true);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetAttributes()
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "d"
|
||||||
|
+ ((Directory.Attributes & FileAttributes.Archive) == FileAttributes.Archive ? "a" : "-")
|
||||||
|
+ ((Directory.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly ? "r" : "-")
|
||||||
|
+ ((Directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden ? "h" : "-")
|
||||||
|
+ ((Directory.Attributes & FileAttributes.System) == FileAttributes.System ? "s" : "-");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user