move source files

This commit is contained in:
Alex
2025-08-19 17:03:56 -07:00
parent feaa20aac5
commit 66c834e8be
21 changed files with 5 additions and 6 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

+119
View File
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text.Json;
using Avalonia.Threading;
using MercuryConverter.UI.Views;
using SaturnData.Notation.Core;
using SaturnData.Notation.Serialization;
using SaturnData.Notation.Serialization.Mer;
using UAssetAPI;
using UAssetAPI.ExportTypes;
using UAssetAPI.PropertyTypes.Objects;
using UAssetAPI.PropertyTypes.Structs;
using UAssetAPI.UnrealTypes;
namespace MercuryConverter.Data;
public static class Database
{
public static ObservableCollection<Song> Songs = new();
public static void SetupNew(string dataPath)
{
Dispatcher.UIThread.Invoke(() => Songs.Clear());
var metadataTablePath = Path.Combine(dataPath, "MusicParameterTable.uasset");
var metadataAsset = new UAsset(metadataTablePath, EngineVersion.VER_UE4_19);
var metadataTable = metadataAsset.Exports[0] as DataTableExport;
foreach (var data in metadataTable!.Table.Data)
{
if (data["AssetDirectory"].ToString()!.Contains("S99"))
{
continue;
}
var previewBegin = ((FloatPropertyData)data["PreviewBeginTime"]).Value;
var previewLen = ((FloatPropertyData)data["PreviewSeconds"]).Value;
string? cTxt = data["CopyrightMessage"].ToString();
var jacketPath = $"{Path.Combine(dataPath, "jackets", data["JacketAssetName"].ToString()!)}.png";
try
{
var song = new Song
{
Id = data["AssetDirectory"].ToString()!,
Rubi = data["Rubi"].ToString()!,
Name = data["MusicMessage"].ToString()!,
Artist = data["ArtistMessage"].ToString()!,
Genre = ((IntPropertyData)data["ScoreGenre"]).Value,
Source = ((UInt32PropertyData)data["VersionNo"]).Value,
PreviewTime = previewBegin,
PreviewLen = previewLen,
Jacket = File.Exists(jacketPath) ? jacketPath : null,
Copyright = (cTxt == "-" || cTxt == "") ? null : cTxt,
};
foreach (Difficulty diff in Enum.GetValues(typeof(Difficulty)))
{
// skip non-canon difficulties
if (diff == Difficulty.None || diff == Difficulty.WorldsEnd) continue;
var lvl = ((FloatPropertyData)data[Consts.DIFF_LVL_KEY[diff]]).Value;
if (lvl == 0) continue; // skip nonexistent level
// check chart existence
var chartFilePath = Path.Combine(dataPath, "MusicData", song.Id, $"{song.Id}_{Consts.DIFF_FILENAME_PREPEND[diff]}.mer");
if (!File.Exists(chartFilePath))
{
// TODO: add warning message to DataScan
Console.WriteLine($"[MISSING CHART] {song.Id} {song.Artist} - {song.Name} / {diff}");
continue;
}
// TODO: check audio existence; add warning but don't skip
song.Levels[(int)diff] = lvl;
}
Dispatcher.UIThread.Post(() => Songs.Add(song));
}
catch (Exception e)
{
Console.WriteLine($"Couldn't construct a song!\n{e}");
}
}
Console.WriteLine("finished music table");
}
private static (Entry, Chart)? GetDiffPair(string dataPath, StructPropertyData song, Difficulty diff)
{
var level = ((FloatPropertyData)song[Consts.DIFF_LVL_KEY[diff]]).Value;
if (level == 0)
return null;
var id = song["AssetDirectory"].ToString()!;
var chartFilePath = Path.Combine(dataPath, "MusicData", id, $"{id}_{Consts.DIFF_FILENAME_PREPEND[diff]}.mer");
var clearThreshold = ((FloatPropertyData)song[Consts.DIFF_CLEAR_KEY[diff]]).Value;
var e = NotationSerializer.ToEntry(chartFilePath, new NotationReadArgs
{
InferClearThresholdFromDifficulty = false
});
e.ClearThreshold = clearThreshold;
e.Difficulty = diff;
var c = NotationSerializer.ToChart(chartFilePath, new NotationReadArgs
{
InferClearThresholdFromDifficulty = false
});
return (e, c);
}
}
+61
View File
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SaturnData.Notation.Core;
namespace MercuryConverter.Data;
public static class Consts
{
private static Dictionary<int, string> _CATEGORY_INDEX = new()
{
{ -1, "Unknown"},
{ 0, "Anime/Pop"},
{ 1, "Vocaloid"},
{ 2, "Touhou Project"},
{ 3, "2.5D" },
{ 4, "Variety" },
{ 5, "Original" },
{ 6, "HARDCORE TANO*C" },
{ 7, "VTuber" },
};
public static readonly IReadOnlyDictionary<int, string> CATEGORY_INDEX = _CATEGORY_INDEX;
private static Dictionary<uint, string> _NUM_SOURCE = new()
{
{ 1, string.Concat(new int[] {87, 65, 67, 67, 65}.Select(i => Convert.ToChar(i))) },
{ 2, string.Concat(new int[] {87, 65, 67, 67, 65, 32, 83}.Select(i => Convert.ToChar(i))) },
{ 3, string.Concat(new int[] {87, 65, 67, 67, 65, 32, 76, 73, 76, 89}.Select(i => Convert.ToChar(i))) },
{ 4, string.Concat(new int[] {87, 65, 67, 67, 65, 32, 76, 73, 76, 89, 32, 82}.Select(i => Convert.ToChar(i))) },
{ 5, string.Concat(new int[] {87, 65, 67, 67, 65, 32, 82, 101, 118, 101, 114, 115, 101}.Select(i => Convert.ToChar(i))) }
};
public static readonly IReadOnlyDictionary<uint, string> NUM_SOURCE = _NUM_SOURCE;
public static readonly IReadOnlyDictionary<string, uint> SOURCE_NUM = _NUM_SOURCE.ToDictionary(p => p.Value, p => p.Key);
private static readonly Dictionary<Difficulty, string> _DIFF_LVL_KEY = new()
{
{Difficulty.Normal, "DifficultyNormalLv"},
{Difficulty.Hard, "DifficultyHardLv"},
{Difficulty.Expert, "DifficultyExtremeLv"},
{Difficulty.Inferno, "DifficultyInfernoLv"},
};
public static readonly IReadOnlyDictionary<Difficulty, string> DIFF_LVL_KEY = _DIFF_LVL_KEY;
private static readonly Dictionary<Difficulty, string> _DIFF_FILENAME_PREPEND = new()
{
{Difficulty.Normal, "00"},
{Difficulty.Hard, "01"},
{Difficulty.Expert, "02"},
{Difficulty.Inferno, "03"},
};
public static readonly IReadOnlyDictionary<Difficulty, string> DIFF_FILENAME_PREPEND = _DIFF_FILENAME_PREPEND;
private static readonly Dictionary<Difficulty, string> _DIFF_CLEAR_KEY = new()
{
{Difficulty.Normal, "ClearNormaRateNormal"},
{Difficulty.Hard, "ClearNormaRateHard"},
{Difficulty.Expert, "ClearNormaRateExtreme"},
{Difficulty.Inferno, "ClearNormaRateInferno"},
};
public static readonly IReadOnlyDictionary<Difficulty, string> DIFF_CLEAR_KEY = _DIFF_CLEAR_KEY;
}
+30
View File
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Media;
using SaturnData.Notation.Core;
namespace MercuryConverter.Data;
/// <summary>
/// Combining SaturnData's Entry & Chart.
/// </summary>
public class Song
{
public required string Id { get; set; } // Snn-nnn
public required string Name { get; set; }
public required string Artist { get; set; }
public required uint Source { get; set; }
public required string Rubi { get; set; }
public string? Copyright { get; set; } // May have never been used?
public required int Genre { get; set; }
public required string? Jacket { get; set; }
public required float PreviewTime { get; set; }
public required float PreviewLen { get; set; }
public string SourceName => Consts.NUM_SOURCE[Source];
public float?[] Levels { get; set; } = { null, null, null, null };
// TODO: For SaturnData.Entry instances, use this Guid format:
// MERCURY_[SONGID]_[DIFF] (each var is int)
}
+39
View File
@@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>false</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.2" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.2" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.2" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.2" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.2" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.2">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="Deadpikle.AvaloniaProgressRing" Version="0.10.10" />
<PackageReference Include="DialogHost.Avalonia" Version="0.9.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="_External\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\_External\UAssetAPI\UAssetAPI\UAssetAPI.csproj" />
<ProjectReference Include="../_External/SaturnData/SaturnData/SaturnData.csproj" />
</ItemGroup>
</Project>
+24
View File
@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MercuryConverter", "MercuryConverter.csproj", "{8D7AB684-46C8-6289-FBE5-52DC512A35BE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8D7AB684-46C8-6289-FBE5-52DC512A35BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D7AB684-46C8-6289-FBE5-52DC512A35BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D7AB684-46C8-6289-FBE5-52DC512A35BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D7AB684-46C8-6289-FBE5-52DC512A35BE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8A55B6C1-9740-4A8D-859C-4CC399386136}
EndGlobalSection
EndGlobal
+37
View File
@@ -0,0 +1,37 @@
namespace MercuryConverter;
using Avalonia;
using System;
using MercuryConverter.UI;
using Avalonia.Logging;
class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args)
{
// BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
try
{
// prepare and run your App here
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
catch (Exception e)
{
// here we can work with the exception, for example add it to our log file
Console.WriteLine($"App exception!!\b{e}");
}
}
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace(LogEventLevel.Debug);
}
+47
View File
@@ -0,0 +1,47 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MercuryConverter.UI.App"
xmlns:dialogHostAvalonia="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
RequestedThemeVariant="Default"> <!-- Dark Default Light -->
<Application.Styles>
<FluentTheme>
<FluentTheme.Palettes>
<ColorPaletteResources x:Key="Light" Accent="#ffb459c0" AltHigh="White" AltLow="White" AltMedium="White" AltMediumHigh="White" AltMediumLow="White" BaseHigh="Black" BaseLow="#ffa1aeff" BaseMedium="#ff5a71c0" BaseMediumHigh="#ff2b4996" BaseMediumLow="#ff435dab" ChromeAltLow="#ff2b4996" ChromeBlackHigh="Black" ChromeBlackLow="#ffa1aeff" ChromeBlackMedium="#ff2b4996" ChromeBlackMediumLow="#ff5a71c0" ChromeDisabledHigh="#ffa1aeff" ChromeDisabledLow="#ff5a71c0" ChromeGray="#ff435dab" ChromeHigh="#ffa1aeff" ChromeLow="#ffd9e1ff" ChromeMedium="#ffced7ff" ChromeMediumLow="#ffd9e1ff" ChromeWhite="White" ListLow="#ffced7ff" ListMedium="#ffa1aeff" RegionColor="#ffccdbff" />
<ColorPaletteResources x:Key="Dark" Accent="#ff9b21dd" AltHigh="Black" AltLow="Black" AltMedium="Black" AltMediumHigh="Black" AltMediumLow="Black" BaseHigh="White" BaseLow="#ff3c2d94" BaseMedium="#ff9e96d2" BaseMediumHigh="#ffb7b0e1" BaseMediumLow="#ff6d61b3" ChromeAltLow="#ffb7b0e1" ChromeBlackHigh="Black" ChromeBlackLow="#ffb7b0e1" ChromeBlackMedium="Black" ChromeBlackMediumLow="Black" ChromeDisabledHigh="#ff3c2d94" ChromeDisabledLow="#ff9e96d2" ChromeGray="#ff867cc2" ChromeHigh="#ff867cc2" ChromeLow="#ff0c0f5e" ChromeMedium="#ff18166c" ChromeMediumLow="#ff302587" ChromeWhite="White" ListLow="#ff18166c" ListMedium="#ff3c2d94" RegionColor="#ff1c1f46" />
</FluentTheme.Palettes>
<Style Selector="DataGrid:focus DataGridCell:current /template/ Grid#FocusVisual">
<Setter Property="IsVisible" Value="False" />
</Style>
</FluentTheme>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
<dialogHostAvalonia:DialogHostStyles/>
<StyleInclude Source="avares://AvaloniaProgressRing/Styles/ProgressRing.xaml"/>
</Application.Styles>
<Application.Resources>
<ResourceDictionary>
<!-- Theming -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ExpanderHeaderBackgroundPointerOver">#40FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="DataGridContentBackground">#40FFFFFF</SolidColorBrush>
<Bitmap x:Key="BannerBitmap">/Assets/imgs/banner_light.png</Bitmap>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ExpanderHeaderBackgroundPointerOver">#20000000</SolidColorBrush>
<SolidColorBrush x:Key="DataGridContentBackground">#ff000000</SolidColorBrush>
<SolidColorBrush x:Key="DialogHostOverlayBackgroundMixinBrush">#222244</SolidColorBrush>
<Bitmap x:Key="BannerBitmap">/Assets/imgs/banner_dark.png</Bitmap>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<!-- Control Property Override -->
<Thickness x:Key="TabItemMargin">12</Thickness>
<!-- Control Override -->
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="Overrides/Expander.axaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
+23
View File
@@ -0,0 +1,23 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace MercuryConverter.UI;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}
+50
View File
@@ -0,0 +1,50 @@
<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:progRing="clr-namespace:AvaloniaProgressRing;assembly=AvaloniaProgressRing"
xmlns:local="clr-namespace:MercuryConverter.UI.Dialogs"
x:Class="MercuryConverter.UI.Dialogs.DataScanning"
>
<Panel Margin="12">
<StackPanel>
<TextBlock Name="ScanStatus" FontSize="24" FontWeight="Light" Text="select your data folder..."/>
<TextBlock Name="ScanPath" IsVisible="false"/>
<StackPanel Margin="0 6 0 0" Name="ScanError" IsVisible="False">
<TextBlock>
<Run FontWeight="DemiBold" Text="ERROR" Foreground="Red"/>
<LineBreak/>
<Run Name="ErrorText" Text="Data pooped its pants"/>
</TextBlock>
</StackPanel>
<StackPanel Name="ScanInfo" Margin="0 12 0 12">
<TextBlock>
<Run FontWeight="Bold" Name="ScanInfoCountText"/>
<Run Text="songs added."/>
</TextBlock>
</StackPanel>
<Grid ColumnDefinitions="Auto, *" HorizontalAlignment="Stretch">
<progRing:ProgressRing Foreground="{DynamicResource SystemBaseMediumColor}"
Name="ProgressAnimation"
Grid.Column="0"
Width="36"
Height="36"
IsActive="True"
HorizontalAlignment="Left"
Margin="0,15,0,0"/>
<StackPanel Margin="0 12 0 0" Name="ButtonGroup" Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right" IsVisible="false">
<Button Name="BtnClose" Margin="6 0 0 0" Content="Close" Click="CloseHandler"/>
<Button Name="BtnSelectFolder" Margin="6 0 0 0" Content="Open Data Folder" Click="OpenDataHandler"/>
</StackPanel>
</Grid>
</StackPanel>
<!-- <StackPanel Name="SelectErrorView" IsVisible="true">
<TextBlock FontSize="24" FontWeight="Light" Text="couldn't fully open data folder"/>
<TextBlock Text="Unable to open PATH: ERROR"/>
<Button HorizontalAlignment="Right" Margin="0 12 0 0" Content="Open Data Folder" />
</StackPanel> -->
</Panel>
</UserControl>
+166
View File
@@ -0,0 +1,166 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using MercuryConverter.Data;
namespace MercuryConverter.UI.Dialogs;
public partial class DataScanning : UserControl
{
public static DataScanning? Instance { get; private set; }
public DataScanning()
{
Instance = this;
InitializeComponent();
if (!Design.IsDesignMode)
RunFlow();
}
public void RunFlow()
{
Task.Run(async () =>
{
var path = ""; // TODO: set to current/saved data path (move to config?)
var selectedPath = await BeginDirSelection();
if (selectedPath == "") // cancelled opening folder
{
// TODO:
// return and go to completed mode if scan already completed
// continue if no scan has been completed
// break if we already have a path but somehow not scanned
UISetError("No data folder provided.");
return;
}
if (!Directory.Exists(selectedPath))
{
UISetError("Folder does not exist.");
return;
}
if (!(File.Exists(Path.Combine(selectedPath, "MusicParameterTable.uasset")) && File.Exists(Path.Combine(selectedPath, "MusicParameterTable.uexp"))))
{
UISetError("Missing MusicParameterTable asset files.\nPlease ensure you've set up your data folder properly!");
return;
}
path = selectedPath;
UIScanningMode(path);
Database.SetupNew(path);
UIScanCompletedMode();
});
}
private void UISelectMode()
{
UISetError();
Dispatcher.UIThread.Post(() =>
{
ScanStatus.Text = "select your data folder...";
ScanPath.IsVisible = true;
ScanInfo.IsVisible = false;
ButtonGroup.IsVisible = false;
ProgressAnimation.IsVisible = true;
});
}
private void UIScanningMode(string path)
{
UISetError();
Dispatcher.UIThread.Post(() =>
{
ScanStatus.Text = "scanning...";
ScanPath.IsVisible = true;
ScanPath.Text = path;
ScanInfo.IsVisible = false;
ButtonGroup.IsVisible = false;
ProgressAnimation.IsVisible = true;
});
}
private void UIScanCompletedMode()
{
Dispatcher.UIThread.Post(() =>
{
ScanStatus.Text = "scan complete";
ScanPath.IsVisible = true;
ScanInfo.IsVisible = true;
ScanInfoCountText.Text = Database.Songs.Count.ToString();
ButtonGroup.IsVisible = true;
ProgressAnimation.IsVisible = false;
});
}
/// <summary>
/// Use only when a scan is no longer running.
/// </summary>
/// <param name="error"></param>
private void UISetError(string? error = null)
{
Dispatcher.UIThread.Post(() =>
{
if (error == null)
{
ScanError.IsVisible = false;
return;
}
ScanError.IsVisible = true;
ErrorText.Text = error;
ProgressAnimation.IsVisible = false;
ButtonGroup.IsVisible = true;
ScanStatus.Text = "an error has occurred";
});
}
private async Task<string> BeginDirSelection(string? startDir = null)
{
IReadOnlyList<IStorageFolder>? dirSelection = null;
UISelectMode();
await Dispatcher.UIThread.Invoke(async () =>
{
await Task.Delay(250);
var tl = TopLevel.GetTopLevel(MainWindow.Instance)!;
dirSelection = await tl.StorageProvider.OpenFolderPickerAsync
(
new FolderPickerOpenOptions
{
Title = "Locate Data Folder",
AllowMultiple = false,
SuggestedStartLocation = startDir == null ? null : await tl.StorageProvider.TryGetFolderFromPathAsync(startDir),
}
);
});
if (dirSelection!.Count <= 0)
{
return "";
}
return dirSelection!.First().TryGetLocalPath()!;
}
private void CloseHandler(object sender, RoutedEventArgs args)
{
Dispatcher.UIThread.Post(() =>
{
MainWindow.Instance!.Dialog.IsOpen = false;
});
}
private void OpenDataHandler(object sender, RoutedEventArgs args)
{
RunFlow();
}
}
+20
View File
@@ -0,0 +1,20 @@
<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:local="clr-namespace:MercuryConverter.UI.Dialogs"
x:Class="MercuryConverter.UI.Dialogs.Welcome"
>
<StackPanel Margin="12">
<TextBlock FontSize="24" FontWeight="Light">
welcome to <Run FontWeight="SemiBold" Text="mercury"/>converter!
</TextBlock>
<TextBlock HorizontalAlignment="Left" Padding="0 6 0 0">
<InlineUIContainer>
<HyperlinkButton Content="Setup your data folder" Padding="0" NavigateUri="https://github.com/muskit/MercuryConverter/blob/main/HOWTO.md" />
</InlineUIContainer>
before proceeding.
</TextBlock>
<Button Content="Open Data Folder" Click="ClickHandler" HorizontalAlignment="Right" Margin="0 24 0 0" />
</StackPanel>
</UserControl>
+19
View File
@@ -0,0 +1,19 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
using DialogHostAvalonia;
namespace MercuryConverter.UI.Dialogs;
public partial class Welcome : Window
{
public Welcome()
{
InitializeComponent();
}
private void ClickHandler(object sender, RoutedEventArgs args)
{
MainWindow.Instance!.Dialog.DialogContent = new DataScanning().Content;
}
}
+52
View File
@@ -0,0 +1,52 @@
<Window 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:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
xmlns:local="clr-namespace:MercuryConverter.UI"
x:Class="MercuryConverter.UI.MainWindow"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
Title="MercuryConverter"
Width="1024" Height="800"
MinWidth="800" MinHeight="600"
ExtendClientAreaToDecorationsHint="True"
>
<dialogHost:DialogHost CloseOnClickAway="False" Name="Dialog">
<dialogHost:DialogHost.DialogContent>
<Button Content="Test"/>
</dialogHost:DialogHost.DialogContent>
<!-- Components -->
<Panel>
<!-- Banner -->
<StackPanel>
<UserControl Background="{DynamicResource SystemAltHighColor}" Height="86"/>
</StackPanel>
<StackPanel HorizontalAlignment="Right">
<Image Height="86" Source="{DynamicResource BannerBitmap}"/>
</StackPanel>
<DockPanel>
<Menu Name="MenuBar" DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="_Open Data Folder..." Command="{Binding OpenDataHandler}" />
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_Data Guide" Command="{Binding OpenDataHOWTO}"/>
</MenuItem>
</Menu>
<TabControl>
<TabItem Header="selection">
<UserControl Name="SelectionControl"/>
</TabItem>
<TabItem Header="export">
</TabItem>
</TabControl>
</DockPanel>
</Panel>
</dialogHost:DialogHost>
</Window>
+58
View File
@@ -0,0 +1,58 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.Threading;
using MercuryConverter.UI.Dialogs;
using MercuryConverter.UI.Views;
namespace MercuryConverter.UI;
public partial class MainWindow : Window
{
public static MainWindow? Instance { get; private set; }
private bool initialShown = false;
public MainWindow()
{
Instance = this;
InitializeComponent();
DataContext = this;
// Force dark mode in designer
if (Design.IsDesignMode)
{
RequestedThemeVariant = ThemeVariant.Dark;
}
// Setup tab views
SelectionControl.Content = new Selection();
// Show dialog on startup
Activated += OnActivated;
}
private void OnActivated(object? sender, EventArgs e)
{
if (initialShown) return;
initialShown = true;
Dialog.DialogContent = new Welcome().Content;
Dialog.IsOpen = true;
}
public void OpenDataHandler()
{
Dialog.IsOpen = true;
Dialog.DialogContent = new DataScanning().Content;
}
public void OpenDataHOWTO()
{
var launcher = GetTopLevel(this)?.Launcher;
if (launcher != null)
{
launcher.LaunchUriAsync(new Uri("https://github.com/muskit/MercuryConverter/blob/main/HOWTO.md"));
}
}
}
+336
View File
@@ -0,0 +1,336 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:ClassModifier="internal">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel Orientation="Vertical" Spacing="20" Width="350" Height="600">
<Expander HorizontalAlignment="Stretch" ExpandDirection="Up" Header="Expand Up">
<StackPanel>
<TextBlock>Expanded content</TextBlock>
</StackPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" ExpandDirection="Down" Header="Expand Down">
<StackPanel>
<TextBlock>Expanded content</TextBlock>
</StackPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" ExpandDirection="Left" Header="Expand Left">
<StackPanel>
<TextBlock>Expanded content</TextBlock>
</StackPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" ExpandDirection="Right" Header="Expand Right">
<StackPanel>
<TextBlock>Expanded content</TextBlock>
</StackPanel>
</Expander>
<Expander HorizontalAlignment="Stretch" ExpandDirection="Up">
<Expander.Header>
<Grid ColumnDefinitions="*, Auto">
<TextBlock Grid.Column="0" Text="Control" />
<TextBlock Grid.Column="1" Text="Header" />
</Grid>
</Expander.Header>
<StackPanel>
<TextBlock>Expanded content</TextBlock>
</StackPanel>
</Expander>
<Expander ExpandDirection="Up" Header="Rounded" CornerRadius="25">
<StackPanel>
<TextBlock>Expanded content</TextBlock>
</StackPanel>
</Expander>
</StackPanel>
</Border>
</Design.PreviewWith>
<!-- Shared header/content -->
<x:Double x:Key="ExpanderMinHeight">48</x:Double>
<!-- Header -->
<HorizontalAlignment x:Key="ExpanderHeaderHorizontalContentAlignment">Stretch</HorizontalAlignment>
<VerticalAlignment x:Key="ExpanderHeaderVerticalContentAlignment">Center</VerticalAlignment>
<Thickness x:Key="ExpanderHeaderPadding">16,0,0,0</Thickness>
<Thickness x:Key="ExpanderHeaderBorderThickness">0</Thickness>
<Thickness x:Key="ExpanderChevronBorderThickness">0</Thickness>
<Thickness x:Key="ExpanderChevronMargin">20,0,8,0</Thickness>
<x:Double x:Key="ExpanderChevronButtonSize">32</x:Double>
<!-- Content -->
<Thickness x:Key="ExpanderContentPadding">8</Thickness>
<Thickness x:Key="ExpanderContentLeftBorderThickness">0,0,0,0</Thickness>
<Thickness x:Key="ExpanderContentUpBorderThickness">0,0,0,0</Thickness>
<Thickness x:Key="ExpanderContentRightBorderThickness">0,0,0,0</Thickness>
<Thickness x:Key="ExpanderContentDownBorderThickness">0,0,0,0</Thickness>
<ControlTheme x:Key="FluentExpanderToggleButtonTheme" TargetType="ToggleButton">
<Setter Property="Background" Value="{DynamicResource ExpanderHeaderBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderHeaderBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderHeaderBorderThickness}" />
<Setter Property="Foreground" Value="{DynamicResource ExpanderHeaderForeground}" />
<Setter Property="Padding" Value="{DynamicResource ExpanderHeaderPadding}" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="{DynamicResource ExpanderHeaderHorizontalContentAlignment}" />
<Setter Property="VerticalContentAlignment" Value="{DynamicResource ExpanderHeaderVerticalContentAlignment}" />
<Setter Property="Template">
<ControlTemplate>
<Border x:Name="ToggleButtonBackground"
CornerRadius="{TemplateBinding CornerRadius}"
Background="#00ffffff"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid x:Name="ToggleButtonGrid"
ColumnDefinitions="*,Auto">
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Foreground="{TemplateBinding Foreground}"
Margin="{TemplateBinding Padding}"/>
<Border x:Name="ExpandCollapseChevronBorder"
Grid.Column="1"
Width="{DynamicResource ExpanderChevronButtonSize}"
Height="{DynamicResource ExpanderChevronButtonSize}"
Margin="{DynamicResource ExpanderChevronMargin}"
CornerRadius="{DynamicResource ControlCornerRadius}"
BorderBrush="{DynamicResource ExpanderChevronBorderBrush}"
BorderThickness="{DynamicResource ExpanderChevronBorderThickness}"
Background="">
<Path x:Name="ExpandCollapseChevron"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="50%,50%"
Stretch="None"
Stroke="{DynamicResource ExpanderChevronForeground}"
StrokeThickness="1">
<Path.RenderTransform>
<RotateTransform />
</Path.RenderTransform>
</Path>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter>
<Style Selector="^[Tag=expanded] /template/ Path#ExpandCollapseChevron">
<Style.Animations>
<Animation FillMode="Both" Duration="0:0:0.0625">
<KeyFrame Cue="100%">
<Setter Property="RotateTransform.Angle" Value="180" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="^[Tag=collapsed] /template/ Path#ExpandCollapseChevron">
<Style.Animations>
<Animation FillMode="Both" Duration="0:0:0.0625">
<KeyFrame Cue="0%">
<Setter Property="RotateTransform.Angle" Value="180" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="RotateTransform.Angle" Value="0" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<!-- PointerOver -->
<Style Selector="^:pointerover /template/ Border#ToggleButtonBackground">
<Setter Property="Background" Value="{DynamicResource ExpanderHeaderBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderHeaderBorderBrushPointerOver}" />
</Style>
<Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ExpanderHeaderForegroundPointerOver}" />
</Style>
<Style Selector="^:pointerover /template/ Border#ExpandCollapseChevronBorder">
<Setter Property="Background" Value="" />
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderChevronBorderBrushPointerOver}" />
</Style>
<Style Selector="^:pointerover /template/ Path#ExpandCollapseChevron">
<Setter Property="Stroke" Value="{DynamicResource ExpanderChevronForegroundPointerOver}" />
</Style>
<!-- Pressed -->
<Style Selector="^:pressed /template/ Border#ToggleButtonBackground">
<Setter Property="Background" Value="{DynamicResource ExpanderHeaderBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderHeaderBorderBrushPressed}" />
</Style>
<Style Selector="^:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ExpanderHeaderForegroundPressed}" />
</Style>
<Style Selector="^:pressed /template/ Border#ExpandCollapseChevronBorder">
<Setter Property="Background" Value="" />
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderChevronBorderBrushPressed}" />
</Style>
<Style Selector="^:pressed /template/ Path#ExpandCollapseChevron">
<Setter Property="Stroke" Value="{DynamicResource ExpanderChevronForegroundPressed}" />
</Style>
<!-- Disabled -->
<Style Selector="^:disabled /template/ Border#ToggleButtonBackground">
<Setter Property="Background" Value="{DynamicResource ExpanderHeaderBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderHeaderBorderBrushDisabled}" />
</Style>
<Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ExpanderHeaderForegroundDisabled}" />
</Style>
<Style Selector="^:disabled /template/ Border#ExpandCollapseChevronBorder">
<Setter Property="Background" Value="" />
<Setter Property="BorderBrush" Value="" />
</Style>
<Style Selector="^:disabled /template/ Path#ExpandCollapseChevron">
<Setter Property="Stroke" Value="{DynamicResource ExpanderChevronForegroundDisabled}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="FluentExpanderToggleButtonUpTheme" TargetType="ToggleButton" BasedOn="{StaticResource FluentExpanderToggleButtonTheme}">
<Style Selector="^ /template/ Path#ExpandCollapseChevron">
<Setter Property="Data" Value="M 0 7 L 7 0 L 14 7" />
</Style>
</ControlTheme>
<ControlTheme x:Key="FluentExpanderToggleButtonDownTheme" TargetType="ToggleButton" BasedOn="{StaticResource FluentExpanderToggleButtonTheme}">
<Style Selector="^ /template/ Path#ExpandCollapseChevron">
<Setter Property="Data" Value="M 0 0 L 7 7 L 14 0" />
</Style>
</ControlTheme>
<ControlTheme x:Key="FluentExpanderToggleButtonLeftTheme" TargetType="ToggleButton" BasedOn="{StaticResource FluentExpanderToggleButtonTheme}">
<Style Selector="^ /template/ Path#ExpandCollapseChevron">
<Setter Property="Data" Value="M 7 0 L 0 7 L 7 14" />
</Style>
</ControlTheme>
<ControlTheme x:Key="FluentExpanderToggleButtonRightTheme" TargetType="ToggleButton" BasedOn="{StaticResource FluentExpanderToggleButtonTheme}">
<Style Selector="^ /template/ Path#ExpandCollapseChevron">
<Setter Property="Data" Value="M 0 0 L 7 7 L 0 14" />
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type Expander}" TargetType="Expander">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="MinWidth" Value="{DynamicResource FlyoutThemeMinWidth}" />
<Setter Property="MinHeight" Value="{DynamicResource ExpanderMinHeight}" />
<Setter Property="BorderBrush" Value="{DynamicResource ExpanderContentBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentDownBorderThickness}" />
<Setter Property="Padding" Value="{DynamicResource ExpanderContentPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="Template">
<ControlTemplate>
<DockPanel MinWidth="{TemplateBinding MinWidth}"
MaxWidth="{TemplateBinding MaxWidth}">
<ToggleButton x:Name="ExpanderHeader"
MinHeight="{TemplateBinding MinHeight}"
CornerRadius="{TemplateBinding CornerRadius}"
IsEnabled="{TemplateBinding IsEnabled}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}" />
<Border x:Name="ExpanderContent"
IsVisible="{TemplateBinding IsExpanded, Mode=TwoWay}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
MinHeight="{TemplateBinding MinHeight}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Padding="{TemplateBinding Padding}">
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</DockPanel>
</ControlTemplate>
</Setter>
<Style Selector="^ /template/ ToggleButton#ExpanderHeader:pressed">
<Setter Property="RenderTransform" Value="{x:Null}" />
</Style>
<Style Selector="^:left /template/ ToggleButton#ExpanderHeader, ^:right /template/ ToggleButton#ExpanderHeader">
<Setter Property="VerticalAlignment" Value="Stretch" />
</Style>
<Style Selector="^:expanded /template/ ToggleButton#ExpanderHeader">
<Setter Property="Tag" Value="expanded" />
</Style>
<Style Selector="^:not(:expanded) /template/ ToggleButton#ExpanderHeader">
<Setter Property="CornerRadius" Value="{Binding $parent[Expander].CornerRadius}" />
<Setter Property="Tag" Value="collapsed" />
</Style>
<Style Selector="^:expanded:up /template/ ToggleButton#ExpanderHeader">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource BottomCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="^:expanded:up /template/ Border#ExpanderContent">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource TopCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="^:expanded:down /template/ ToggleButton#ExpanderHeader">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource TopCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="^:expanded:down /template/ Border#ExpanderContent">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource BottomCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="^:expanded:left /template/ ToggleButton#ExpanderHeader">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="^:expanded:left /template/ Border#ExpanderContent">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="^:expanded:right /template/ ToggleButton#ExpanderHeader">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="^:expanded:right /template/ Border#ExpanderContent">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="^:left /template/ ToggleButton#ExpanderHeader">
<Setter Property="DockPanel.Dock" Value="Right" />
</Style>
<Style Selector="^:up /template/ ToggleButton#ExpanderHeader">
<Setter Property="DockPanel.Dock" Value="Bottom" />
</Style>
<Style Selector="^:right /template/ ToggleButton#ExpanderHeader">
<Setter Property="DockPanel.Dock" Value="Left" />
</Style>
<Style Selector="^:down /template/ ToggleButton#ExpanderHeader">
<Setter Property="DockPanel.Dock" Value="Top" />
</Style>
<Style Selector="^:left /template/ ToggleButton#ExpanderHeader">
<Setter Property="Theme" Value="{DynamicResource FluentExpanderToggleButtonLeftTheme}" />
</Style>
<Style Selector="^:up /template/ ToggleButton#ExpanderHeader">
<Setter Property="Theme" Value="{DynamicResource FluentExpanderToggleButtonUpTheme}" />
</Style>
<Style Selector="^:right /template/ ToggleButton#ExpanderHeader">
<Setter Property="Theme" Value="{DynamicResource FluentExpanderToggleButtonRightTheme}" />
</Style>
<Style Selector="^:down /template/ ToggleButton#ExpanderHeader">
<Setter Property="Theme" Value="{DynamicResource FluentExpanderToggleButtonDownTheme}" />
</Style>
<Style Selector="^:left /template/ Border#ExpanderContent">
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentLeftBorderThickness}" />
</Style>
<Style Selector="^:up /template/ Border#ExpanderContent">
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentUpBorderThickness}" />
</Style>
<Style Selector="^:right /template/ Border#ExpanderContent">
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentRightBorderThickness}" />
</Style>
<Style Selector="^:down /template/ Border#ExpanderContent">
<Setter Property="BorderThickness" Value="{DynamicResource ExpanderContentDownBorderThickness}" />
</Style>
</ControlTheme>
</ResourceDictionary>
+61
View File
@@ -0,0 +1,61 @@
<Panel 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:local="clr-namespace:MercuryConverter.UI.Views"
x:Class="MercuryConverter.UI.Views.Selection"
xmlns:data="clr-namespace:MercuryConverter.Data"
>
<DockPanel>
<!-- Sidebar -->
<TabControl Margin="8 8 0 0" Width="250" DockPanel.Dock="Right">
<TabControl.Resources>
<ResourceDictionary>
<Thickness x:Key="TabItemHeaderMargin">12 0 12 15</Thickness>
<Thickness x:Key="TabItemMargin">0 0 0 0</Thickness>
</ResourceDictionary>
</TabControl.Resources>
<TabItem Header="Info" FontSize="16">
<StackPanel Name="SelectionInfo">
<Image Name="InfoImageJacket" Margin="24 0 24 0" Source="/Assets/imgs/jacket-placeholder.png"/>
<TextBlock Name="InfoNameText" Text="Name" FontWeight="Bold" HorizontalAlignment="Center" Margin="0 10 0 0"/>
<TextBlock Name="InfoArtistText" Text="Artist" HorizontalAlignment="Center"/>
<TextBlock Name="InfoSourceText" Text="Source" HorizontalAlignment="Center"/>
</StackPanel>
</TabItem>
<TabItem Header="Filters" FontSize="16">
<StackPanel>
<!-- Sources Filter -->
<TextBlock Text="Sources" FontWeight="Bold" Margin="0 0 0 4"/>
<StackPanel Margin="10 0 0 0" Name="FilterSourceContainer"/>
<!-- Categories Filter -->
<TextBlock Text="Categories" FontWeight="Bold" Margin="0 20 0 4"/>
<StackPanel Margin="10 0 0 0" Name="FilterCategoryContainer"/>
</StackPanel>
</TabItem>
</TabControl>
<!-- Song Listing Table -->
<DockPanel>
<DockPanel Margin="0 4 0 8" DockPanel.Dock="Top">
<TextBox Watermark="Search for title, artist, designer..."/>
</DockPanel>
<UserControl Background="{DynamicResource DataGridContentBackground}">
<DataGrid Name="ListingTable" IsReadOnly="True" SelectionMode="Extended" ItemsSource="{x:Static data:Database.Songs}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Width="90" Binding="{Binding Id}"/>
<DataGridTextColumn Header="Title" Width="*" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Artist" Width="*" Binding="{Binding Artist}"/>
<DataGridTextColumn Header="Source" Width="150" Binding="{Binding SourceName}" SortMemberPath="Source"/>
</DataGrid.Columns>
<DataGrid.Styles>
<Style Selector="DataGridCell">
<Setter Property="ToolTip.Tip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text}" />
</Style>
</DataGrid.Styles>
</DataGrid>
</UserControl>
</DockPanel>
</DockPanel>
</Panel>
+69
View File
@@ -0,0 +1,69 @@
using Microsoft.VisualBasic;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading.Tasks;
using Avalonia.Controls;
using MercuryConverter.Data;
using Avalonia;
using Avalonia.Threading;
using Avalonia.Media;
using System.IO;
using Avalonia.Media.Imaging;
namespace MercuryConverter.UI.Views;
public partial class Selection : Panel
{
public Selection()
{
InitializeComponent();
ListingTable.CellPointerPressed += OnCellClicked;
ListingTable.SelectionMode = DataGridSelectionMode.Extended;
foreach (var (k, v) in Consts.NUM_SOURCE)
{
FilterSourceContainer.Children.Add(
new CheckBox
{
Name = $"FilterSourceCheckbox{k}",
Content = v,
}
);
}
foreach (var (k, v) in Consts.CATEGORY_INDEX)
{
if (k == -1)
continue;
FilterCategoryContainer.Children.Add(
new CheckBox
{
Name = $"FilterCategoryCheckbox{k}",
Content = v,
}
);
}
}
private void OnCellClicked(object? sender, DataGridCellPointerPressedEventArgs e)
{
Console.WriteLine($"{e.PointerPressedEventArgs.KeyModifiers} - {e.Cell}");
if (e.Row.DataContext is Song song)
{
Console.WriteLine($"{song.Id}: {song.Artist} - {song.Name}");
Dispatcher.UIThread.Post(() =>
{
if (song.Jacket != null)
{
var file = File.OpenRead(song.Jacket);
InfoImageJacket.Source = new Bitmap(file);
}
InfoNameText.Text = song.Name;
InfoArtistText.Text = song.Artist;
InfoSourceText.Text = song.SourceName;
});
}
}
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="MercuryConverter.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>