Added app configuration
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
public static class AssemblyExtensions
|
||||
{
|
||||
public static Stream ExtractResource(this Assembly assembly, string filename)
|
||||
{
|
||||
var resourceName = $"{assembly.GetName().Name.Replace("-", "_")}.{filename}";
|
||||
return assembly.GetManifestResourceStream(resourceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
|
||||
public static class Class1
|
||||
{
|
||||
public static void Test(Stream s)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
public static class ConfigurationBuilderExtensions
|
||||
{
|
||||
public static IConfigurationBuilder AddWritableConfiguration(this IConfigurationBuilder builder)
|
||||
{
|
||||
return builder.Add(new WritableJsonConfigurationSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
public record CreateShortcut(string Path);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using WindowsShortcutFactory;
|
||||
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
public class CreateShortcutHandler : IMessageHandler<CreateShortcut>
|
||||
{
|
||||
public void Handle(CreateShortcut message)
|
||||
{
|
||||
using var shortcut = new WindowsShortcut
|
||||
{
|
||||
Path = message.Path,
|
||||
};
|
||||
|
||||
shortcut.Save(@"C:\temp\MyShortcut.lnk");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
public class ShortcutConfiguration
|
||||
{
|
||||
private string? _shortcutDirectory;
|
||||
|
||||
public string? ShortcutDirectory
|
||||
{
|
||||
get => !string.IsNullOrEmpty(_shortcutDirectory) ? Environment.ExpandEnvironmentVariables(_shortcutDirectory) : null;
|
||||
set => _shortcutDirectory = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,10 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageReference Include="System.Runtime.WindowsRuntime" Version="5.0.0-preview.5.20278.1" />
|
||||
<PackageReference Include="WindowsShortcutFactory" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="UIA">
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
public static class WritableJsonConfigurationExtensions
|
||||
{
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path)
|
||||
{
|
||||
return AddWritableJsonFile(builder, null, path, false, false, null);
|
||||
}
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
Stream createFromStream)
|
||||
{
|
||||
return AddWritableJsonFile(builder, null, path, false, false, createFromStream);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
bool optional)
|
||||
{
|
||||
return AddWritableJsonFile(builder, null, path, optional, false, null);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
bool optional,
|
||||
Stream createFromStream)
|
||||
{
|
||||
return AddWritableJsonFile(builder, null, path, optional, false, createFromStream);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
bool optional,
|
||||
bool reloadOnChange)
|
||||
{
|
||||
return AddWritableJsonFile(builder, null, path, optional, reloadOnChange, null);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
string path,
|
||||
bool optional,
|
||||
bool reloadOnChange,
|
||||
Stream createFromStream)
|
||||
{
|
||||
return AddWritableJsonFile(builder, null, path, optional, reloadOnChange, createFromStream);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
IFileProvider? provider,
|
||||
string path,
|
||||
bool optional,
|
||||
bool reloadOnChange, Stream? createFromStream)
|
||||
{
|
||||
return builder.AddWritableJsonFile(configuration =>
|
||||
{
|
||||
configuration.FileProvider = provider;
|
||||
configuration.Path = path;
|
||||
configuration.Optional = optional;
|
||||
configuration.ReloadOnChange = reloadOnChange;
|
||||
configuration.CreateFromSteam = createFromStream;
|
||||
configuration.ResolveFileProvider();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static IConfigurationBuilder AddWritableJsonFile(this IConfigurationBuilder builder,
|
||||
Action<WritableJsonConfigurationSource> configureSource) => builder.Add(configureSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
internal class WritableJsonConfigurationFile
|
||||
{
|
||||
private readonly Dictionary<string, (JsonValueKind, string)> _data = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Stack<string> _paths = new();
|
||||
private JObject _tokenCache = new();
|
||||
|
||||
public IDictionary<string, string> Parse(Stream input)
|
||||
{
|
||||
return ParseStream(input);
|
||||
}
|
||||
|
||||
private object ConvertValue(JsonValueKind kind, string value)
|
||||
{
|
||||
if (kind is JsonValueKind.True or JsonValueKind.False)
|
||||
{
|
||||
return bool.Parse(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Write(string key, string value, Stream output)
|
||||
{
|
||||
var tokenPath = $"$.{key.Replace(":", ".")}";
|
||||
|
||||
var token = _tokenCache.SelectToken(tokenPath);
|
||||
if (token is not null)
|
||||
{
|
||||
var (kind, _) = _data[key];
|
||||
|
||||
var newValue = ConvertValue(kind, value);
|
||||
_data[key] = new(kind, value);
|
||||
|
||||
token.Replace(JToken.FromObject(newValue));
|
||||
|
||||
using StreamWriter streamWriter = new(output);
|
||||
using JsonTextWriter writer = new(streamWriter) { Formatting = Formatting.Indented };
|
||||
_tokenCache.WriteTo(writer);
|
||||
output.SetLength(output.Position);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnterContext(string context)
|
||||
{
|
||||
_paths.Push(_paths.Count > 0 ? _paths.Peek() + ConfigurationPath.KeyDelimiter + context : context);
|
||||
}
|
||||
|
||||
private void ExitContext()
|
||||
{
|
||||
_paths.Pop();
|
||||
}
|
||||
|
||||
private IDictionary<string, string> ParseStream(Stream input)
|
||||
{
|
||||
_data.Clear();
|
||||
|
||||
var jsonDocumentOptions = new JsonDocumentOptions
|
||||
{
|
||||
CommentHandling = JsonCommentHandling.Skip,
|
||||
AllowTrailingCommas = true,
|
||||
};
|
||||
|
||||
using (var reader = new StreamReader(input))
|
||||
{
|
||||
var content = reader.ReadToEnd();
|
||||
_tokenCache = JObject.Parse(content);
|
||||
|
||||
using var doc = JsonDocument.Parse(content, jsonDocumentOptions);
|
||||
VisitElement(doc.RootElement);
|
||||
}
|
||||
|
||||
return _data.ToDictionary(k => k.Key, v => v.Value.Item2.ToString());
|
||||
}
|
||||
|
||||
private void VisitElement(JsonElement element)
|
||||
{
|
||||
var isEmpty = true;
|
||||
|
||||
foreach (JsonProperty property in element.EnumerateObject())
|
||||
{
|
||||
isEmpty = false;
|
||||
|
||||
EnterContext(property.Name);
|
||||
VisitValue(property.Value);
|
||||
ExitContext();
|
||||
}
|
||||
|
||||
if (isEmpty && _paths.Count > 0)
|
||||
{
|
||||
_data[_paths.Peek()] = (JsonValueKind.Null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void VisitValue(JsonElement value)
|
||||
{
|
||||
switch (value.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
VisitElement(value);
|
||||
break;
|
||||
|
||||
case JsonValueKind.Array:
|
||||
int index = 0;
|
||||
foreach (JsonElement arrayElement in value.EnumerateArray())
|
||||
{
|
||||
EnterContext(index.ToString());
|
||||
VisitValue(arrayElement);
|
||||
ExitContext();
|
||||
index++;
|
||||
}
|
||||
break;
|
||||
|
||||
case JsonValueKind.Number:
|
||||
case JsonValueKind.String:
|
||||
case JsonValueKind.True:
|
||||
case JsonValueKind.False:
|
||||
case JsonValueKind.Null:
|
||||
string key = _paths.Peek();
|
||||
if (_data.ContainsKey(key))
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
_data[key] = new(value.ValueKind, value.ToString());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.Extensions.Configuration.Json;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
public class WritableJsonConfigurationProvider : JsonConfigurationProvider
|
||||
{
|
||||
private readonly WritableJsonConfigurationFile writableJsonConfigurationFile;
|
||||
|
||||
public WritableJsonConfigurationProvider(JsonConfigurationSource source) : base(source)
|
||||
{
|
||||
writableJsonConfigurationFile = new WritableJsonConfigurationFile();
|
||||
}
|
||||
|
||||
public override void Load(Stream stream)
|
||||
{
|
||||
Data = writableJsonConfigurationFile.Parse(stream);
|
||||
}
|
||||
|
||||
public override void Set(string key, string value)
|
||||
{
|
||||
var file = Source.FileProvider?.GetFileInfo(Source.Path ?? string.Empty);
|
||||
static Stream OpenRead(IFileInfo fileInfo)
|
||||
{
|
||||
if (fileInfo.PhysicalPath is not null)
|
||||
{
|
||||
return new FileStream(fileInfo.PhysicalPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
|
||||
}
|
||||
|
||||
return fileInfo.CreateReadStream();
|
||||
}
|
||||
|
||||
using Stream stream = OpenRead(file);
|
||||
writableJsonConfigurationFile.Write(key, value, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration.Json;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace TheXamlGuy.TaskbarGroup.Core
|
||||
{
|
||||
public class WritableJsonConfigurationSource : JsonConfigurationSource
|
||||
{
|
||||
public override IConfigurationProvider Build(IConfigurationBuilder builder)
|
||||
{
|
||||
EnsureDefaultsWithSteam(builder);
|
||||
return new WritableJsonConfigurationProvider(this);
|
||||
}
|
||||
|
||||
private void EnsureDefaultsWithSteam(IConfigurationBuilder builder)
|
||||
{
|
||||
EnsureDefaults(builder);
|
||||
|
||||
if (FileProvider is PhysicalFileProvider physicalFileProvider)
|
||||
{
|
||||
var outputFile = System.IO.Path.Combine(physicalFileProvider.Root, Path);
|
||||
if (!File.Exists(outputFile) && CreateFromSteam is not null)
|
||||
{
|
||||
using var fileStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
|
||||
CreateFromSteam.CopyTo(fileStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Stream? CreateFromSteam { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Shell;
|
||||
using TheXamlGuy.TaskbarGroup.Core;
|
||||
using Microsoft.WindowsAPICodePack.Shell;
|
||||
|
||||
namespace TheXamlGuy.TaskbarGroup.Foundation
|
||||
{
|
||||
public class FileDropTarget : IDropTarget<UIElement>
|
||||
{
|
||||
private UIElement? target;
|
||||
private readonly IMessenger messenger;
|
||||
|
||||
public FileDropTarget(IMessenger messenger)
|
||||
{
|
||||
this.messenger = messenger;
|
||||
}
|
||||
|
||||
public void Register(UIElement target)
|
||||
{
|
||||
if (this.target is not null)
|
||||
{
|
||||
target.DragOver -= OnDragOver;
|
||||
target.DragEnter -= OnDragEnter;
|
||||
target.Drop -= OnDrop;
|
||||
}
|
||||
|
||||
this.target = target;
|
||||
|
||||
target.DragOver += OnDragOver;
|
||||
target.DragEnter += OnDragEnter;
|
||||
target.Drop += OnDrop;
|
||||
}
|
||||
|
||||
private void OnDrop(object sender, DragEventArgs args)
|
||||
{
|
||||
String[] fileName = (String[])args.Data.GetFormats();
|
||||
|
||||
var ddd = ShellObjectCollection.FromDataObject((System.Runtime.InteropServices.ComTypes.IDataObject)args.Data);
|
||||
|
||||
//args.Handled = true;
|
||||
//var fileName = GetFileName(args.Data);
|
||||
//messenger.Publish<FileDropped>();
|
||||
|
||||
}
|
||||
|
||||
private string GetFileName(IDataObject data)
|
||||
{
|
||||
var filenames = (string[])data.GetData(DataFormats.FileDrop);
|
||||
return filenames[0];
|
||||
}
|
||||
|
||||
private bool IsFileDrop(IDataObject data)
|
||||
{
|
||||
return data.GetDataPresent(DataFormats.FileDrop);
|
||||
}
|
||||
|
||||
private void OnDragEnter(object sender, DragEventArgs args)
|
||||
{
|
||||
if (IsFileDrop(args.Data))
|
||||
{
|
||||
args.Effects = DragDropEffects.Link;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Effects = DragDropEffects.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDragOver(object sender, DragEventArgs args)
|
||||
{
|
||||
if (IsFileDrop(args.Data))
|
||||
{
|
||||
args.Effects = DragDropEffects.Link;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Effects = DragDropEffects.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,7 @@ namespace TheXamlGuy.TaskbarGroup.Foundation
|
||||
.AddSingleton<TemplateSelector>()
|
||||
.AddSingleton<IDataTemplateCollection>(new DataTemplateCollection(new Dictionary<Type, Type>()))
|
||||
.AddSingleton<DataTemplateFactory>()
|
||||
.AddSingleton<IDispatcherTimerFactory, DispatcherTimerFactory>()
|
||||
.AddTransient<FileDropTarget>();
|
||||
.AddSingleton<IDispatcherTimerFactory, DispatcherTimerFactory>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
<Platforms>x64;x86</Platforms>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Core" Version="1.1.4" />
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using TheXamlGuy.TaskbarGroup.Core;
|
||||
using TheXamlGuy.TaskbarGroup.Flyout;
|
||||
@@ -19,7 +20,10 @@ namespace TheXamlGuy.TaskbarGroup
|
||||
host = new HostBuilder()
|
||||
.UseContentRoot(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
|
||||
"TheXamlGuy", "TaskbarGroup"), true)
|
||||
.ConfigureServices(ConfigureServices)
|
||||
.ConfigureAppConfiguration(configuration =>
|
||||
{
|
||||
configuration.AddWritableJsonFile("Settings.json", false, true, Assembly.GetExecutingAssembly().ExtractResource("Settings.json"));
|
||||
}).ConfigureServices(ConfigureServices)
|
||||
.Build();
|
||||
|
||||
await host.StartAsync();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
@@ -11,6 +11,9 @@
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Settings.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
|
||||
@@ -22,6 +25,9 @@
|
||||
<ProjectReference Include="..\TheXamlGuy.TaskbarGroup.Flyout\TheXamlGuy.TaskbarGroup.Flyout.csproj" />
|
||||
<ProjectReference Include="..\TheXamlGuy.TaskbarGroup.Foundation\TheXamlGuy.TaskbarGroup.Foundation.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Settings.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.VCRTForwarders.140" Version="1.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user