Merge pull request #1193 from Mbucari/master
Add support for custom themes in chardonnay
@ -1,34 +1,36 @@
|
|||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 524 524" enable-background="new 0 0 524 524">
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 288 288" enable-background="new 0 0 288 288">
|
||||||
<defs>
|
<defs>
|
||||||
<g id="glass">
|
<g id="glass">
|
||||||
<path fill-rule="evenodd" d=
|
<path transform="translate(16 16)" fill-rule="evenodd" d=
|
||||||
"M262,8
|
"M177,16
|
||||||
h-117
|
H79
|
||||||
a 192,200 0 0 0 -36,82
|
A 32.0781 63.7932 -1.5106 0 0 66 80
|
||||||
a 222,334 41 0 0 138,236
|
A 158.789 471.1259 41.9466 0 0 90 131
|
||||||
v158
|
A 81.7197 122.0515 35.3745 0 0 128 143.3484
|
||||||
h-81
|
A 81.7197 122.0515 -35.3745 0 0 166 131
|
||||||
a 16,16 0 0 0 0,32
|
A 158.789 471.1259 -41.9466 0 0 190 80
|
||||||
h192
|
A 32.0781 63.7932 1.5106 0 0 177 16
|
||||||
a 16 16 0 0 0 0,-32
|
L 184 0
|
||||||
h-81
|
A 44.7901 78.5247 1.1521 0 1 194 122
|
||||||
v-158
|
A 97.0039 135.3148 -36.2124 0 1 136 159
|
||||||
a 222,334 -41 0 0 138,-236
|
V 240
|
||||||
a 192,200 0 0 0 -36,-82
|
H 176
|
||||||
h-117
|
A 8 8 0 0 1 176 256
|
||||||
m-99,30
|
H 80
|
||||||
a 192,200 0 0 0 -26,95
|
A 8 8 0 0 1 80 240
|
||||||
a 187.5,334 35 0 0 125,159
|
H 120
|
||||||
a 187.5,334 -35 0 0 125,-159
|
V 159
|
||||||
a 192,200 0 0 0 -26,-95
|
A 97.0039 135.3148 36.2124 0 1 62 122
|
||||||
h-198
|
A 44.7901 78.5247 -1.1521 0 1 72 0
|
||||||
|
H184
|
||||||
z"/>
|
z"/>
|
||||||
</g>
|
</g>
|
||||||
<g id="wine-level">
|
<g transform="translate(16 16)" id="wine-level">
|
||||||
<path d=
|
<path d=
|
||||||
"M158,136
|
"M182,64
|
||||||
a 168,305 35 0 0 104,136
|
H 74
|
||||||
a 168,305 -35 0 0 104,-136
|
A 115.9979 308.8033 38.9474 0 0 128 134.4277
|
||||||
|
A 115.9979 308.8033 -38.9474 0 0 182,64
|
||||||
z"/>
|
z"/>
|
||||||
</g>
|
</g>
|
||||||
</defs>
|
</defs>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 1.2 KiB |
@ -1,30 +1,31 @@
|
|||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" enable-background="new 0 0 512 512">
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 288 288" enable-background="new 0 0 288 288">
|
||||||
|
<g>
|
||||||
<g transform="translate(0 80) rotate(90 256,256)">
|
<path transform="rotate(90 128,128) translate(60 -16)" fill-rule="evenodd" d=
|
||||||
<path id="glass" d=
|
"M177,16
|
||||||
"M139,2
|
H79
|
||||||
A 192,200 0 0 0 103,84
|
A 32.0781 63.7932 -1.5106 0 0 66 80
|
||||||
A 222,334 41 0 0 241,320
|
A 158.789 471.1259 41.9466 0 0 90 131
|
||||||
V478
|
A 81.7197 122.0515 35.3745 0 0 128 143.3484
|
||||||
H160
|
A 81.7197 122.0515 -35.3745 0 0 166 131
|
||||||
A 16,16 0 0 0 160,510
|
A 158.789 471.1259 -41.9466 0 0 190 80
|
||||||
H352
|
A 32.0781 63.7932 1.5106 0 0 177 16
|
||||||
A16 16 0 0 0 352,478
|
L 184 0
|
||||||
H271
|
A 44.7901 78.5247 1.1521 0 1 194 122
|
||||||
V320
|
A 97.0039 135.3148 -36.2124 0 1 136 159
|
||||||
A 222,334 -41 0 0 409,84
|
V 240
|
||||||
A 192,200 0 0 0 373,2
|
H 176
|
||||||
M355,32
|
A 8 8 0 0 1 176 256
|
||||||
A 192,200 0 0 1 381,127
|
H 80
|
||||||
A 187.5,334 -35 0 1 256,286
|
A 8 8 0 0 1 80 240
|
||||||
A 187.5,334 35 0 1 131,127
|
H 120
|
||||||
A 192,200 0 0 1 157,32
|
V 159
|
||||||
H355
|
A 97.0039 135.3148 36.2124 0 1 62 122
|
||||||
z" />
|
A 44.7901 78.5247 -1.1521 0 1 72 0
|
||||||
<path id="wine-level" d=
|
H184
|
||||||
"M345,44
|
M170,115
|
||||||
A 192,184 0 0 1 366,126
|
V24
|
||||||
A 320,180 55 0 1 345,226
|
A 19.5181 45.9183 -3.3549 0 1 182.4322 69.5
|
||||||
z"/>
|
A 19.5181 45.9183 3.3549 0 1 170 115
|
||||||
</g>
|
z"/>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 936 B |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 498 B After Width: | Height: | Size: 601 B |
|
After Width: | Height: | Size: 754 B |
|
After Width: | Height: | Size: 929 B |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 36 KiB |
@ -20,6 +20,11 @@
|
|||||||
<ControlTheme x:Key="{x:Type DataGridCell}" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
|
<ControlTheme x:Key="{x:Type DataGridCell}" TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
|
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
|
||||||
</ControlTheme>
|
</ControlTheme>
|
||||||
|
<ControlTheme x:Key="{x:Type DataGridColumnHeader}" TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
|
||||||
|
<Setter Property="Padding" Value="6,0,0,0" />
|
||||||
|
</ControlTheme>
|
||||||
|
<x:Double x:Key="DataGridSortIconMinWidth">0</x:Double>
|
||||||
|
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
<ResourceDictionary x:Key="Light">
|
<ResourceDictionary x:Key="Light">
|
||||||
<SolidColorBrush x:Key="SeriesEntryGridBackgroundBrush" Opacity="0.3" Color="#abffab" />
|
<SolidColorBrush x:Key="SeriesEntryGridBackgroundBrush" Opacity="0.3" Color="#abffab" />
|
||||||
@ -28,17 +33,11 @@
|
|||||||
<SolidColorBrush x:Key="ProcessQueueBookCancelledBrush" Color="Khaki" />
|
<SolidColorBrush x:Key="ProcessQueueBookCancelledBrush" Color="Khaki" />
|
||||||
<SolidColorBrush x:Key="HyperlinkNew" Color="Blue" />
|
<SolidColorBrush x:Key="HyperlinkNew" Color="Blue" />
|
||||||
<SolidColorBrush x:Key="HyperlinkVisited" Color="Purple" />
|
<SolidColorBrush x:Key="HyperlinkVisited" Color="Purple" />
|
||||||
<SolidColorBrush x:Key="ProcessQueueBookDefaultBrush" Color="White" />
|
|
||||||
<SolidColorBrush x:Key="SystemOpaqueBase" Color="White" />
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="CancelRed" Color="FireBrick" />
|
<SolidColorBrush x:Key="CancelRed" Color="FireBrick" />
|
||||||
<SolidColorBrush x:Key="IconFill" Color="#231F20" />
|
<SolidColorBrush x:Key="IconFill" Color="#231F20" />
|
||||||
<SolidColorBrush x:Key="StoplightRed" Color="#F06060" />
|
<SolidColorBrush x:Key="StoplightRed" Color="#F06060" />
|
||||||
<SolidColorBrush x:Key="StoplightYellow" Color="#F0E160" />
|
<SolidColorBrush x:Key="StoplightYellow" Color="#F0E160" />
|
||||||
<SolidColorBrush x:Key="StoplightGreen" Color="#70FA70" />
|
<SolidColorBrush x:Key="StoplightGreen" Color="#70FA70" />
|
||||||
|
|
||||||
<SolidColorBrush x:Key="DisabledGrayBrush" Opacity="0.4" Color="{StaticResource SystemChromeMediumColor}" />
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
<ResourceDictionary x:Key="Dark">
|
<ResourceDictionary x:Key="Dark">
|
||||||
<SolidColorBrush x:Key="SeriesEntryGridBackgroundBrush" Opacity="0.3" Color="#bed2fa" />
|
<SolidColorBrush x:Key="SeriesEntryGridBackgroundBrush" Opacity="0.3" Color="#bed2fa" />
|
||||||
@ -47,35 +46,32 @@
|
|||||||
<SolidColorBrush x:Key="ProcessQueueBookCancelledBrush" Color="#4e4b15" />
|
<SolidColorBrush x:Key="ProcessQueueBookCancelledBrush" Color="#4e4b15" />
|
||||||
<SolidColorBrush x:Key="HyperlinkNew" Color="CornflowerBlue" />
|
<SolidColorBrush x:Key="HyperlinkNew" Color="CornflowerBlue" />
|
||||||
<SolidColorBrush x:Key="HyperlinkVisited" Color="Orchid" />
|
<SolidColorBrush x:Key="HyperlinkVisited" Color="Orchid" />
|
||||||
<SolidColorBrush x:Key="ProcessQueueBookDefaultBrush" Color="Black" />
|
|
||||||
<SolidColorBrush x:Key="SystemOpaqueBase" Color="Black" />
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="CancelRed" Color="#802727" />
|
<SolidColorBrush x:Key="CancelRed" Color="#802727" />
|
||||||
<SolidColorBrush x:Key="IconFill" Color="#DCE0DF" />
|
<SolidColorBrush x:Key="IconFill" Color="#DCE0DF" />
|
||||||
<SolidColorBrush x:Key="StoplightRed" Color="#7d1f1f" />
|
<SolidColorBrush x:Key="StoplightRed" Color="#7d1f1f" />
|
||||||
<SolidColorBrush x:Key="StoplightYellow" Color="#7d7d1f" />
|
<SolidColorBrush x:Key="StoplightYellow" Color="#7d7d1f" />
|
||||||
<SolidColorBrush x:Key="StoplightGreen" Color="#1f7d1f" />
|
<SolidColorBrush x:Key="StoplightGreen" Color="#1f7d1f" />
|
||||||
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="DisabledGrayBrush" Opacity="0.4" Color="{StaticResource SystemChromeMediumColor}" />
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ResourceDictionary.ThemeDictionaries>
|
</ResourceDictionary.ThemeDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme />
|
|
||||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/FluentTheme.xaml"/>
|
|
||||||
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
||||||
|
<StyleInclude Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml" />
|
||||||
<StyleInclude Source="/Assets/LibationVectorIcons.xaml"/>
|
<StyleInclude Source="/Assets/LibationVectorIcons.xaml"/>
|
||||||
<StyleInclude Source="/Assets/DataGridColumnHeader.xaml"/>
|
<FluentTheme>
|
||||||
|
<FluentTheme.Palettes>
|
||||||
|
<ColorPaletteResources x:Key="Light" />
|
||||||
|
<ColorPaletteResources x:Key="Dark" />
|
||||||
|
</FluentTheme.Palettes>
|
||||||
|
</FluentTheme>
|
||||||
|
|
||||||
<Style Selector="TextBox[IsReadOnly=true]">
|
<Style Selector="TextBox[IsReadOnly=true]">
|
||||||
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
<Setter Property="Background" Value="{DynamicResource SystemChromeDisabledHighColor}" />
|
||||||
<Setter Property="CaretBrush" Value="{DynamicResource SystemControlTransparentBrush}" />
|
<Setter Property="CaretBrush" Value="{DynamicResource SystemControlTransparentBrush}" />
|
||||||
<Style Selector="^ /template/ Border#PART_BorderElement">
|
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||||
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
<Setter Property="Background" Value="{DynamicResource SystemChromeDisabledHighColor}" />
|
||||||
</Style>
|
</Style>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="controls|LinkLabel">
|
<Style Selector="controls|LinkLabel">
|
||||||
@ -84,6 +80,9 @@
|
|||||||
</Style>
|
</Style>
|
||||||
<Style Selector="Button">
|
<Style Selector="Button">
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||||
|
<Style Selector="^">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource SystemChromeAltLowColor}" />
|
||||||
|
</Style>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="ScrollBar">
|
<Style Selector="ScrollBar">
|
||||||
<!-- It's called AutoHide, but this is really the mouseover shrink/expand. -->
|
<!-- It's called AutoHide, but this is really the mouseover shrink/expand. -->
|
||||||
@ -91,61 +90,14 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="dialogs|DialogWindow">
|
<Style Selector="dialogs|DialogWindow">
|
||||||
<Style Selector="^[UseCustomTitleBar=false]">
|
|
||||||
<Setter Property="SystemDecorations" Value="Full"/>
|
<Setter Property="SystemDecorations" Value="Full"/>
|
||||||
|
<Setter Property="Icon" Value="/Assets/libation.ico"/>
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate>
|
<ControlTemplate>
|
||||||
<ContentPresenter Background="{DynamicResource SystemControlBackgroundAltHighBrush}" Content="{TemplateBinding Content}" />
|
<ContentPresenter Background="{DynamicResource SystemRegionColor}" Content="{TemplateBinding Content}" />
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="^[UseCustomTitleBar=true]">
|
|
||||||
<Style Selector="^[CanResize=false] Border#DialogWindowFormBorder">
|
|
||||||
<Setter Property="BorderThickness" Value="2" />
|
|
||||||
</Style>
|
|
||||||
<Setter Property="SystemDecorations" Value="BorderOnly"/>
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<Border Name="DialogWindowFormBorder" BorderBrush="{DynamicResource SystemBaseMediumLowColor}" Background="{DynamicResource SystemControlBackgroundAltHighBrush}">
|
|
||||||
<Grid RowDefinitions="30,*">
|
|
||||||
<Border Name="DialogWindowTitleBorder" Margin="5,0" Background="{DynamicResource SystemAltMediumColor}">
|
|
||||||
<Border.Styles>
|
|
||||||
<Style Selector="Button#DialogCloseButton">
|
|
||||||
<Style Selector="^:pointerover">
|
|
||||||
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
|
|
||||||
<Setter Property="Background" Value="Red" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="^ Path">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource IconFill}" />
|
|
||||||
</Style>
|
|
||||||
</Style>
|
|
||||||
<Style Selector="^:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
|
|
||||||
<Setter Property="Background" Value="Transparent" />
|
|
||||||
<Setter Property="BorderBrush" Value="Transparent" />
|
|
||||||
</Style>
|
|
||||||
</Style>
|
|
||||||
</Border.Styles>
|
|
||||||
|
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
|
||||||
|
|
||||||
<Path Name="DialogWindowTitleIcon" Margin="3,5,0,5" Fill="{DynamicResource IconFill}" Stretch="Uniform" Data="{StaticResource LibationGlassIcon}"/>
|
|
||||||
|
|
||||||
<TextBlock Name="DialogWindowTitleTextBlock" Margin="8,0,0,0" VerticalAlignment="Center" FontWeight="DemiBold" FontSize="12" Grid.Column="1" Text="{TemplateBinding Title}" />
|
|
||||||
|
|
||||||
<Button Name="DialogCloseButton" Grid.Column="2">
|
|
||||||
<Path Fill="{DynamicResource SystemControlBackgroundBaseLowBrush}" VerticalAlignment="Center" Stretch="Uniform" RenderTransform="{StaticResource Rotate45Transform}" Data="{StaticResource CancelButtonIcon}" />
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
<Path Stroke="{DynamicResource SystemBaseMediumLowColor}" StrokeThickness="1" VerticalAlignment="Bottom" Stretch="Fill" Data="M0,0 L1,0" />
|
|
||||||
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}" />
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
|
|
||||||
<NativeMenu.Menu>
|
<NativeMenu.Menu>
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Media;
|
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using LibationAvalonia.Dialogs;
|
using LibationAvalonia.Dialogs;
|
||||||
@ -13,19 +12,22 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using Dinah.Core;
|
||||||
|
using LibationAvalonia.Themes;
|
||||||
|
using Avalonia.Data.Core.Plugins;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
namespace LibationAvalonia
|
namespace LibationAvalonia
|
||||||
{
|
{
|
||||||
public class App : Application
|
public class App : Application
|
||||||
{
|
{
|
||||||
public static MainWindow MainWindow { get; private set; }
|
public static Task<List<DataLayer.LibraryBook>>? LibraryTask { get; set; }
|
||||||
public static IBrush ProcessQueueBookFailedBrush { get; private set; }
|
public static ChardonnayTheme? DefaultThemeColors { get; private set; }
|
||||||
public static IBrush ProcessQueueBookCompletedBrush { get; private set; }
|
public static MainWindow? MainWindow { get; private set; }
|
||||||
public static IBrush ProcessQueueBookCancelledBrush { get; private set; }
|
public static Uri AssetUriBase { get; } = new("avares://Libation/Assets/");
|
||||||
public static IBrush ProcessQueueBookDefaultBrush { get; private set; }
|
public static new Application Current => Application.Current ?? throw new InvalidOperationException("The Avalonia app hasn't started yet.");
|
||||||
public static IBrush SeriesEntryGridBackgroundBrush { get; private set; }
|
|
||||||
|
|
||||||
public static readonly Uri AssetUriBase = new("avares://Libation/Assets/");
|
|
||||||
public static Stream OpenAsset(string assetRelativePath)
|
public static Stream OpenAsset(string assetRelativePath)
|
||||||
=> AssetLoader.Open(new Uri(AssetUriBase, assetRelativePath));
|
=> AssetLoader.Open(new Uri(AssetUriBase, assetRelativePath));
|
||||||
|
|
||||||
@ -34,12 +36,16 @@ namespace LibationAvalonia
|
|||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<List<DataLayer.LibraryBook>> LibraryTask;
|
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
|
DefaultThemeColors = ChardonnayTheme.GetLiveTheme();
|
||||||
|
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
|
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
|
||||||
|
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
|
||||||
|
DisableAvaloniaDataAnnotationValidation();
|
||||||
|
|
||||||
var config = Configuration.Instance;
|
var config = Configuration.Instance;
|
||||||
|
|
||||||
if (!config.LibationSettingsAreValid)
|
if (!config.LibationSettingsAreValid)
|
||||||
@ -69,11 +75,23 @@ namespace LibationAvalonia
|
|||||||
|
|
||||||
base.OnFrameworkInitializationCompleted();
|
base.OnFrameworkInitializationCompleted();
|
||||||
}
|
}
|
||||||
|
private void DisableAvaloniaDataAnnotationValidation()
|
||||||
private async void Setup_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
|
||||||
{
|
{
|
||||||
var setupDialog = sender as SetupDialog;
|
// Get an array of plugins to remove
|
||||||
var desktop = ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
|
var dataValidationPluginsToRemove =
|
||||||
|
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
|
||||||
|
|
||||||
|
// remove each entry found
|
||||||
|
foreach (var plugin in dataValidationPluginsToRemove)
|
||||||
|
{
|
||||||
|
BindingPlugins.DataValidators.Remove(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Setup_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not SetupDialog setupDialog || ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -87,7 +105,7 @@ namespace LibationAvalonia
|
|||||||
|
|
||||||
if (setupDialog.Config.LibationSettingsAreValid)
|
if (setupDialog.Config.LibationSettingsAreValid)
|
||||||
{
|
{
|
||||||
string theme = setupDialog.SelectedTheme.Content as string;
|
string? theme = setupDialog.SelectedTheme.Content as string;
|
||||||
|
|
||||||
setupDialog.Config.SetString(theme, nameof(ThemeVariant));
|
setupDialog.Config.SetString(theme, nameof(ThemeVariant));
|
||||||
|
|
||||||
@ -143,7 +161,7 @@ namespace LibationAvalonia
|
|||||||
desktop.MainWindow = libationFilesDialog;
|
desktop.MainWindow = libationFilesDialog;
|
||||||
libationFilesDialog.Show();
|
libationFilesDialog.Show();
|
||||||
|
|
||||||
void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
|
void WindowClosing(object? sender, System.ComponentModel.CancelEventArgs e)
|
||||||
{
|
{
|
||||||
libationFilesDialog.Closing -= WindowClosing;
|
libationFilesDialog.Closing -= WindowClosing;
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
@ -201,16 +219,9 @@ namespace LibationAvalonia
|
|||||||
|
|
||||||
private static void ShowMainWindow(IClassicDesktopStyleApplicationLifetime desktop)
|
private static void ShowMainWindow(IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
Current.RequestedThemeVariant = Configuration.Instance.GetString(propertyName: nameof(ThemeVariant)) switch
|
Configuration.Instance.PropertyChanged += ThemeVariant_PropertyChanged;
|
||||||
{
|
OpenAndApplyTheme(Configuration.Instance.GetString(propertyName: nameof(ThemeVariant)));
|
||||||
nameof(ThemeVariant.Dark) => ThemeVariant.Dark,
|
|
||||||
nameof(ThemeVariant.Light) => ThemeVariant.Light,
|
|
||||||
// "System"
|
|
||||||
_ => ThemeVariant.Default
|
|
||||||
};
|
|
||||||
|
|
||||||
//Reload colors for current theme
|
|
||||||
LoadStyles();
|
|
||||||
var mainWindow = new MainWindow();
|
var mainWindow = new MainWindow();
|
||||||
desktop.MainWindow = MainWindow = mainWindow;
|
desktop.MainWindow = MainWindow = mainWindow;
|
||||||
mainWindow.Loaded += MainWindow_Loaded;
|
mainWindow.Loaded += MainWindow_Loaded;
|
||||||
@ -218,19 +229,23 @@ namespace LibationAvalonia
|
|||||||
mainWindow.Show();
|
mainWindow.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async void MainWindow_Loaded(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
[PropertyChangeFilter(nameof(ThemeVariant))]
|
||||||
|
private static void ThemeVariant_PropertyChanged(object sender, PropertyChangedEventArgsEx e)
|
||||||
|
=> OpenAndApplyTheme(e.NewValue as string);
|
||||||
|
|
||||||
|
private static void OpenAndApplyTheme(string? themeVariant)
|
||||||
|
{
|
||||||
|
using var themePersister = ChardonnayThemePersister.Create();
|
||||||
|
themePersister?.Target.ApplyTheme(themeVariant);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async void MainWindow_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (LibraryTask is not null && MainWindow is not null)
|
||||||
{
|
{
|
||||||
var library = await LibraryTask;
|
var library = await LibraryTask;
|
||||||
await Dispatcher.UIThread.InvokeAsync(() => MainWindow.OnLibraryLoadedAsync(library));
|
await Dispatcher.UIThread.InvokeAsync(() => MainWindow.OnLibraryLoadedAsync(library));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LoadStyles()
|
|
||||||
{
|
|
||||||
ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookFailedBrush));
|
|
||||||
ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCompletedBrush));
|
|
||||||
ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCancelledBrush));
|
|
||||||
SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources(nameof(SeriesEntryGridBackgroundBrush));
|
|
||||||
ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookDefaultBrush));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,104 +0,0 @@
|
|||||||
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:collections="using:Avalonia.Collections">
|
|
||||||
<Styles.Resources>
|
|
||||||
<!--
|
|
||||||
Based on Fluent template from v11.0.0-preview8
|
|
||||||
Modified sort arrow positioning to make more room for header text
|
|
||||||
-->
|
|
||||||
<ControlTheme x:Key="{x:Type DataGridColumnHeader}" TargetType="DataGridColumnHeader">
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource DataGridColumnHeaderForegroundBrush}" />
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderBackgroundBrush}" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
|
||||||
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
<Setter Property="Padding" Value="8,0,0,0" />
|
|
||||||
<Setter Property="FontSize" Value="12" />
|
|
||||||
<Setter Property="MinHeight" Value="32" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<Border x:Name="HeaderBorder"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
CornerRadius="{TemplateBinding CornerRadius}">
|
|
||||||
<Grid Name="PART_ColumnHeaderRoot" ColumnDefinitions="*,Auto">
|
|
||||||
|
|
||||||
<Grid Margin="{TemplateBinding Padding}"
|
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="16" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<ContentPresenter Content="{TemplateBinding Content}"
|
|
||||||
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
|
||||||
|
|
||||||
<Path Name="SortIcon"
|
|
||||||
IsVisible="False"
|
|
||||||
Grid.Column="1"
|
|
||||||
Height="12"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Fill="{TemplateBinding Foreground}"
|
|
||||||
Stretch="Uniform" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Rectangle Name="VerticalSeparator"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="1"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="{TemplateBinding SeparatorBrush}"
|
|
||||||
IsVisible="{TemplateBinding AreSeparatorsVisible}" />
|
|
||||||
|
|
||||||
<Grid x:Name="FocusVisual" IsHitTestVisible="False"
|
|
||||||
IsVisible="False">
|
|
||||||
<Rectangle x:Name="FocusVisualPrimary"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
|
|
||||||
StrokeThickness="2" />
|
|
||||||
<Rectangle x:Name="FocusVisualSecondary"
|
|
||||||
Margin="2"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
|
|
||||||
StrokeThickness="1" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
|
|
||||||
<Style Selector="^:focus-visible /template/ Grid#FocusVisual">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="^:pointerover /template/ Grid#PART_ColumnHeaderRoot">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderHoveredBackgroundBrush}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="^:pressed /template/ Grid#PART_ColumnHeaderRoot">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderPressedBackgroundBrush}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="^:dragIndicator">
|
|
||||||
<Setter Property="Opacity" Value="0.5" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="^:sortascending /template/ Path#SortIcon">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
<Setter Property="Data" Value="{StaticResource DataGridSortIconAscendingPath}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="^:sortdescending /template/ Path#SortIcon">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
<Setter Property="Data" Value="{StaticResource DataGridSortIconDescendingPath}" />
|
|
||||||
</Style>
|
|
||||||
</ControlTheme>
|
|
||||||
</Styles.Resources>
|
|
||||||
</Styles>
|
|
||||||
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 35 KiB |
@ -1,8 +1,8 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia;
|
||||||
using Avalonia.Media;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml.MarkupExtensions;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
using LibationAvalonia.Dialogs;
|
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -11,17 +11,21 @@ namespace LibationAvalonia
|
|||||||
{
|
{
|
||||||
internal static class AvaloniaUtils
|
internal static class AvaloniaUtils
|
||||||
{
|
{
|
||||||
public static IBrush GetBrushFromResources(string name)
|
public static T DynamicResource<T>(this T control, AvaloniaProperty prop, object resourceKey) where T : Control
|
||||||
=> GetBrushFromResources(name, Brushes.Transparent);
|
|
||||||
public static IBrush GetBrushFromResources(string name, IBrush defaultBrush)
|
|
||||||
{
|
{
|
||||||
if ((App.Current?.TryGetResource(name, App.Current.ActualThemeVariant, out var value) ?? false) && value is IBrush brush)
|
control[!prop] = new DynamicResourceExtension(resourceKey);
|
||||||
return brush;
|
return control;
|
||||||
return defaultBrush;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<DialogResult> ShowDialogAsync(this DialogWindow dialogWindow, Window? owner = null)
|
public static Task<DialogResult> ShowDialogAsync(this Dialogs.DialogWindow dialogWindow, Window? owner = null)
|
||||||
=> dialogWindow.ShowDialog<DialogResult>(owner ?? App.MainWindow);
|
=> ((owner ?? App.MainWindow) is Window window)
|
||||||
|
? dialogWindow.ShowDialog<DialogResult>(window)
|
||||||
|
: Task.FromResult(DialogResult.None);
|
||||||
|
|
||||||
|
public static Task<DialogResult> ShowDialogAsync(this Dialogs.Login.WebLoginDialog dialogWindow, Window? owner = null)
|
||||||
|
=> ((owner ?? App.MainWindow) is Window window)
|
||||||
|
? dialogWindow.ShowDialog<DialogResult>(window)
|
||||||
|
: Task.FromResult(DialogResult.None);
|
||||||
|
|
||||||
public static Window? GetParentWindow(this Control control) => control.GetVisualRoot() as Window;
|
public static Window? GetParentWindow(this Control control) => control.GetVisualRoot() as Window;
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<ContentControl.Styles>
|
<ContentControl.Styles>
|
||||||
<Style Selector="controls|GroupBox">
|
<Style Selector="controls|GroupBox">
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource SystemBaseMediumLowColor}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemBaseMediumColor}" />
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="CornerRadius" Value="3" />
|
<Setter Property="CornerRadius" Value="3" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
@ -28,7 +28,7 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Name="PART_Label"
|
Name="PART_Label"
|
||||||
Padding="4,0"
|
Padding="4,0"
|
||||||
Background="{DynamicResource SystemAltHighColor}"
|
Background="{DynamicResource SystemRegionColor}"
|
||||||
Text="{TemplateBinding Label}"
|
Text="{TemplateBinding Label}"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -158,15 +158,24 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
FontSize="16"
|
FontSize="16"
|
||||||
|
Margin="0,0,15,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="Theme: "/>
|
Text="Theme:"/>
|
||||||
|
|
||||||
<controls:WheelComboBox
|
<controls:WheelComboBox
|
||||||
|
Name="ThemeComboBox"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
MinWidth="80"
|
MinWidth="80"
|
||||||
SelectedItem="{CompiledBinding ThemeVariant, Mode=TwoWay}"
|
SelectedItem="{CompiledBinding ThemeVariant, Mode=TwoWay}"
|
||||||
ItemsSource="{CompiledBinding Themes}"/>
|
ItemsSource="{CompiledBinding Themes}"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Padding="20,0"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Content="Edit Theme Colors"
|
||||||
|
Click="EditThemeColors_Click"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@ -1,13 +1,18 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
|
using LibationAvalonia.Dialogs;
|
||||||
using LibationAvalonia.ViewModels.Settings;
|
using LibationAvalonia.ViewModels.Settings;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
namespace LibationAvalonia.Controls.Settings
|
namespace LibationAvalonia.Controls.Settings
|
||||||
{
|
{
|
||||||
public partial class Important : UserControl
|
public partial class Important : UserControl
|
||||||
{
|
{
|
||||||
|
private ImportantSettingsVM? ViewModel => DataContext as ImportantSettingsVM;
|
||||||
public Important()
|
public Important()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -16,6 +21,42 @@ namespace LibationAvalonia.Controls.Settings
|
|||||||
_ = Configuration.Instance.LibationFiles;
|
_ = Configuration.Instance.LibationFiles;
|
||||||
DataContext = new ImportantSettingsVM(Configuration.Instance);
|
DataContext = new ImportantSettingsVM(Configuration.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThemeComboBox.SelectionChanged += ThemeComboBox_SelectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditThemeColors_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (App.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
|
||||||
|
{
|
||||||
|
//Only allow a single instance of the theme picker
|
||||||
|
//Show it as a window, not a dialog, so users can preview
|
||||||
|
//their changes throughout the entire app.
|
||||||
|
if (lifetime.Windows.OfType<ThemePickerDialog>().FirstOrDefault() is ThemePickerDialog dialog)
|
||||||
|
{
|
||||||
|
dialog.BringIntoView();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var themePicker = new ThemePickerDialog();
|
||||||
|
themePicker.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThemeComboBox_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
//Remove the combo box before changing the theme, then re-add it.
|
||||||
|
//This is a workaround to a crash that will happen if the theme
|
||||||
|
//is changed while the combo box is open
|
||||||
|
ThemeComboBox.SelectionChanged -= ThemeComboBox_SelectionChanged;
|
||||||
|
var parent = ThemeComboBox.Parent as Panel;
|
||||||
|
if (parent?.Children.Remove(ThemeComboBox) ?? false)
|
||||||
|
{
|
||||||
|
Configuration.Instance.SetString(ViewModel?.ThemeVariant, nameof(ViewModel.ThemeVariant));
|
||||||
|
parent.Children.Add(ThemeComboBox);
|
||||||
|
}
|
||||||
|
ThemeComboBox.SelectionChanged += ThemeComboBox_SelectionChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenLogFolderButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void OpenLogFolderButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
|||||||
52
Source/LibationAvalonia/Controls/ThemePreviewControl.axaml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<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="600" d:DesignHeight="650"
|
||||||
|
xmlns:views="clr-namespace:LibationAvalonia.Views"
|
||||||
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
|
x:Class="LibationAvalonia.Controls.ThemePreviewControl">
|
||||||
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
|
<controls:GroupBox>
|
||||||
|
<WrapPanel>
|
||||||
|
<RadioButton Margin="5,0" Content="This is an option" IsChecked="True" />
|
||||||
|
<RadioButton Margin="5,0" Content="This is another option" />
|
||||||
|
<CheckBox Margin="5,0" Content="This is a check box" />
|
||||||
|
<controls:WheelComboBox
|
||||||
|
Margin="5,0"
|
||||||
|
ItemsSource="{Binding ComboBoxItems}"
|
||||||
|
SelectedIndex="{Binding ComboBoxSelectedIndex}" />
|
||||||
|
<TextBox Margin="5,0" Text="This is an editable text box" />
|
||||||
|
<TextBox Margin="5,0" Text="This is a read-only text box" IsReadOnly="True" />
|
||||||
|
<NumericUpDown Margin="5,0" Value="100" />
|
||||||
|
<controls:LinkLabel VerticalAlignment="Center" Margin="5,5" Text="This is an unvisited link" />
|
||||||
|
<controls:LinkLabel VerticalAlignment="Center" Margin="5,5" Text="This is a visited link" Foreground="{DynamicResource HyperlinkVisited}" />
|
||||||
|
<StackPanel Margin="5,0" Height="25" Orientation="Horizontal">
|
||||||
|
<StackPanel.Styles>
|
||||||
|
<Style Selector="Path">
|
||||||
|
<Setter Property="Stretch" Value="Uniform" />
|
||||||
|
<Setter Property="Margin" Value="3,5" />
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource IconFill}" />
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Styles>
|
||||||
|
<Path Data="{StaticResource QueuedIcon}" />
|
||||||
|
<Path Data="{StaticResource QueueCompletedIcon}" />
|
||||||
|
<Path Data="{StaticResource QueueErrorIcon}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</WrapPanel>
|
||||||
|
</controls:GroupBox>
|
||||||
|
<WrapPanel Orientation="Horizontal" Grid.Row="1">
|
||||||
|
<views:ProcessBookControl DataContext="{Binding QueuedBook}" ProcessBookStatus="{Binding Status}" />
|
||||||
|
<views:ProcessBookControl DataContext="{Binding WorkingBook}" ProcessBookStatus="{Binding Status}" />
|
||||||
|
<views:ProcessBookControl DataContext="{Binding CompletedBook}" ProcessBookStatus="{Binding Status}" />
|
||||||
|
<views:ProcessBookControl DataContext="{Binding CancelledBook}" ProcessBookStatus="{Binding Status}" />
|
||||||
|
<views:ProcessBookControl DataContext="{Binding FailedBook}" ProcessBookStatus="{Binding Status}" />
|
||||||
|
|
||||||
|
</WrapPanel>
|
||||||
|
|
||||||
|
<views:ProductsDisplay
|
||||||
|
Grid.Row="2"
|
||||||
|
DataContext="{Binding ProductsDisplay}" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using DataLayer;
|
||||||
|
using Dinah.Core.ErrorHandling;
|
||||||
|
using LibationAvalonia.ViewModels;
|
||||||
|
using LibationFileManager;
|
||||||
|
using NPOI.Util.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationAvalonia.Controls;
|
||||||
|
|
||||||
|
public partial class ThemePreviewControl : UserControl
|
||||||
|
{
|
||||||
|
public ProductsDisplayViewModel ProductsDisplay { get; set; }
|
||||||
|
public string[] ComboBoxItems { get; } = Enumerable.Range(1, 9).Select(n => $"Combo box item {n}").ToArray();
|
||||||
|
public int ComboBoxSelectedIndex { get; set; }
|
||||||
|
|
||||||
|
public ProcessBookViewModel QueuedBook { get; }
|
||||||
|
public ProcessBookViewModel WorkingBook { get; }
|
||||||
|
public ProcessBookViewModel CompletedBook { get; }
|
||||||
|
public ProcessBookViewModel CancelledBook { get; }
|
||||||
|
public ProcessBookViewModel FailedBook { get; }
|
||||||
|
public ThemePreviewControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
List<LibraryBook> sampleEntries;
|
||||||
|
sampleEntries = CreateMockBooks().ToList();
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
using var ms1 = new MemoryStream();
|
||||||
|
App.OpenAsset("img-coverart-prod-unavailable_80x80.jpg").CopyTo(ms1);
|
||||||
|
PictureStorage.SetDefaultImage(PictureSize._80x80, ms1.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
QueuedBook = new ProcessBookViewModel(sampleEntries[0], null) { Status = ProcessBookStatus.Queued };
|
||||||
|
WorkingBook = new ProcessBookViewModel(sampleEntries[0], null) { Status = ProcessBookStatus.Working };
|
||||||
|
CompletedBook = new ProcessBookViewModel(sampleEntries[0], null) { Status = ProcessBookStatus.Completed };
|
||||||
|
CancelledBook = new ProcessBookViewModel(sampleEntries[0], null) { Status = ProcessBookStatus.Cancelled };
|
||||||
|
FailedBook = new ProcessBookViewModel(sampleEntries[0], null) { Status = ProcessBookStatus.Failed };
|
||||||
|
|
||||||
|
//Set the current processable so that the empty queue doesn't try to advance.
|
||||||
|
QueuedBook.AddDownloadPdf();
|
||||||
|
WorkingBook.AddDownloadPdf();
|
||||||
|
|
||||||
|
typeof(ProcessBookViewModel).GetProperty(nameof(ProcessBookViewModel.Progress)).SetValue(WorkingBook, 50);
|
||||||
|
|
||||||
|
ProductsDisplay = new ProductsDisplayViewModel();
|
||||||
|
_ = ProductsDisplay.BindToGridAsync(sampleEntries);
|
||||||
|
DataContext = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<LibraryBook> CreateMockBooks()
|
||||||
|
{
|
||||||
|
var author = new Contributor("Some Author", "asin_contributor");
|
||||||
|
var narrator = new Contributor("Some Narrator", "asin_narrator");
|
||||||
|
|
||||||
|
var book1 = new Book(new AudibleProductId("asin_book1"), "Some Book 1", "The Theming", "Demo Book Entry", 525600, ContentType.Product, [author], [narrator], "us");
|
||||||
|
var book2 = new Book(new AudibleProductId("asin_book2"), "Some Book 2", "The Theming", "Demo Book Entry", 525600, ContentType.Product, [author], [narrator], "us");
|
||||||
|
var book3 = new Book(new AudibleProductId("asin_book3"), "Some Book 3", "The Theming", "Demo Book Entry", 525600, ContentType.Product, [author], [narrator], "us");
|
||||||
|
var book4 = new Book(new AudibleProductId("asin_book4"), "Some Book 4", "The Theming", "Demo Book Entry", 525600, ContentType.Product, [author], [narrator], "us");
|
||||||
|
var seriesParent = new Book(new AudibleProductId("asin_series"), "Some Series", "", "Demo Series Entry", 0, ContentType.Parent, [author], [narrator], "us");
|
||||||
|
var episode = new Book(new AudibleProductId("asin_episode"), "Some Episode", "Episode 1", "Demo Episode Entry", 56, ContentType.Episode, [author], [narrator], "us");
|
||||||
|
|
||||||
|
var series = new Series(new AudibleSeriesId(seriesParent.AudibleProductId), seriesParent.Title);
|
||||||
|
|
||||||
|
seriesParent.UpsertSeries(series, "");
|
||||||
|
episode.UpsertSeries(series, "1");
|
||||||
|
|
||||||
|
book1.UserDefinedItem.BookStatus = LiberatedStatus.Liberated;
|
||||||
|
book4.UserDefinedItem.BookStatus = LiberatedStatus.Error;
|
||||||
|
//Set the backing field directly to preserve LiberatedStatus.PartialDownload
|
||||||
|
typeof(UserDefinedItem).GetField("_bookStatus", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(book2.UserDefinedItem, LiberatedStatus.PartialDownload);
|
||||||
|
|
||||||
|
yield return new LibraryBook(book1, System.DateTime.Now.AddDays(4), "someone@email.co");
|
||||||
|
yield return new LibraryBook(book2, System.DateTime.Now.AddDays(3), "someone@email.co");
|
||||||
|
yield return new LibraryBook(book3, System.DateTime.Now.AddDays(2), "someone@email.co") { AbsentFromLastScan = true };
|
||||||
|
yield return new LibraryBook(book4, System.DateTime.Now.AddDays(1), "someone@email.co");
|
||||||
|
yield return new LibraryBook(seriesParent, System.DateTime.Now, "someone@email.co");
|
||||||
|
yield return new LibraryBook(episode, System.DateTime.Now, "someone@email.co");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockProcessable : FileLiberator.Processable
|
||||||
|
{
|
||||||
|
public override string Name => nameof(MockProcessable);
|
||||||
|
public override Task<StatusHandler> ProcessAsync(LibraryBook libraryBook) => Task.FromResult(new StatusHandler());
|
||||||
|
public override bool Validate(LibraryBook libraryBook) => false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,13 +2,12 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
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"
|
||||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="520"
|
mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="520"
|
||||||
MinWidth="400" MinHeight="520"
|
MinWidth="450" MinHeight="520"
|
||||||
Width="400" Height="520"
|
Width="450" Height="520"
|
||||||
x:Class="LibationAvalonia.Dialogs.AboutDialog"
|
x:Class="LibationAvalonia.Dialogs.AboutDialog"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
Title="About Libation"
|
Title="About Libation">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid Margin="10" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,*">
|
<Grid Margin="10" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,*">
|
||||||
|
|
||||||
@ -65,6 +64,15 @@
|
|||||||
<controls:LinkLabel Text="wtanksleyjr" Tapped="Link_GithubUser" />
|
<controls:LinkLabel Text="wtanksleyjr" Tapped="Link_GithubUser" />
|
||||||
<controls:LinkLabel Text="Dr.Blank" Tapped="Link_GithubUser" />
|
<controls:LinkLabel Text="Dr.Blank" Tapped="Link_GithubUser" />
|
||||||
<controls:LinkLabel Text="CharlieRussel" Tapped="Link_GithubUser" />
|
<controls:LinkLabel Text="CharlieRussel" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="cbordeman" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="jwillikers" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="Shuvashish76" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="RokeJulianLockhart" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="maaximal" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="muchtall" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="ScubyG" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="patienttruth" Tapped="Link_GithubUser" />
|
||||||
|
<controls:LinkLabel Text="stickystyle" Tapped="Link_GithubUser" />
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</controls:GroupBox>
|
</controls:GroupBox>
|
||||||
|
|||||||
@ -5,8 +5,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
Width="800" Height="450"
|
Width="800" Height="450"
|
||||||
x:Class="LibationAvalonia.Dialogs.AccountsDialog"
|
x:Class="LibationAvalonia.Dialogs.AccountsDialog"
|
||||||
Title="Audible Accounts"
|
Title="Audible Accounts">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
<Grid RowDefinitions="*,Auto">
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
|
||||||
<Grid.Styles>
|
<Grid.Styles>
|
||||||
|
|||||||
@ -7,8 +7,7 @@
|
|||||||
Width="650" Height="500"
|
Width="650" Height="500"
|
||||||
x:Class="LibationAvalonia.Dialogs.BookDetailsDialog"
|
x:Class="LibationAvalonia.Dialogs.BookDetailsDialog"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
Title="Book Details" Name="BookDetails"
|
Title="Book Details" Name="BookDetails">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="*,Auto,Auto,40">
|
<Grid RowDefinitions="*,Auto,Auto,40">
|
||||||
<Grid.Styles>
|
<Grid.Styles>
|
||||||
|
|||||||
@ -5,8 +5,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="700" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="700" d:DesignHeight="450"
|
||||||
Width="700" Height="450"
|
Width="700" Height="450"
|
||||||
x:Class="LibationAvalonia.Dialogs.BookRecordsDialog"
|
x:Class="LibationAvalonia.Dialogs.BookRecordsDialog"
|
||||||
Title="BookRecordsDialog"
|
Title="BookRecordsDialog">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="*,Auto">
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
|||||||
@ -10,103 +10,43 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
public abstract class DialogWindow : Window
|
public abstract class DialogWindow : Window
|
||||||
{
|
{
|
||||||
public bool SaveAndRestorePosition { get; set; } = true;
|
protected bool CancelOnEscape { get; set; } = true;
|
||||||
|
protected bool SaveOnEnter { get; set; } = true;
|
||||||
|
public bool SaveAndRestorePosition { get; set; }
|
||||||
public Control ControlToFocusOnShow { get; set; }
|
public Control ControlToFocusOnShow { get; set; }
|
||||||
protected override Type StyleKeyOverride => typeof(DialogWindow);
|
protected override Type StyleKeyOverride => typeof(DialogWindow);
|
||||||
|
|
||||||
public static readonly StyledProperty<bool> UseCustomTitleBarProperty =
|
public DialogWindow(bool saveAndRestorePosition = true)
|
||||||
AvaloniaProperty.Register<DialogWindow, bool>(nameof(UseCustomTitleBar));
|
|
||||||
|
|
||||||
public bool UseCustomTitleBar
|
|
||||||
{
|
|
||||||
get { return GetValue(UseCustomTitleBarProperty); }
|
|
||||||
set { SetValue(UseCustomTitleBarProperty, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public DialogWindow()
|
|
||||||
{
|
{
|
||||||
|
SaveAndRestorePosition = saveAndRestorePosition;
|
||||||
KeyDown += DialogWindow_KeyDown;
|
KeyDown += DialogWindow_KeyDown;
|
||||||
Initialized += DialogWindow_Initialized;
|
Initialized += DialogWindow_Initialized;
|
||||||
Opened += DialogWindow_Opened;
|
Opened += DialogWindow_Opened;
|
||||||
|
Loaded += DialogWindow_Loaded;
|
||||||
Closing += DialogWindow_Closing;
|
Closing += DialogWindow_Closing;
|
||||||
|
|
||||||
UseCustomTitleBar = Configuration.IsWindows;
|
|
||||||
|
|
||||||
if (Design.IsDesignMode)
|
if (Design.IsDesignMode)
|
||||||
RequestedThemeVariant = ThemeVariant.Dark;
|
RequestedThemeVariant = ThemeVariant.Dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool fixedMinHeight = false;
|
private void DialogWindow_Loaded(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
private bool fixedMaxHeight = false;
|
|
||||||
private bool fixedHeight = false;
|
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
|
||||||
{
|
{
|
||||||
const int customTitleBarHeight = 30;
|
if (!CanResize)
|
||||||
if (UseCustomTitleBar)
|
this.HideMinMaxBtns();
|
||||||
{
|
|
||||||
if (change.Property == MinHeightProperty && !fixedMinHeight)
|
|
||||||
{
|
|
||||||
fixedMinHeight = true;
|
|
||||||
MinHeight += customTitleBarHeight;
|
|
||||||
fixedMinHeight = false;
|
|
||||||
}
|
|
||||||
if (change.Property == MaxHeightProperty && !fixedMaxHeight)
|
|
||||||
{
|
|
||||||
fixedMaxHeight = true;
|
|
||||||
MaxHeight += customTitleBarHeight;
|
|
||||||
fixedMaxHeight = false;
|
|
||||||
}
|
|
||||||
if (change.Property == HeightProperty && !fixedHeight)
|
|
||||||
{
|
|
||||||
fixedHeight = true;
|
|
||||||
Height += customTitleBarHeight;
|
|
||||||
fixedHeight = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
base.OnPropertyChanged(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DialogWindow(bool saveAndRestorePosition) : this()
|
|
||||||
{
|
|
||||||
SaveAndRestorePosition = saveAndRestorePosition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
|
|
||||||
if (!UseCustomTitleBar)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var closeButton = e.NameScope.Find<Button>("DialogCloseButton");
|
|
||||||
var border = e.NameScope.Get<Border>("DialogWindowTitleBorder");
|
|
||||||
var titleBlock = e.NameScope.Get<TextBlock>("DialogWindowTitleTextBlock");
|
|
||||||
var icon = e.NameScope.Get<Avalonia.Controls.Shapes.Path>("DialogWindowTitleIcon");
|
|
||||||
|
|
||||||
closeButton.Click += CloseButton_Click;
|
|
||||||
border.PointerPressed += Border_PointerPressed;
|
|
||||||
icon.IsVisible = Icon != null;
|
|
||||||
|
|
||||||
if (MinHeight == MaxHeight && MinWidth == MaxWidth)
|
if (MinHeight == MaxHeight && MinWidth == MaxWidth)
|
||||||
{
|
{
|
||||||
CanResize = false;
|
CanResize = false;
|
||||||
border.Margin = new Thickness(0);
|
Height = MinHeight;
|
||||||
icon.Margin = new Thickness(8, 5, 0, 5);
|
Width = MinWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Border_PointerPressed(object sender, Avalonia.Input.PointerPressedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.GetCurrentPoint(null).Properties.IsLeftButtonPressed)
|
|
||||||
BeginMoveDrag(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CloseButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
CancelAndClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DialogWindow_Initialized(object sender, EventArgs e)
|
private void DialogWindow_Initialized(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
this.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
this.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
@ -132,9 +72,9 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
private async void DialogWindow_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
private async void DialogWindow_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == Avalonia.Input.Key.Escape)
|
if (CancelOnEscape && e.Key == Avalonia.Input.Key.Escape)
|
||||||
await CancelAndCloseAsync();
|
await CancelAndCloseAsync();
|
||||||
else if (e.Key == Avalonia.Input.Key.Return)
|
else if (SaveOnEnter && e.Key == Avalonia.Input.Key.Return)
|
||||||
await SaveAndCloseAsync();
|
await SaveAndCloseAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
Width="800" Height="450"
|
Width="800" Height="450"
|
||||||
x:Class="LibationAvalonia.Dialogs.EditQuickFilters"
|
x:Class="LibationAvalonia.Dialogs.EditQuickFilters"
|
||||||
Title="Audible Accounts"
|
Title="Audible Accounts"
|
||||||
Icon="/Assets/libation.ico"
|
|
||||||
x:DataType="dialogs:EditQuickFilters">
|
x:DataType="dialogs:EditQuickFilters">
|
||||||
<Grid RowDefinitions="*,Auto">
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,7 @@
|
|||||||
MinWidth="500" MinHeight="450"
|
MinWidth="500" MinHeight="450"
|
||||||
Width="500" Height="450"
|
Width="500" Height="450"
|
||||||
x:Class="LibationAvalonia.Dialogs.EditReplacementChars"
|
x:Class="LibationAvalonia.Dialogs.EditReplacementChars"
|
||||||
Title="Illegal Character Replacement"
|
Title="Illegal Character Replacement">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
RowDefinitions="*,Auto"
|
RowDefinitions="*,Auto"
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
Width="800" Height="450"
|
Width="800" Height="450"
|
||||||
x:Class="LibationAvalonia.Dialogs.EditTemplateDialog"
|
x:Class="LibationAvalonia.Dialogs.EditTemplateDialog"
|
||||||
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
|
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
|
||||||
Icon="/Assets/libation.ico"
|
|
||||||
Title="EditTemplateDialog">
|
Title="EditTemplateDialog">
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*,Auto">
|
<Grid RowDefinitions="Auto,*,Auto">
|
||||||
|
|||||||
@ -7,8 +7,7 @@
|
|||||||
MinWidth="500" MinHeight="500"
|
MinWidth="500" MinHeight="500"
|
||||||
Width="500" Height="520"
|
Width="500" Height="520"
|
||||||
Title="Cover"
|
Title="Cover"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Image Stretch="Uniform" Source="{Binding CoverImage}">
|
<Image Stretch="Uniform" Source="{Binding CoverImage}">
|
||||||
<Image.ContextMenu>
|
<Image.ContextMenu>
|
||||||
|
|||||||
@ -9,8 +9,7 @@
|
|||||||
x:Class="LibationAvalonia.Dialogs.LibationFilesDialog"
|
x:Class="LibationAvalonia.Dialogs.LibationFilesDialog"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
Title="Book Details"
|
Title="Book Details">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
RowDefinitions="Auto,Auto">
|
RowDefinitions="Auto,Auto">
|
||||||
|
|||||||
@ -9,8 +9,7 @@
|
|||||||
MinHeight="135" MaxHeight="135"
|
MinHeight="135" MaxHeight="135"
|
||||||
MinWidth="550" MaxWidth="550"
|
MinWidth="550" MaxWidth="550"
|
||||||
Width="550" Height="135"
|
Width="550" Height="135"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid Margin="10" RowDefinitions="Auto,Auto">
|
<Grid Margin="10" RowDefinitions="Auto,Auto">
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,7 @@
|
|||||||
MinWidth="400" MinHeight="100"
|
MinWidth="400" MinHeight="100"
|
||||||
MaxWidth="400" MaxHeight="100"
|
MaxWidth="400" MaxHeight="100"
|
||||||
Width="400" Height="100"
|
Width="400" Height="100"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,Auto">
|
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,Auto">
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,7 @@
|
|||||||
Width="600" Height="450"
|
Width="600" Height="450"
|
||||||
x:Class="LibationAvalonia.Dialogs.LocateAudiobooksDialog"
|
x:Class="LibationAvalonia.Dialogs.LocateAudiobooksDialog"
|
||||||
Title="Locate Audiobooks"
|
Title="Locate Audiobooks"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid Margin="5" ColumnDefinitions="*,Auto" RowDefinitions="Auto,*">
|
<Grid Margin="5" ColumnDefinitions="*,Auto" RowDefinitions="Auto,*">
|
||||||
<TextBlock Grid.Column="0" Text="Found Audiobooks" />
|
<TextBlock Grid.Column="0" Text="Found Audiobooks" />
|
||||||
|
|||||||
@ -8,8 +8,7 @@
|
|||||||
Width="240" Height="140"
|
Width="240" Height="140"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.ApprovalNeededDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.ApprovalNeededDialog"
|
||||||
Title="Approval Alert Detected"
|
Title="Approval Alert Detected">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto,*">
|
<Grid RowDefinitions="Auto,Auto,*">
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
using AudibleApi;
|
using AudibleApi;
|
||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Avalonia.Threading;
|
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
namespace LibationAvalonia.Dialogs.Login
|
namespace LibationAvalonia.Dialogs.Login
|
||||||
{
|
{
|
||||||
public class AvaloniaLoginChoiceEager : ILoginChoiceEager
|
public class AvaloniaLoginChoiceEager : ILoginChoiceEager
|
||||||
@ -23,14 +23,14 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
LoginCallback = new AvaloniaLoginCallback(_account);
|
LoginCallback = new AvaloniaLoginCallback(_account);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ChoiceOut> StartAsync(ChoiceIn choiceIn)
|
public async Task<ChoiceOut?> StartAsync(ChoiceIn choiceIn)
|
||||||
{
|
{
|
||||||
if (Configuration.IsWindows && Environment.OSVersion.Version.Major >= 10)
|
if (Configuration.IsWindows && Environment.OSVersion.Version.Major >= 10)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var weblogin = new WebLoginDialog(_account.AccountId, choiceIn.LoginUrl);
|
var weblogin = new WebLoginDialog(_account.AccountId, choiceIn.LoginUrl);
|
||||||
if (await weblogin.ShowDialog<DialogResult>(App.MainWindow) is DialogResult.OK)
|
if (await weblogin.ShowDialogAsync(App.MainWindow) is DialogResult.OK)
|
||||||
return ChoiceOut.External(weblogin.ResponseUrl);
|
return ChoiceOut.External(weblogin.ResponseUrl);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@ -8,8 +8,7 @@
|
|||||||
Width="220" Height="250"
|
Width="220" Height="250"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.CaptchaDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.CaptchaDialog"
|
||||||
Title="CAPTCHA"
|
Title="CAPTCHA">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,*"
|
RowDefinitions="Auto,Auto,Auto,Auto,*"
|
||||||
|
|||||||
@ -7,8 +7,7 @@
|
|||||||
Width="300" Height="120"
|
Width="300" Height="120"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.LoginCallbackDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.LoginCallbackDialog"
|
||||||
Title="Audible Login"
|
Title="Audible Login">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto,Auto,*" ColumnDefinitions="*" Margin="5">
|
<Grid RowDefinitions="Auto,Auto,Auto,*" ColumnDefinitions="*" Margin="5">
|
||||||
|
|||||||
@ -8,8 +8,7 @@
|
|||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.LoginChoiceEagerDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.LoginChoiceEagerDialog"
|
||||||
Title="Audible Login"
|
Title="Audible Login">
|
||||||
Icon="/Assets/libation.ico" >
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto,Auto,*" ColumnDefinitions="*" Margin="5">
|
<Grid RowDefinitions="Auto,Auto,Auto,*" ColumnDefinitions="*" Margin="5">
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,7 @@
|
|||||||
Width="650" Height="500"
|
Width="650" Height="500"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.LoginExternalDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.LoginExternalDialog"
|
||||||
Title="Audible Login External"
|
Title="Audible Login External">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto,*,Auto,*" ColumnDefinitions="*" Margin="5">
|
<Grid RowDefinitions="Auto,Auto,*,Auto,*" ColumnDefinitions="*" Margin="5">
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,7 @@
|
|||||||
Width="400" Height="200"
|
Width="400" Height="200"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login.MfaDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.MfaDialog"
|
||||||
Title="Two-Step Verification"
|
Title="Two-Step Verification">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="*,Auto">
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
x:Class="LibationAvalonia.Dialogs.Login.WebLoginDialog"
|
x:Class="LibationAvalonia.Dialogs.Login.WebLoginDialog"
|
||||||
Width="500" Height="800"
|
Width="500" Height="800"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Icon="/Assets/libation.ico"
|
|
||||||
Title="Audible Login">
|
Title="Audible Login">
|
||||||
<controls:NativeWebView Name="webView" />
|
<controls:NativeWebView Name="webView" />
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@ -8,8 +8,7 @@
|
|||||||
Width="200" Height="200"
|
Width="200" Height="200"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
x:Class="LibationAvalonia.Dialogs.Login._2faCodeDialog"
|
x:Class="LibationAvalonia.Dialogs.Login._2faCodeDialog"
|
||||||
Title="2FA Code"
|
Title="2FA Code">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
|
|||||||
@ -9,8 +9,7 @@
|
|||||||
x:Class="LibationAvalonia.Dialogs.MessageBoxAlertAdminDialog"
|
x:Class="LibationAvalonia.Dialogs.MessageBoxAlertAdminDialog"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
Title="MessageBoxAlertAdminDialog"
|
Title="MessageBoxAlertAdminDialog"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*,Auto,Auto">
|
<Grid RowDefinitions="Auto,*,Auto,Auto">
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="265" d:DesignHeight="110"
|
mc:Ignorable="d" d:DesignWidth="265" d:DesignHeight="110"
|
||||||
MinWidth="265" MinHeight="110"
|
MinWidth="265" MinHeight="110"
|
||||||
x:Class="LibationAvalonia.Dialogs.MessageBoxWindow"
|
x:Class="LibationAvalonia.Dialogs.MessageBoxWindow"
|
||||||
Title="{Binding Caption}" ShowInTaskbar="True"
|
Title="{Binding Caption}" ShowInTaskbar="True">
|
||||||
Icon="/Assets/1x1.png">
|
|
||||||
<Grid ColumnDefinitions="*" RowDefinitions="*,Auto">
|
<Grid ColumnDefinitions="*" RowDefinitions="*,Auto">
|
||||||
|
|
||||||
<DockPanel Margin="5,10,10,20" Grid.Row="0">
|
<DockPanel Margin="5,10,10,20" Grid.Row="0">
|
||||||
|
|||||||
@ -7,8 +7,7 @@
|
|||||||
MinWidth="500" MinHeight="160"
|
MinWidth="500" MinHeight="160"
|
||||||
Width="500" Height="200"
|
Width="500" Height="200"
|
||||||
Title="Which Accounts?"
|
Title="Which Accounts?"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*,Auto">
|
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*,Auto">
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,7 @@
|
|||||||
Width="800" Height="650"
|
Width="800" Height="650"
|
||||||
x:Class="LibationAvalonia.Dialogs.SearchSyntaxDialog"
|
x:Class="LibationAvalonia.Dialogs.SearchSyntaxDialog"
|
||||||
Title="Filter Options"
|
Title="Filter Options"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
Margin="10,0,10,10"
|
Margin="10,0,10,10"
|
||||||
|
|||||||
@ -10,8 +10,7 @@
|
|||||||
xmlns:settings="clr-namespace:LibationAvalonia.Controls.Settings"
|
xmlns:settings="clr-namespace:LibationAvalonia.Controls.Settings"
|
||||||
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels.Settings"
|
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels.Settings"
|
||||||
x:DataType="vm:SettingsVM"
|
x:DataType="vm:SettingsVM"
|
||||||
Title="Edit Settings"
|
Title="Edit Settings">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="*,Auto">
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,7 @@
|
|||||||
MaxWidth="630" MaxHeight="90"
|
MaxWidth="630" MaxHeight="90"
|
||||||
Width="630" Height="110"
|
Width="630" Height="110"
|
||||||
Title="Replace Tags"
|
Title="Replace Tags"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,Auto" Margin="10">
|
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,Auto" Margin="10">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
|||||||
78
Source/LibationAvalonia/Dialogs/ThemePickerDialog.axaml
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<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"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="965" d:DesignHeight="850"
|
||||||
|
Width="965" Height="850"
|
||||||
|
x:Class="LibationAvalonia.Dialogs.ThemePickerDialog"
|
||||||
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
|
Title="Theme Editor">
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<controls:ThemePreviewControl Name="ThemePickerPreviewControl" Margin="5" Grid.Column="1" />
|
||||||
|
<Grid
|
||||||
|
RowDefinitions="*,Auto,Auto">
|
||||||
|
<Grid.Styles>
|
||||||
|
<Style Selector="Button">
|
||||||
|
<Setter Property="Height" Value="30" />
|
||||||
|
<Setter Property="Padding" Value="20,0" />
|
||||||
|
<Setter Property="Margin" Value="5" />
|
||||||
|
</Style>
|
||||||
|
</Grid.Styles>
|
||||||
|
<DataGrid
|
||||||
|
Grid.Row="0"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
Margin="5"
|
||||||
|
IsReadOnly="False"
|
||||||
|
ItemsSource="{Binding ThemeColors}">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTemplateColumn Width="Auto" Header="Color">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ColorPicker
|
||||||
|
IsHexInputVisible="True"
|
||||||
|
Color="{Binding ThemeColor, Mode=TwoWay}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="Auto"
|
||||||
|
Binding="{Binding ThemeItemName, Mode=TwoWay}"
|
||||||
|
Header="Theme Item"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
<Grid
|
||||||
|
Grid.Row="1"
|
||||||
|
ColumnDefinitions="Auto,*,Auto">
|
||||||
|
<Button
|
||||||
|
Grid.Column="0"
|
||||||
|
Content="Import Theme"
|
||||||
|
Command="{Binding ImportTheme}" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
Content="Export Theme"
|
||||||
|
Command="{Binding ExportTheme}" />
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
Grid.Row="2"
|
||||||
|
ColumnDefinitions="Auto,Auto,Auto,*,Auto">
|
||||||
|
<Button
|
||||||
|
Grid.Column="0"
|
||||||
|
Content="Cancel"
|
||||||
|
Command="{Binding CancelAndClose}" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="1"
|
||||||
|
Content="Reset"
|
||||||
|
Command="{Binding ResetColors}" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
Content="Defaults"
|
||||||
|
Command="{Binding LoadDefaultColors}" />
|
||||||
|
<Button
|
||||||
|
Grid.Column="4"
|
||||||
|
Content="Save"
|
||||||
|
Command="{Binding SaveAndCloseAsync}" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
195
Source/LibationAvalonia/Dialogs/ThemePickerDialog.axaml.cs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
using Avalonia.Collections;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using ReactiveUI;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using System;
|
||||||
|
using LibationAvalonia.Themes;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
namespace LibationAvalonia.Dialogs;
|
||||||
|
|
||||||
|
public partial class ThemePickerDialog : DialogWindow
|
||||||
|
{
|
||||||
|
protected DataGridCollectionView ThemeColors { get; }
|
||||||
|
private ChardonnayTheme ExistingTheme { get; } = ChardonnayTheme.GetLiveTheme();
|
||||||
|
private ChardonnayTheme WorkingTheme { get; set; }
|
||||||
|
|
||||||
|
public ThemePickerDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
CancelOnEscape = false;
|
||||||
|
WorkingTheme = (ChardonnayTheme)ExistingTheme.Clone();
|
||||||
|
ThemeColors = new(EnumerateThemeItemColors());
|
||||||
|
|
||||||
|
DataContext = this;
|
||||||
|
Closing += ThemePickerDialog_Closing;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThemePickerDialog_Closing(object? sender, Avalonia.Controls.WindowClosingEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.IsProgrammatic)
|
||||||
|
{
|
||||||
|
CancelAndClose();
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task ImportTheme()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var openFileDialogOptions = new FilePickerOpenOptions
|
||||||
|
{
|
||||||
|
Title = $"Select the ChardonnayTheme.json file",
|
||||||
|
AllowMultiple = false,
|
||||||
|
FileTypeFilter =
|
||||||
|
[
|
||||||
|
new("JSON files (*.json)")
|
||||||
|
{
|
||||||
|
Patterns = ["*.json"],
|
||||||
|
AppleUniformTypeIdentifiers = ["public.json"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectedFiles = await StorageProvider.OpenFilePickerAsync(openFileDialogOptions);
|
||||||
|
var selectedFile = selectedFiles.SingleOrDefault()?.TryGetLocalPath();
|
||||||
|
|
||||||
|
if (selectedFile is null) return;
|
||||||
|
|
||||||
|
using (var theme = new ChardonnayThemePersister(selectedFile))
|
||||||
|
{
|
||||||
|
ResetTheme(theme.Target);
|
||||||
|
}
|
||||||
|
|
||||||
|
await MessageBox.Show(this, "Theme imported and applied", "Theme Imported");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await MessageBox.ShowAdminAlert(this, "Error attempting to import your chardonnay theme.", "Error Importing", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task ExportTheme()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new FilePickerSaveOptions
|
||||||
|
{
|
||||||
|
Title = "Where to export Library",
|
||||||
|
SuggestedFileName = $"ChardonnayTheme",
|
||||||
|
DefaultExtension = "json",
|
||||||
|
ShowOverwritePrompt = true,
|
||||||
|
FileTypeChoices =
|
||||||
|
[
|
||||||
|
new("JSON files (*.json)")
|
||||||
|
{
|
||||||
|
Patterns = ["*.json"],
|
||||||
|
AppleUniformTypeIdentifiers = ["public.json"]
|
||||||
|
},
|
||||||
|
new("All files (*.*)") { Patterns = ["*"] }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectedFile = (await StorageProvider.SaveFilePickerAsync(options))?.TryGetLocalPath();
|
||||||
|
|
||||||
|
if (selectedFile is null) return;
|
||||||
|
|
||||||
|
using (var theme = new ChardonnayThemePersister(WorkingTheme, selectedFile))
|
||||||
|
theme.Target.Save();
|
||||||
|
|
||||||
|
await MessageBox.Show(this, "Theme exported to:\r\n" + selectedFile, "Theme Exported");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await MessageBox.ShowAdminAlert(this, "Error attempting to export your chardonnay theme.", "Error Exporting", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CancelAndClose()
|
||||||
|
{
|
||||||
|
ExistingTheme.ApplyTheme(ActualThemeVariant);
|
||||||
|
base.CancelAndClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ResetColors()
|
||||||
|
=> ResetTheme(ExistingTheme);
|
||||||
|
|
||||||
|
protected void LoadDefaultColors()
|
||||||
|
{
|
||||||
|
if (App.DefaultThemeColors is ChardonnayTheme defaults)
|
||||||
|
ResetTheme(defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
using (var themePersister = ChardonnayThemePersister.Create())
|
||||||
|
{
|
||||||
|
if (themePersister is null)
|
||||||
|
{
|
||||||
|
await MessageBox.Show(this, "Failed to save the theme.", "Error saving theme", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var i in ThemeColors.OfType<ThemeItemColor>())
|
||||||
|
{
|
||||||
|
themePersister.Target.SetColor(ActualThemeVariant, i.ThemeItemName, i.ThemeColor);
|
||||||
|
}
|
||||||
|
themePersister.Target.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetTheme(ChardonnayTheme theme)
|
||||||
|
{
|
||||||
|
WorkingTheme = (ChardonnayTheme)theme.Clone();
|
||||||
|
WorkingTheme.ApplyTheme(ActualThemeVariant);
|
||||||
|
|
||||||
|
foreach (var i in ThemeColors.OfType<ThemeItemColor>())
|
||||||
|
{
|
||||||
|
i.ColorSetter = null;
|
||||||
|
i.ThemeColor = WorkingTheme.GetColor(ActualThemeVariant, i.ThemeItemName);
|
||||||
|
i.ColorSetter = ColorSetter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<ThemeItemColor> EnumerateThemeItemColors()
|
||||||
|
=> WorkingTheme
|
||||||
|
.GetThemeColors(ActualThemeVariant)
|
||||||
|
.Select(kvp => new ThemeItemColor
|
||||||
|
{
|
||||||
|
ThemeItemName = kvp.Key,
|
||||||
|
ThemeColor = kvp.Value,
|
||||||
|
ColorSetter = ColorSetter
|
||||||
|
});
|
||||||
|
|
||||||
|
private void ColorSetter(Color color, string colorName)
|
||||||
|
{
|
||||||
|
WorkingTheme.SetColor(ActualThemeVariant, colorName, color);
|
||||||
|
WorkingTheme.ApplyTheme(ActualThemeVariant);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ThemeItemColor : ViewModels.ViewModelBase
|
||||||
|
{
|
||||||
|
public required string ThemeItemName { get; init; }
|
||||||
|
public required Action<Color, string>? ColorSetter { get; set; }
|
||||||
|
|
||||||
|
private Color _themeColor;
|
||||||
|
public Color ThemeColor
|
||||||
|
{
|
||||||
|
get => _themeColor;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var setColors = !_themeColor.Equals(value);
|
||||||
|
this.RaiseAndSetIfChanged(ref _themeColor, value);
|
||||||
|
if (setColors)
|
||||||
|
ColorSetter?.Invoke(_themeColor, ThemeItemName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,10 +7,8 @@
|
|||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
MinWidth="500" MinHeight="400"
|
MinWidth="500" MinHeight="400"
|
||||||
Height="450" Width="550"
|
Height="450" Width="550"
|
||||||
|
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Title="Upgrade Available"
|
Title="Upgrade Available">
|
||||||
Icon="/Assets/libation.ico">
|
|
||||||
|
|
||||||
<Grid Margin="6" RowDefinitions="Auto,*,Auto">
|
<Grid Margin="6" RowDefinitions="Auto,*,Auto">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using Dinah.Core;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -111,5 +112,63 @@ namespace LibationAvalonia
|
|||||||
public int Width;
|
public int Width;
|
||||||
public bool IsMaximized;
|
public bool IsMaximized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void HideMinMaxBtns(this Window form)
|
||||||
|
{
|
||||||
|
if (Design.IsDesignMode || !Configuration.IsWindows || form.TryGetPlatformHandle() is not IPlatformHandle handle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var windowStyle
|
||||||
|
= GetWindowStyle(handle.Handle)
|
||||||
|
.Remove(WINDOW_STYLE.WS_MINIMIZEBOX)
|
||||||
|
.Remove(WINDOW_STYLE.WS_MAXIMIZEBOX);
|
||||||
|
|
||||||
|
SetWindowStyle(handle.Handle, windowStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int GWL_STYLE = -16;
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "GetWindowLong")]
|
||||||
|
static extern long GetWindowLong(IntPtr hWnd, int nIndex);
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowLong")]
|
||||||
|
static extern int SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong);
|
||||||
|
|
||||||
|
static WINDOW_STYLE GetWindowStyle(IntPtr hWnd) => (WINDOW_STYLE)GetWindowLong(hWnd, GWL_STYLE);
|
||||||
|
static void SetWindowStyle(IntPtr hWnd, WINDOW_STYLE style) => SetWindowLong(hWnd, GWL_STYLE, (long)style);
|
||||||
|
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
enum WINDOW_STYLE : long
|
||||||
|
{
|
||||||
|
WS_OVERLAPPED = 0x0,
|
||||||
|
WS_TILED = 0x0,
|
||||||
|
WS_ACTIVECAPTION = 0x1,
|
||||||
|
WS_MAXIMIZEBOX = 0x10000,
|
||||||
|
WS_TABSTOP = 0x10000,
|
||||||
|
WS_MINIMIZEBOX = 0x20000,
|
||||||
|
WS_GROUP = 0x20000,
|
||||||
|
WS_THICKFRAME = 0x40000,
|
||||||
|
WS_SIZEBOX = 0x40000,
|
||||||
|
WS_SYSMENU = 0x80000,
|
||||||
|
WS_HSCROLL = 0x100000,
|
||||||
|
WS_VSCROLL = 0x200000,
|
||||||
|
WS_DLGFRAME = 0x400000,
|
||||||
|
WS_BORDER = 0x800000,
|
||||||
|
WS_CAPTION = 0xc00000,
|
||||||
|
WS_OVERLAPPEDWINDOW = 0xcf0000,
|
||||||
|
WS_TILEDWINDOW = 0xcf0000,
|
||||||
|
WS_MAXIMIZE = 0x1000000,
|
||||||
|
WS_CLIPCHILDREN = 0x2000000,
|
||||||
|
WS_CLIPSIBLINGS = 0x4000000,
|
||||||
|
WS_DISABLED = 0x8000000,
|
||||||
|
WS_VISIBLE = 0x10000000,
|
||||||
|
WS_ICONIC = 0x20000000,
|
||||||
|
WS_MINIMIZE = 0x20000000,
|
||||||
|
WS_CHILD = 0x40000000,
|
||||||
|
WS_CHILDWINDOW = 0x40000000,
|
||||||
|
WS_POPUP = 0x80000000,
|
||||||
|
WS_POPUPWINDOW = 0x80880000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**" />
|
<AvaloniaResource Include="Assets\**" />
|
||||||
<None Remove=".gitignore" />
|
<None Remove=".gitignore" />
|
||||||
<None Remove="Assets\DataGridColumnHeader.xaml" />
|
|
||||||
<None Remove="Assets\img-coverart-prod-unavailable_300x300.jpg" />
|
<None Remove="Assets\img-coverart-prod-unavailable_300x300.jpg" />
|
||||||
<None Remove="Assets\img-coverart-prod-unavailable_500x500.jpg" />
|
<None Remove="Assets\img-coverart-prod-unavailable_500x500.jpg" />
|
||||||
<None Remove="Assets\img-coverart-prod-unavailable_80x80.jpg" />
|
<None Remove="Assets\img-coverart-prod-unavailable_80x80.jpg" />
|
||||||
@ -74,6 +73,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Avalonia.Controls.ColorPicker" Version="11.2.5" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.5" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" />
|
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.5" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" />
|
||||||
<PackageReference Include="Avalonia" Version="11.2.5" />
|
<PackageReference Include="Avalonia" Version="11.2.5" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.5" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.5" />
|
||||||
|
|||||||
206
Source/LibationAvalonia/Themes/ChardonnayTheme.cs
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
using Avalonia.Media;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using System;
|
||||||
|
using Dinah.Core;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Themes.Fluent;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections.Frozen;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
namespace LibationAvalonia;
|
||||||
|
|
||||||
|
public class ChardonnayTheme : IUpdatable, ICloneable
|
||||||
|
{
|
||||||
|
public event EventHandler? Updated;
|
||||||
|
|
||||||
|
/// <summary>Theme color overrides</summary>
|
||||||
|
[JsonProperty]
|
||||||
|
private readonly Dictionary<ThemeVariant, Dictionary<string, Color>> ThemeColors;
|
||||||
|
|
||||||
|
/// <summary>The two theme variants supported by Fluent themes</summary>
|
||||||
|
private static readonly FrozenSet<ThemeVariant> FluentVariants = [ThemeVariant.Light, ThemeVariant.Dark];
|
||||||
|
|
||||||
|
/// <summary>Reusable color pallets for the two theme variants</summary>
|
||||||
|
private static readonly FrozenDictionary<ThemeVariant, ColorPaletteResources> ColorPalettes
|
||||||
|
= FluentVariants.ToFrozenDictionary(t => t, _ => new ColorPaletteResources());
|
||||||
|
|
||||||
|
private ChardonnayTheme()
|
||||||
|
{
|
||||||
|
ThemeColors = FluentVariants.ToDictionary(t => t, _ => new Dictionary<string, Color>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Invoke <see cref="IUpdatable.Updated"/> </summary>
|
||||||
|
public void Save() => Updated?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
|
public Color GetColor(string? themeVariant, string itemName)
|
||||||
|
=> GetColor(FromVariantName(themeVariant), itemName);
|
||||||
|
|
||||||
|
public Color GetColor(ThemeVariant themeVariant, string itemName)
|
||||||
|
{
|
||||||
|
ValidateThemeVariant(themeVariant);
|
||||||
|
return ThemeColors[themeVariant].TryGetValue(itemName, out var color) ? color : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChardonnayTheme SetColor(string? themeVariant, Expression<Func<ColorPaletteResources, Color>> colorSelector, Color color)
|
||||||
|
=> SetColor(FromVariantName(themeVariant), colorSelector, color);
|
||||||
|
|
||||||
|
public ChardonnayTheme SetColor(ThemeVariant themeVariant, Expression<Func<ColorPaletteResources, Color>> colorSelector, Color color)
|
||||||
|
{
|
||||||
|
if (colorSelector.Body.NodeType is ExpressionType.MemberAccess &&
|
||||||
|
colorSelector.Body is MemberExpression memberExpression &&
|
||||||
|
memberExpression.Member is PropertyInfo colorProperty &&
|
||||||
|
colorProperty.DeclaringType == typeof(ColorPaletteResources))
|
||||||
|
return SetColor(themeVariant, colorProperty.Name, color);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChardonnayTheme SetColor(string? themeVariant, string itemName, Color itemColor)
|
||||||
|
=> SetColor(FromVariantName(themeVariant), itemName, itemColor);
|
||||||
|
|
||||||
|
public ChardonnayTheme SetColor(ThemeVariant themeVariant, string itemName, Color itemColor)
|
||||||
|
{
|
||||||
|
ValidateThemeVariant(themeVariant);
|
||||||
|
ThemeColors[themeVariant][itemName] = itemColor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrozenDictionary<string, Color> GetThemeColors(string? themeVariant)
|
||||||
|
=> GetThemeColors(FromVariantName(themeVariant));
|
||||||
|
|
||||||
|
public FrozenDictionary<string, Color> GetThemeColors(ThemeVariant themeVariant)
|
||||||
|
{
|
||||||
|
ValidateThemeVariant(themeVariant);
|
||||||
|
return ThemeColors[themeVariant].ToFrozenDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyTheme(string? themeVariant)
|
||||||
|
=> ApplyTheme(FromVariantName(themeVariant));
|
||||||
|
|
||||||
|
public void ApplyTheme(ThemeVariant themeVariant)
|
||||||
|
{
|
||||||
|
App.Current.RequestedThemeVariant = themeVariant;
|
||||||
|
themeVariant = App.Current.ActualThemeVariant;
|
||||||
|
ValidateThemeVariant(themeVariant);
|
||||||
|
|
||||||
|
bool fluentColorChanged = false;
|
||||||
|
|
||||||
|
//Set the Libation-specific brushes
|
||||||
|
var themeBrushes = (ResourceDictionary)App.Current.Resources.ThemeDictionaries[themeVariant];
|
||||||
|
foreach (var colorName in themeBrushes.Keys.OfType<string>())
|
||||||
|
{
|
||||||
|
if (ThemeColors[themeVariant].TryGetValue(colorName, out var color) && color != default)
|
||||||
|
{
|
||||||
|
if (themeBrushes[colorName] is ISolidColorBrush brush && brush.Color != color)
|
||||||
|
themeBrushes[colorName] = new SolidColorBrush(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set the fluent theme colors
|
||||||
|
foreach (var p in GetColorResourceProperties())
|
||||||
|
{
|
||||||
|
if (ThemeColors[themeVariant].TryGetValue(p.Name, out var color) && color != default)
|
||||||
|
{
|
||||||
|
if (p.GetValue(ColorPalettes[themeVariant]) is not Color c || c != color)
|
||||||
|
{
|
||||||
|
p.SetValue(ColorPalettes[themeVariant], color);
|
||||||
|
fluentColorChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fluentColorChanged)
|
||||||
|
{
|
||||||
|
var oldFluent = App.Current.Styles.OfType<FluentTheme>().Single();
|
||||||
|
App.Current.Styles.Remove(oldFluent);
|
||||||
|
|
||||||
|
//We must make a new fluent theme and add it to the app for
|
||||||
|
//the changes to the ColorPaletteResources to take effect.
|
||||||
|
//Changes to the Libation-specific resources are instant.
|
||||||
|
var newFluent = new FluentTheme();
|
||||||
|
|
||||||
|
foreach (var kvp in ColorPalettes)
|
||||||
|
newFluent.Palettes[kvp.Key] = kvp.Value;
|
||||||
|
|
||||||
|
App.Current.Styles.Add(newFluent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Get the currently-active theme colors. </summary>
|
||||||
|
public static ChardonnayTheme GetLiveTheme()
|
||||||
|
{
|
||||||
|
var theme = new ChardonnayTheme();
|
||||||
|
|
||||||
|
foreach (var themeVariant in FluentVariants)
|
||||||
|
{
|
||||||
|
//Get the Libation-specific brushes
|
||||||
|
var themeBrushes = (ResourceDictionary)App.Current.Resources.ThemeDictionaries[themeVariant];
|
||||||
|
foreach (var colorName in themeBrushes.Keys.OfType<string>())
|
||||||
|
{
|
||||||
|
if (themeBrushes[colorName] is ISolidColorBrush brush)
|
||||||
|
{
|
||||||
|
//We're only working with colors, so convert the Brush's opacity to an alpha value
|
||||||
|
var color = Color.FromArgb
|
||||||
|
(
|
||||||
|
(byte)Math.Round(brush.Color.A * brush.Opacity, 0),
|
||||||
|
brush.Color.R,
|
||||||
|
brush.Color.G,
|
||||||
|
brush.Color.B
|
||||||
|
);
|
||||||
|
|
||||||
|
theme.ThemeColors[themeVariant][colorName] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the fluent theme colors
|
||||||
|
foreach (var p in GetColorResourceProperties())
|
||||||
|
{
|
||||||
|
var color = (Color)p.GetValue(ColorPalettes[themeVariant])!;
|
||||||
|
|
||||||
|
//The color isn't being overridden, so get the static resource value.
|
||||||
|
if (color == default)
|
||||||
|
{
|
||||||
|
var staticResourceName = p.Name == nameof(ColorPaletteResources.RegionColor) ? "SystemRegionColor" : $"System{p.Name}Color";
|
||||||
|
if (App.Current.TryGetResource(staticResourceName, themeVariant, out var colorObj) && colorObj is Color c)
|
||||||
|
color = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
theme.ThemeColors[themeVariant][p.Name] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Clone()
|
||||||
|
{
|
||||||
|
var clone = new ChardonnayTheme();
|
||||||
|
foreach (var t in ThemeColors)
|
||||||
|
{
|
||||||
|
clone.ThemeColors[t.Key] = t.Value.ToDictionary();
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<PropertyInfo> GetColorResourceProperties()
|
||||||
|
=> typeof(ColorPaletteResources).GetProperties().Where(p => p.PropertyType == typeof(Color));
|
||||||
|
|
||||||
|
[System.Diagnostics.StackTraceHidden]
|
||||||
|
private static void ValidateThemeVariant(ThemeVariant themeVariant)
|
||||||
|
{
|
||||||
|
if (!FluentVariants.Contains(themeVariant))
|
||||||
|
throw new InvalidOperationException("FluentTheme.Palettes only supports Light and Dark variants.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ThemeVariant FromVariantName(string? variantName)
|
||||||
|
=> variantName switch
|
||||||
|
{
|
||||||
|
nameof(ThemeVariant.Dark) => ThemeVariant.Dark,
|
||||||
|
nameof(ThemeVariant.Light) => ThemeVariant.Light,
|
||||||
|
// "System"
|
||||||
|
_ => ThemeVariant.Default
|
||||||
|
};
|
||||||
|
}
|
||||||
61
Source/LibationAvalonia/Themes/ChardonnayThemePersister.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using Avalonia.Media;
|
||||||
|
using Dinah.Core.IO;
|
||||||
|
using FileManager;
|
||||||
|
using LibationFileManager;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
namespace LibationAvalonia.Themes;
|
||||||
|
|
||||||
|
public class ChardonnayThemePersister : JsonFilePersister<ChardonnayTheme>
|
||||||
|
{
|
||||||
|
public static string jsonPath = System.IO.Path.Combine(Configuration.Instance.LibationFiles, "ChardonnayTheme.json");
|
||||||
|
|
||||||
|
public ChardonnayThemePersister(string path)
|
||||||
|
: base(path, null) { }
|
||||||
|
public ChardonnayThemePersister(ChardonnayTheme target, string path)
|
||||||
|
: base(target, path, null) { }
|
||||||
|
|
||||||
|
protected override JsonSerializerSettings GetSerializerSettings()
|
||||||
|
=> new JsonSerializerSettings { Converters = { new ColorConverter() } };
|
||||||
|
|
||||||
|
public static ChardonnayThemePersister? Create()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return System.IO.File.Exists(jsonPath)
|
||||||
|
? new ChardonnayThemePersister(jsonPath)
|
||||||
|
: new ChardonnayThemePersister(ChardonnayTheme.GetLiveTheme(), jsonPath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex, $"Failed to open {jsonPath}. Overwriting with empty file.");
|
||||||
|
FileUtility.SaferDelete(jsonPath);
|
||||||
|
return new ChardonnayThemePersister(ChardonnayTheme.GetLiveTheme(), jsonPath);
|
||||||
|
}
|
||||||
|
catch (Exception ex2)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex2, $"Failed to open {jsonPath}.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Store colors as #ARGB values so that the json file is easier to manually edit </summary>
|
||||||
|
private class ColorConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override bool CanConvert(Type objectType) => objectType == typeof(Color);
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
|
=> reader.Value is string hexColor && Color.TryParse(hexColor, out var color) ? color : default;
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value is Color color)
|
||||||
|
writer.WriteValue(color.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,3 @@
|
|||||||
using Avalonia.Media;
|
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using LibationUiBase.GridView;
|
using LibationUiBase.GridView;
|
||||||
@ -9,8 +8,6 @@ namespace LibationAvalonia.ViewModels
|
|||||||
{
|
{
|
||||||
public class AvaloniaEntryStatus : EntryStatus, IEntryStatus, IComparable
|
public class AvaloniaEntryStatus : EntryStatus, IEntryStatus, IComparable
|
||||||
{
|
{
|
||||||
public override IBrush BackgroundBrush => IsEpisode ? App.SeriesEntryGridBackgroundBrush : Brushes.Transparent;
|
|
||||||
|
|
||||||
private AvaloniaEntryStatus(LibraryBook libraryBook) : base(libraryBook) { }
|
private AvaloniaEntryStatus(LibraryBook libraryBook) : base(libraryBook) { }
|
||||||
public static EntryStatus Create(LibraryBook libraryBook) => new AvaloniaEntryStatus(libraryBook);
|
public static EntryStatus Create(LibraryBook libraryBook) => new AvaloniaEntryStatus(libraryBook);
|
||||||
|
|
||||||
|
|||||||
@ -61,7 +61,7 @@ namespace LibationAvalonia.ViewModels
|
|||||||
|
|
||||||
#region Properties exposed to the view
|
#region Properties exposed to the view
|
||||||
public ProcessBookResult Result { get => _result; set { this.RaiseAndSetIfChanged(ref _result, value); this.RaisePropertyChanged(nameof(StatusText)); } }
|
public ProcessBookResult Result { get => _result; set { this.RaiseAndSetIfChanged(ref _result, value); this.RaisePropertyChanged(nameof(StatusText)); } }
|
||||||
public ProcessBookStatus Status { get => _status; set { this.RaiseAndSetIfChanged(ref _status, value); this.RaisePropertyChanged(nameof(BackgroundColor)); this.RaisePropertyChanged(nameof(IsFinished)); this.RaisePropertyChanged(nameof(IsDownloading)); this.RaisePropertyChanged(nameof(Queued)); } }
|
public ProcessBookStatus Status { get => _status; set { this.RaiseAndSetIfChanged(ref _status, value); this.RaisePropertyChanged(nameof(IsFinished)); this.RaisePropertyChanged(nameof(IsDownloading)); this.RaisePropertyChanged(nameof(Queued)); } }
|
||||||
public string? Narrator { get => _narrator; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _narrator, value)); }
|
public string? Narrator { get => _narrator; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _narrator, value)); }
|
||||||
public string? Author { get => _author; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _author, value)); }
|
public string? Author { get => _author; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _author, value)); }
|
||||||
public string? Title { get => _title; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _title, value)); }
|
public string? Title { get => _title; set => Dispatcher.UIThread.Invoke(() => this.RaiseAndSetIfChanged(ref _title, value)); }
|
||||||
@ -72,20 +72,13 @@ namespace LibationAvalonia.ViewModels
|
|||||||
public bool IsDownloading => Status is ProcessBookStatus.Working;
|
public bool IsDownloading => Status is ProcessBookStatus.Working;
|
||||||
public bool Queued => Status is ProcessBookStatus.Queued;
|
public bool Queued => Status is ProcessBookStatus.Queued;
|
||||||
|
|
||||||
public IBrush BackgroundColor => Status switch
|
|
||||||
{
|
|
||||||
ProcessBookStatus.Cancelled => App.ProcessQueueBookCancelledBrush,
|
|
||||||
ProcessBookStatus.Completed => App.ProcessQueueBookCompletedBrush,
|
|
||||||
ProcessBookStatus.Failed => App.ProcessQueueBookFailedBrush,
|
|
||||||
_ => App.ProcessQueueBookDefaultBrush,
|
|
||||||
};
|
|
||||||
public string StatusText => Result switch
|
public string StatusText => Result switch
|
||||||
{
|
{
|
||||||
ProcessBookResult.Success => "Finished",
|
ProcessBookResult.Success => "Finished",
|
||||||
ProcessBookResult.Cancelled => "Cancelled",
|
ProcessBookResult.Cancelled => "Cancelled",
|
||||||
ProcessBookResult.ValidationFail => "Validion fail",
|
ProcessBookResult.ValidationFail => "Validation fail",
|
||||||
ProcessBookResult.FailedRetry => "Error, will retry later",
|
ProcessBookResult.FailedRetry => "Error, will retry later",
|
||||||
ProcessBookResult.FailedSkip => "Error, Skippping",
|
ProcessBookResult.FailedSkip => "Error, Skipping",
|
||||||
ProcessBookResult.FailedAbort => "Error, Abort",
|
ProcessBookResult.FailedAbort => "Error, Abort",
|
||||||
ProcessBookResult.LicenseDenied => "License Denied",
|
ProcessBookResult.LicenseDenied => "License Denied",
|
||||||
ProcessBookResult.LicenseDeniedPossibleOutage => "Possible Service Interruption",
|
ProcessBookResult.LicenseDeniedPossibleOutage => "Possible Service Interruption",
|
||||||
|
|||||||
@ -45,7 +45,6 @@ namespace LibationAvalonia.ViewModels.Settings
|
|||||||
config.CreationTime = CreationTime.Value;
|
config.CreationTime = CreationTime.Value;
|
||||||
config.LastWriteTime = LastWriteTime.Value;
|
config.LastWriteTime = LastWriteTime.Value;
|
||||||
config.LogLevel = LoggingLevel;
|
config.LogLevel = LoggingLevel;
|
||||||
Configuration.Instance.SetString(ThemeVariant, nameof(ThemeVariant));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float scaleFactorToLinearRange(float scaleFactor)
|
private static float scaleFactorToLinearRange(float scaleFactor)
|
||||||
@ -95,20 +94,7 @@ namespace LibationAvalonia.ViewModels.Settings
|
|||||||
public string ThemeVariant
|
public string ThemeVariant
|
||||||
{
|
{
|
||||||
get => themeVariant;
|
get => themeVariant;
|
||||||
set
|
set => this.RaiseAndSetIfChanged(ref themeVariant, value);
|
||||||
{
|
|
||||||
var changed = !value.Equals(themeVariant);
|
|
||||||
this.RaiseAndSetIfChanged(ref themeVariant, value);
|
|
||||||
|
|
||||||
if (changed && App.Current is Avalonia.Application app)
|
|
||||||
app.RequestedThemeVariant = themeVariant switch
|
|
||||||
{
|
|
||||||
nameof(Avalonia.Styling.ThemeVariant.Dark) => Avalonia.Styling.ThemeVariant.Dark,
|
|
||||||
nameof(Avalonia.Styling.ThemeVariant.Light) => Avalonia.Styling.ThemeVariant.Light,
|
|
||||||
// "System"
|
|
||||||
_ => Avalonia.Styling.ThemeVariant.Default
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,8 +26,8 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Button:disabled /template/ ContentPresenter#PART_ContentPresenter">
|
<Style Selector="Button:disabled /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="{DynamicResource SystemChromeDisabledLowColor}" />
|
||||||
<Setter Property="BorderBrush" Value="Transparent" />
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemChromeDisabledLowColor}" />
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
|
|
||||||
@ -72,12 +72,6 @@
|
|||||||
IsVisible="{CompiledBinding IsError}"
|
IsVisible="{CompiledBinding IsError}"
|
||||||
Fill="{DynamicResource CancelRed}"
|
Fill="{DynamicResource CancelRed}"
|
||||||
Data="{StaticResource BookErrorIcon}" />
|
Data="{StaticResource BookErrorIcon}" />
|
||||||
|
|
||||||
<Path
|
|
||||||
Stretch="Fill"
|
|
||||||
IsVisible="{CompiledBinding !IsButtonEnabled}"
|
|
||||||
Fill="{DynamicResource DisabledGrayBrush}"
|
|
||||||
Data="M0,0 H1 V1 H0" />
|
|
||||||
</Panel>
|
</Panel>
|
||||||
</Viewbox>
|
</Viewbox>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -160,7 +160,7 @@
|
|||||||
</Menu>
|
</Menu>
|
||||||
|
|
||||||
<StackPanel IsVisible="{CompiledBinding ActivelyScanning}" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
<StackPanel IsVisible="{CompiledBinding ActivelyScanning}" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
<Path VerticalAlignment="Center" Fill="{StaticResource IconFill}" Data="{StaticResource ImportIcon}" />
|
<Path VerticalAlignment="Center" Fill="{DynamicResource IconFill}" Data="{StaticResource ImportIcon}" />
|
||||||
<TextBlock Margin="5,0,5,0" VerticalAlignment="Center" Text="{CompiledBinding ScanningText}"/>
|
<TextBlock Margin="5,0,5,0" VerticalAlignment="Center" Text="{CompiledBinding ScanningText}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -221,6 +221,7 @@
|
|||||||
Name="productsDisplay"
|
Name="productsDisplay"
|
||||||
DataContext="{CompiledBinding ProductsDisplay}"
|
DataContext="{CompiledBinding ProductsDisplay}"
|
||||||
LiberateClicked="ProductsDisplay_LiberateClicked"
|
LiberateClicked="ProductsDisplay_LiberateClicked"
|
||||||
|
TagsButtonClicked="ProductsDisplay_TagsButtonClicked"
|
||||||
LiberateSeriesClicked="ProductsDisplay_LiberateSeriesClicked"
|
LiberateSeriesClicked="ProductsDisplay_LiberateSeriesClicked"
|
||||||
ConvertToMp3Clicked="ProductsDisplay_ConvertToMp3Clicked" />
|
ConvertToMp3Clicked="ProductsDisplay_ConvertToMp3Clicked" />
|
||||||
</SplitView>
|
</SplitView>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ using Avalonia.ReactiveUI;
|
|||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
|
using LibationAvalonia.Dialogs;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using LibationUiBase.GridView;
|
using LibationUiBase.GridView;
|
||||||
@ -137,6 +138,18 @@ namespace LibationAvalonia.Views
|
|||||||
public void ProductsDisplay_LiberateSeriesClicked(object _, ISeriesEntry series) => ViewModel.LiberateSeriesClicked(series);
|
public void ProductsDisplay_LiberateSeriesClicked(object _, ISeriesEntry series) => ViewModel.LiberateSeriesClicked(series);
|
||||||
public void ProductsDisplay_ConvertToMp3Clicked(object _, LibraryBook libraryBook) => ViewModel.ConvertToMp3Clicked(libraryBook);
|
public void ProductsDisplay_ConvertToMp3Clicked(object _, LibraryBook libraryBook) => ViewModel.ConvertToMp3Clicked(libraryBook);
|
||||||
|
|
||||||
|
BookDetailsDialog bookDetailsForm;
|
||||||
|
public void ProductsDisplay_TagsButtonClicked(object _, LibraryBook libraryBook)
|
||||||
|
{
|
||||||
|
if (bookDetailsForm is null || !bookDetailsForm.IsVisible)
|
||||||
|
{
|
||||||
|
bookDetailsForm = new BookDetailsDialog(libraryBook);
|
||||||
|
bookDetailsForm.Show(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
bookDetailsForm.LibraryBook = libraryBook;
|
||||||
|
}
|
||||||
|
|
||||||
public async void filterSearchTb_KeyPress(object _, KeyEventArgs e)
|
public async void filterSearchTb_KeyPress(object _, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == Key.Return)
|
if (e.Key == Key.Return)
|
||||||
|
|||||||
@ -3,9 +3,10 @@
|
|||||||
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="clr-namespace:LibationAvalonia.ViewModels"
|
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels"
|
||||||
|
xmlns:views="clr-namespace:LibationAvalonia.Views"
|
||||||
x:DataType="vm:ProcessBookViewModel"
|
x:DataType="vm:ProcessBookViewModel"
|
||||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="87" MaxHeight="87" MinHeight="87" MinWidth="300"
|
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="87" MaxHeight="87" MinHeight="87" MinWidth="300"
|
||||||
x:Class="LibationAvalonia.Views.ProcessBookControl" Background="{CompiledBinding BackgroundColor}">
|
x:Class="LibationAvalonia.Views.ProcessBookControl">
|
||||||
|
|
||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<Style Selector="Border#QueuedItemBorder:not(:pointerover) Button">
|
<Style Selector="Border#QueuedItemBorder:not(:pointerover) Button">
|
||||||
@ -14,6 +15,18 @@
|
|||||||
<Style Selector="Border#QueuedItemBorder:pointerover Button">
|
<Style Selector="Border#QueuedItemBorder:pointerover Button">
|
||||||
<Setter Property="IsVisible" Value="True" />
|
<Setter Property="IsVisible" Value="True" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style Selector="views|ProcessBookControl">
|
||||||
|
<Setter Property="ProcessBookStatus" Value="{CompiledBinding Status}" />
|
||||||
|
<Style Selector="^[ProcessBookStatus=Cancelled]">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ProcessQueueBookCancelledBrush}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[ProcessBookStatus=Failed]">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ProcessQueueBookFailedBrush}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[ProcessBookStatus=Completed]">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ProcessQueueBookCompletedBrush}" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
</UserControl.Styles>
|
</UserControl.Styles>
|
||||||
|
|
||||||
<Border Name="QueuedItemBorder" Background="Transparent" BorderBrush="{DynamicResource SystemControlForegroundBaseMediumBrush}" BorderThickness="0,0,0,1">
|
<Border Name="QueuedItemBorder" Background="Transparent" BorderBrush="{DynamicResource SystemControlForegroundBaseMediumBrush}" BorderThickness="0,0,0,1">
|
||||||
@ -68,7 +81,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Panel Margin="3,0,0,0" Grid.Column="1" VerticalAlignment="Top" IsVisible="{CompiledBinding !IsFinished}">
|
<Panel Margin="3,0,0,0" Grid.Column="1" VerticalAlignment="Top" IsVisible="{CompiledBinding !IsFinished}">
|
||||||
<Button Height="32" Background="{DynamicResource CancelRed}" Width="22" CornerRadius="11" Click="Cancel_Click">
|
<Button Height="32" Background="{DynamicResource CancelRed}" Width="22" CornerRadius="11" Click="Cancel_Click">
|
||||||
<Path Fill="{DynamicResource ProcessQueueBookDefaultBrush}" VerticalAlignment="Center" Data="{StaticResource CancelButtonIcon}" RenderTransform="{StaticResource Rotate45Transform}" />
|
<Path Fill="{DynamicResource SystemAltHighColor}" VerticalAlignment="Center" Data="{StaticResource CancelButtonIcon}" RenderTransform="{StaticResource Rotate45Transform}" />
|
||||||
</Button>
|
</Button>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
@ -12,6 +13,16 @@ namespace LibationAvalonia.Views
|
|||||||
{
|
{
|
||||||
public static event QueueItemPositionButtonClicked PositionButtonClicked;
|
public static event QueueItemPositionButtonClicked PositionButtonClicked;
|
||||||
public static event QueueItemCancelButtonClicked CancelButtonClicked;
|
public static event QueueItemCancelButtonClicked CancelButtonClicked;
|
||||||
|
|
||||||
|
public static readonly StyledProperty<ProcessBookStatus> ProcessBookStatusProperty =
|
||||||
|
AvaloniaProperty.Register<ProcessBookControl, ProcessBookStatus>(nameof(ProcessBookStatus), enableDataValidation: true);
|
||||||
|
|
||||||
|
public ProcessBookStatus ProcessBookStatus
|
||||||
|
{
|
||||||
|
get => GetValue(ProcessBookStatusProperty);
|
||||||
|
set => SetValue(ProcessBookStatusProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ProcessBookControl()
|
public ProcessBookControl()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
xmlns:viewModels="clr-namespace:LibationAvalonia.ViewModels"
|
xmlns:viewModels="clr-namespace:LibationAvalonia.ViewModels"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="650"
|
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="650"
|
||||||
|
Background="{DynamicResource SystemRegionColor}"
|
||||||
x:Class="LibationAvalonia.Views.ProcessQueueControl">
|
x:Class="LibationAvalonia.Views.ProcessQueueControl">
|
||||||
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
@ -35,7 +36,7 @@
|
|||||||
<TextBlock FontSize="14" VerticalAlignment="Center">Process Queue</TextBlock>
|
<TextBlock FontSize="14" VerticalAlignment="Center">Process Queue</TextBlock>
|
||||||
</TabItem.Header>
|
</TabItem.Header>
|
||||||
<Grid ColumnDefinitions="*" RowDefinitions="*,40">
|
<Grid ColumnDefinitions="*" RowDefinitions="*,40">
|
||||||
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource SystemBaseMediumColor}" Background="{DynamicResource SystemChromeMediumLowColor}">
|
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource SystemBaseMediumColor}" Background="{DynamicResource SystemRegionColor}">
|
||||||
<ScrollViewer
|
<ScrollViewer
|
||||||
Name="scroller"
|
Name="scroller"
|
||||||
HorizontalScrollBarVisibility="Disabled"
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
@ -81,7 +82,7 @@
|
|||||||
<TextBlock FontSize="14" VerticalAlignment="Center">Queue Log</TextBlock>
|
<TextBlock FontSize="14" VerticalAlignment="Center">Queue Log</TextBlock>
|
||||||
</TabItem.Header>
|
</TabItem.Header>
|
||||||
<Grid ColumnDefinitions="*" RowDefinitions="*,40">
|
<Grid ColumnDefinitions="*" RowDefinitions="*,40">
|
||||||
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource SystemBaseMediumColor}" Background="{DynamicResource SystemChromeMediumLowColor}">
|
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource SystemBaseMediumColor}" Background="{DynamicResource SystemRegionColor}">
|
||||||
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding LogEntries}">
|
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding LogEntries}">
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn SortMemberPath="LogDate" Header="Timestamp" CanUserSort="True" Binding="{Binding LogDateString}" Width="90"/>
|
<DataGridTextColumn SortMemberPath="LogDate" Header="Timestamp" CanUserSort="True" Binding="{Binding LogDateString}" Width="90"/>
|
||||||
|
|||||||
@ -9,13 +9,15 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
public partial class ProcessQueueControl : UserControl
|
public partial class ProcessQueueControl : UserControl
|
||||||
{
|
{
|
||||||
private TrackedQueue<ProcessBookViewModel> Queue => _viewModel.Queue;
|
private TrackedQueue<ProcessBookViewModel>? Queue => _viewModel?.Queue;
|
||||||
private ProcessQueueViewModel _viewModel => DataContext as ProcessQueueViewModel;
|
private ProcessQueueViewModel? _viewModel => DataContext as ProcessQueueViewModel;
|
||||||
|
|
||||||
public ProcessQueueControl()
|
public ProcessQueueControl()
|
||||||
{
|
{
|
||||||
@ -25,6 +27,7 @@ namespace LibationAvalonia.Views
|
|||||||
ProcessBookControl.CancelButtonClicked += ProcessBookControl2_CancelButtonClicked;
|
ProcessBookControl.CancelButtonClicked += ProcessBookControl2_CancelButtonClicked;
|
||||||
|
|
||||||
#region Design Mode Testing
|
#region Design Mode Testing
|
||||||
|
#if DEBUG
|
||||||
if (Design.IsDesignMode)
|
if (Design.IsDesignMode)
|
||||||
{
|
{
|
||||||
var vm = new ProcessQueueViewModel();
|
var vm = new ProcessQueueViewModel();
|
||||||
@ -85,6 +88,7 @@ namespace LibationAvalonia.Views
|
|||||||
vm.Queue.MoveNext();
|
vm.Queue.MoveNext();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,53 +102,59 @@ namespace LibationAvalonia.Views
|
|||||||
private async void ProcessBookControl2_CancelButtonClicked(ProcessBookViewModel item)
|
private async void ProcessBookControl2_CancelButtonClicked(ProcessBookViewModel item)
|
||||||
{
|
{
|
||||||
if (item is not null)
|
if (item is not null)
|
||||||
|
{
|
||||||
await item.CancelAsync();
|
await item.CancelAsync();
|
||||||
Queue.RemoveQueued(item);
|
Queue?.RemoveQueued(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessBookControl2_ButtonClicked(ProcessBookViewModel item, QueuePosition queueButton)
|
private void ProcessBookControl2_ButtonClicked(ProcessBookViewModel item, QueuePosition queueButton)
|
||||||
{
|
{
|
||||||
Queue.MoveQueuePosition(item, queueButton);
|
Queue?.MoveQueuePosition(item, queueButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void CancelAllBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void CancelAllBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Queue.ClearQueue();
|
Queue?.ClearQueue();
|
||||||
if (Queue.Current is not null)
|
if (Queue?.Current is not null)
|
||||||
await Queue.Current.CancelAsync();
|
await Queue.Current.CancelAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearFinishedBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void ClearFinishedBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Queue.ClearCompleted();
|
Queue?.ClearCompleted();
|
||||||
|
|
||||||
if (!_viewModel.Running)
|
if (_viewModel?.Running is false)
|
||||||
_viewModel.RunningTime = string.Empty;
|
_viewModel.RunningTime = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearLogBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void ClearLogBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_viewModel.LogEntries.Clear();
|
_viewModel?.LogEntries.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void LogCopyBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private async void LogCopyBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
string logText = string.Join("\r\n", _viewModel.LogEntries.Select(r => $"{r.LogDate.ToShortDateString()} {r.LogDate.ToShortTimeString()}\t{r.LogMessage}"));
|
if (_viewModel is ProcessQueueViewModel vm)
|
||||||
await App.MainWindow.Clipboard.SetTextAsync(logText);
|
{
|
||||||
|
string logText = string.Join("\r\n", vm.LogEntries.Select(r => $"{r.LogDate.ToShortDateString()} {r.LogDate.ToShortTimeString()}\t{r.LogMessage}"));
|
||||||
|
if (App.MainWindow?.Clipboard?.SetTextAsync(logText) is Task setter)
|
||||||
|
await setter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void cancelAllBtn_Click(object sender, EventArgs e)
|
private async void cancelAllBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Queue.ClearQueue();
|
Queue?.ClearQueue();
|
||||||
if (Queue.Current is not null)
|
if (Queue?.Current is not null)
|
||||||
await Queue.Current.CancelAsync();
|
await Queue.Current.CancelAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnClearFinished_Click(object sender, EventArgs e)
|
private void btnClearFinished_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Queue.ClearCompleted();
|
Queue?.ClearCompleted();
|
||||||
|
|
||||||
if (!_viewModel.Running)
|
if (_viewModel?.Running is false)
|
||||||
_viewModel.RunningTime = string.Empty;
|
_viewModel.RunningTime = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +165,7 @@ namespace LibationAvalonia.Views
|
|||||||
{
|
{
|
||||||
public static readonly DecimalConverter Instance = new();
|
public static readonly DecimalConverter Instance = new();
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
if (value is string sourceText && targetType.IsAssignableTo(typeof(decimal?)))
|
if (value is string sourceText && targetType.IsAssignableTo(typeof(decimal?)))
|
||||||
{
|
{
|
||||||
@ -172,7 +182,7 @@ namespace LibationAvalonia.Views
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
if (value is decimal val)
|
if (value is decimal val)
|
||||||
{
|
{
|
||||||
@ -184,7 +194,7 @@ namespace LibationAvalonia.Views
|
|||||||
: val.ToString("F2")
|
: val.ToString("F2")
|
||||||
) + " MB/s";
|
) + " MB/s";
|
||||||
}
|
}
|
||||||
return value.ToString();
|
return value?.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
ItemsSource="{Binding GridEntries}"
|
ItemsSource="{Binding GridEntries}"
|
||||||
CanUserSortColumns="True" BorderThickness="3"
|
CanUserSortColumns="True" BorderThickness="3"
|
||||||
CanUserResizeColumns="True"
|
CanUserResizeColumns="True"
|
||||||
|
LoadingRow="ProductsDisplay_LoadingRow"
|
||||||
CanUserReorderColumns="True">
|
CanUserReorderColumns="True">
|
||||||
|
|
||||||
<DataGrid.Styles>
|
<DataGrid.Styles>
|
||||||
@ -93,7 +94,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Title" MinWidth="10" Width="{Binding TitleWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Title" ClipboardContentBinding="{Binding Title}">
|
<controls:DataGridTemplateColumnExt Header="Title" MinWidth="10" Width="{Binding TitleWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Title" ClipboardContentBinding="{Binding Title}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Classes="h1" Text="{CompiledBinding Title}" />
|
<TextBlock Classes="h1" Text="{CompiledBinding Title}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -103,7 +104,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Authors" MinWidth="10" Width="{Binding AuthorsWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Authors" ClipboardContentBinding="{Binding Authors}">
|
<controls:DataGridTemplateColumnExt Header="Authors" MinWidth="10" Width="{Binding AuthorsWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Authors" ClipboardContentBinding="{Binding Authors}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Text="{CompiledBinding Authors}" />
|
<TextBlock Text="{CompiledBinding Authors}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -113,7 +114,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Narrators" MinWidth="10" Width="{Binding NarratorsWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Narrators" ClipboardContentBinding="{Binding Narrators}">
|
<controls:DataGridTemplateColumnExt Header="Narrators" MinWidth="10" Width="{Binding NarratorsWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Narrators" ClipboardContentBinding="{Binding Narrators}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Text="{CompiledBinding Narrators}" />
|
<TextBlock Text="{CompiledBinding Narrators}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -123,7 +124,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Length" MinWidth="10" Width="{Binding LengthWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Length" ClipboardContentBinding="{Binding Length}">
|
<controls:DataGridTemplateColumnExt Header="Length" MinWidth="10" Width="{Binding LengthWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Length" ClipboardContentBinding="{Binding Length}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Text="{CompiledBinding Length}" />
|
<TextBlock Text="{CompiledBinding Length}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -133,7 +134,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Series" MinWidth="10" Width="{Binding SeriesWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Series" ClipboardContentBinding="{Binding Series}">
|
<controls:DataGridTemplateColumnExt Header="Series" MinWidth="10" Width="{Binding SeriesWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Series" ClipboardContentBinding="{Binding Series}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Text="{CompiledBinding Series}" />
|
<TextBlock Text="{CompiledBinding Series}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -143,7 +144,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Series
Order" MinWidth="10" Width="{Binding SeriesOrderWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="SeriesOrder" ClipboardContentBinding="{Binding Series}">
|
<controls:DataGridTemplateColumnExt Header="Series
Order" MinWidth="10" Width="{Binding SeriesOrderWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="SeriesOrder" ClipboardContentBinding="{Binding Series}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Text="{CompiledBinding SeriesOrder}" HorizontalAlignment="Center" />
|
<TextBlock Text="{CompiledBinding SeriesOrder}" HorizontalAlignment="Center" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -153,7 +154,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Description" MinWidth="10" Width="{Binding DescriptionWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Description" ClipboardContentBinding="{Binding Description}">
|
<controls:DataGridTemplateColumnExt Header="Description" MinWidth="10" Width="{Binding DescriptionWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Description" ClipboardContentBinding="{Binding Description}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}" Tapped="Description_Click" ToolTip.Tip="Click to see full description" >
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Tapped="Description_Click" ToolTip.Tip="Click to see full description" >
|
||||||
<TextBlock Text="{CompiledBinding Description}" VerticalAlignment="Top" />
|
<TextBlock Text="{CompiledBinding Description}" VerticalAlignment="Top" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -163,7 +164,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Category" MinWidth="10" Width="{Binding CategoryWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Category" ClipboardContentBinding="{Binding Category}">
|
<controls:DataGridTemplateColumnExt Header="Category" MinWidth="10" Width="{Binding CategoryWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Category" ClipboardContentBinding="{Binding Category}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Text="{CompiledBinding Category}" />
|
<TextBlock Text="{CompiledBinding Category}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -177,14 +178,13 @@
|
|||||||
MinWidth="10" Width="{Binding ProductRatingWidth, Mode=TwoWay}"
|
MinWidth="10" Width="{Binding ProductRatingWidth, Mode=TwoWay}"
|
||||||
SortMemberPath="ProductRating" CanUserSort="True"
|
SortMemberPath="ProductRating" CanUserSort="True"
|
||||||
OpacityBinding="{CompiledBinding Liberate.Opacity}"
|
OpacityBinding="{CompiledBinding Liberate.Opacity}"
|
||||||
BackgroundBinding="{CompiledBinding Liberate.BackgroundBrush}"
|
|
||||||
ClipboardContentBinding="{CompiledBinding ProductRating}"
|
ClipboardContentBinding="{CompiledBinding ProductRating}"
|
||||||
Binding="{CompiledBinding ProductRating}" />
|
Binding="{CompiledBinding ProductRating}" />
|
||||||
|
|
||||||
<controls:DataGridTemplateColumnExt Header="Purchase
Date" MinWidth="10" Width="{Binding PurchaseDateWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="PurchaseDate" ClipboardContentBinding="{Binding PurchaseDate}">
|
<controls:DataGridTemplateColumnExt Header="Purchase
Date" MinWidth="10" Width="{Binding PurchaseDateWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="PurchaseDate" ClipboardContentBinding="{Binding PurchaseDate}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Text="{CompiledBinding PurchaseDate}" />
|
<TextBlock Text="{CompiledBinding PurchaseDate}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -198,14 +198,13 @@
|
|||||||
MinWidth="10" Width="{Binding MyRatingWidth, Mode=TwoWay}"
|
MinWidth="10" Width="{Binding MyRatingWidth, Mode=TwoWay}"
|
||||||
SortMemberPath="MyRating" CanUserSort="True"
|
SortMemberPath="MyRating" CanUserSort="True"
|
||||||
OpacityBinding="{CompiledBinding Liberate.Opacity}"
|
OpacityBinding="{CompiledBinding Liberate.Opacity}"
|
||||||
BackgroundBinding="{CompiledBinding Liberate.BackgroundBrush}"
|
|
||||||
ClipboardContentBinding="{CompiledBinding MyRating}"
|
ClipboardContentBinding="{CompiledBinding MyRating}"
|
||||||
Binding="{CompiledBinding MyRating, Mode=TwoWay}" />
|
Binding="{CompiledBinding MyRating, Mode=TwoWay}" />
|
||||||
|
|
||||||
<controls:DataGridTemplateColumnExt Header="Misc" MinWidth="10" Width="{Binding MiscWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Misc" ClipboardContentBinding="{Binding Misc}">
|
<controls:DataGridTemplateColumnExt Header="Misc" MinWidth="10" Width="{Binding MiscWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="Misc" ClipboardContentBinding="{Binding Misc}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<TextBlock Text="{CompiledBinding Misc}" TextWrapping="WrapWithOverflow" />
|
<TextBlock Text="{CompiledBinding Misc}" TextWrapping="WrapWithOverflow" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
@ -215,7 +214,7 @@
|
|||||||
<controls:DataGridTemplateColumnExt Header="Last
Download" MinWidth="10" Width="{Binding LastDownloadWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="LastDownload" ClipboardContentBinding="{Binding LastDownload}">
|
<controls:DataGridTemplateColumnExt Header="Last
Download" MinWidth="10" Width="{Binding LastDownloadWidth, Mode=TwoWay}" CanUserSort="True" SortMemberPath="LastDownload" ClipboardContentBinding="{Binding LastDownload}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate x:DataType="uibase:IGridEntry">
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}" ToolTip.Tip="{CompiledBinding LastDownload.ToolTipText}" DoubleTapped="Version_DoubleClick">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" ToolTip.Tip="{CompiledBinding LastDownload.ToolTipText}" DoubleTapped="Version_DoubleClick">
|
||||||
<TextBlock Text="{CompiledBinding LastDownload}" TextWrapping="WrapWithOverflow" />
|
<TextBlock Text="{CompiledBinding LastDownload}" TextWrapping="WrapWithOverflow" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input.Platform;
|
||||||
|
using Avalonia.Media;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
@ -17,16 +19,18 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
public partial class ProductsDisplay : UserControl
|
public partial class ProductsDisplay : UserControl
|
||||||
{
|
{
|
||||||
public event EventHandler<LibraryBook> LiberateClicked;
|
public event EventHandler<LibraryBook>? LiberateClicked;
|
||||||
public event EventHandler<ISeriesEntry> LiberateSeriesClicked;
|
public event EventHandler<ISeriesEntry>? LiberateSeriesClicked;
|
||||||
public event EventHandler<LibraryBook> ConvertToMp3Clicked;
|
public event EventHandler<LibraryBook>? ConvertToMp3Clicked;
|
||||||
|
public event EventHandler<LibraryBook>? TagsButtonClicked;
|
||||||
|
|
||||||
private ProductsDisplayViewModel _viewModel => DataContext as ProductsDisplayViewModel;
|
private ProductsDisplayViewModel? _viewModel => DataContext as ProductsDisplayViewModel;
|
||||||
ImageDisplayDialog imageDisplayDialog;
|
ImageDisplayDialog? imageDisplayDialog;
|
||||||
|
|
||||||
public ProductsDisplay()
|
public ProductsDisplay()
|
||||||
{
|
{
|
||||||
@ -52,6 +56,8 @@ namespace LibationAvalonia.Views
|
|||||||
Configuration.Instance.PropertyChanged += Configuration_GridScaleChanged;
|
Configuration.Instance.PropertyChanged += Configuration_GridScaleChanged;
|
||||||
Configuration.Instance.PropertyChanged += Configuration_FontChanged;
|
Configuration.Instance.PropertyChanged += Configuration_FontChanged;
|
||||||
|
|
||||||
|
#region Design Mode Testing
|
||||||
|
#if DEBUG
|
||||||
if (Design.IsDesignMode)
|
if (Design.IsDesignMode)
|
||||||
{
|
{
|
||||||
using var context = DbContexts.GetContext();
|
using var context = DbContexts.GetContext();
|
||||||
@ -80,6 +86,8 @@ namespace LibationAvalonia.Views
|
|||||||
setFontScale(1);
|
setFontScale(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#endregion
|
||||||
|
|
||||||
setGridScale(Configuration.Instance.GridScaleFactor);
|
setGridScale(Configuration.Instance.GridScaleFactor);
|
||||||
setFontScale(Configuration.Instance.GridFontScaleFactor);
|
setFontScale(Configuration.Instance.GridFontScaleFactor);
|
||||||
@ -91,6 +99,14 @@ namespace LibationAvalonia.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ProductsDisplay_LoadingRow(object sender, DataGridRowEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Row.DataContext is LibraryBookEntry<AvaloniaEntryStatus> entry && entry.Liberate.IsEpisode)
|
||||||
|
e.Row.DynamicResource(DataGridRow.BackgroundProperty, "SeriesEntryGridBackgroundBrush");
|
||||||
|
else
|
||||||
|
e.Row.DynamicResource(DataGridRow.BackgroundProperty, "SystemRegionColor");
|
||||||
|
}
|
||||||
|
|
||||||
private void RemoveColumn_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
private void RemoveColumn_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is DataGridColumn col && e.Property == DataGridColumn.IsVisibleProperty)
|
if (sender is DataGridColumn col && e.Property == DataGridColumn.IsVisibleProperty)
|
||||||
@ -105,13 +121,15 @@ namespace LibationAvalonia.Views
|
|||||||
[PropertyChangeFilter(nameof(Configuration.GridScaleFactor))]
|
[PropertyChangeFilter(nameof(Configuration.GridScaleFactor))]
|
||||||
private void Configuration_GridScaleChanged(object sender, Dinah.Core.PropertyChangedEventArgsEx e)
|
private void Configuration_GridScaleChanged(object sender, Dinah.Core.PropertyChangedEventArgsEx e)
|
||||||
{
|
{
|
||||||
setGridScale((float)e.NewValue);
|
if (e.NewValue is float value)
|
||||||
|
setGridScale(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[PropertyChangeFilter(nameof(Configuration.GridFontScaleFactor))]
|
[PropertyChangeFilter(nameof(Configuration.GridFontScaleFactor))]
|
||||||
private void Configuration_FontChanged(object sender, Dinah.Core.PropertyChangedEventArgsEx e)
|
private void Configuration_FontChanged(object sender, Dinah.Core.PropertyChangedEventArgsEx e)
|
||||||
{
|
{
|
||||||
setFontScale((float)e.NewValue);
|
if (e.NewValue is float value)
|
||||||
|
setFontScale(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Style rowHeightStyle;
|
private readonly Style rowHeightStyle;
|
||||||
@ -171,17 +189,18 @@ namespace LibationAvalonia.Views
|
|||||||
|
|
||||||
#region Cell Context Menu
|
#region Cell Context Menu
|
||||||
|
|
||||||
public void ProductsGrid_CellContextMenuStripNeeded(object sender, DataGridCellContextMenuStripNeededEventArgs args)
|
public void ProductsGrid_CellContextMenuStripNeeded(object? sender, DataGridCellContextMenuStripNeededEventArgs args)
|
||||||
{
|
{
|
||||||
var entry = args.GridEntry;
|
var entry = args.GridEntry;
|
||||||
var ctx = new GridContextMenu(entry, '_');
|
var ctx = new GridContextMenu(entry, '_');
|
||||||
|
|
||||||
if (args.Column.SortMemberPath is not "Liberate" and not "Cover")
|
if (args.Column.SortMemberPath is not "Liberate" and not "Cover"
|
||||||
|
&& App.MainWindow?.Clipboard is IClipboard clipboard)
|
||||||
{
|
{
|
||||||
args.ContextMenuItems.Add(new MenuItem
|
args.ContextMenuItems.Add(new MenuItem
|
||||||
{
|
{
|
||||||
Header = ctx.CopyCellText,
|
Header = ctx.CopyCellText,
|
||||||
Command = ReactiveCommand.CreateFromTask(() => App.MainWindow.Clipboard.SetTextAsync(args.CellClipboardContents))
|
Command = ReactiveCommand.CreateFromTask(() => clipboard?.SetTextAsync(args.CellClipboardContents) ?? Task.CompletedTask)
|
||||||
});
|
});
|
||||||
args.ContextMenuItems.Add(new Separator());
|
args.ContextMenuItems.Add(new Separator());
|
||||||
}
|
}
|
||||||
@ -240,13 +259,14 @@ namespace LibationAvalonia.Views
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var window = this.GetParentWindow();
|
if (this.GetParentWindow() is not Window window)
|
||||||
|
return;
|
||||||
|
|
||||||
var openFileDialogOptions = new FilePickerOpenOptions
|
var openFileDialogOptions = new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = ctx.LocateFileDialogTitle,
|
Title = ctx.LocateFileDialogTitle,
|
||||||
AllowMultiple = false,
|
AllowMultiple = false,
|
||||||
SuggestedStartLocation = await window.StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix),
|
SuggestedStartLocation = await window.StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books?.PathWithoutPrefix!),
|
||||||
FileTypeFilter = new FilePickerFileType[]
|
FileTypeFilter = new FilePickerFileType[]
|
||||||
{
|
{
|
||||||
new("All files (*.*)") { Patterns = new[] { "*" } },
|
new("All files (*.*)") { Patterns = new[] { "*" } },
|
||||||
@ -303,7 +323,7 @@ namespace LibationAvalonia.Views
|
|||||||
{
|
{
|
||||||
var template = ctx.CreateTemplateEditor<T>(libraryBook, existingTemplate);
|
var template = ctx.CreateTemplateEditor<T>(libraryBook, existingTemplate);
|
||||||
var form = new EditTemplateDialog(template);
|
var form = new EditTemplateDialog(template);
|
||||||
if (await form.ShowDialog<DialogResult>(this.GetParentWindow()) == DialogResult.OK)
|
if (this.GetParentWindow() is Window window && await form.ShowDialog<DialogResult>(window) == DialogResult.OK)
|
||||||
{
|
{
|
||||||
setNewTemplate(template.EditingTemplate.TemplateText);
|
setNewTemplate(template.EditingTemplate.TemplateText);
|
||||||
}
|
}
|
||||||
@ -340,12 +360,12 @@ namespace LibationAvalonia.Views
|
|||||||
|
|
||||||
#region View Bookmarks/Clips
|
#region View Bookmarks/Clips
|
||||||
|
|
||||||
if (!entry.Liberate.IsSeries)
|
if (!entry.Liberate.IsSeries && VisualRoot is Window window)
|
||||||
{
|
{
|
||||||
args.ContextMenuItems.Add(new MenuItem
|
args.ContextMenuItems.Add(new MenuItem
|
||||||
{
|
{
|
||||||
Header = ctx.ViewBookmarksText,
|
Header = ctx.ViewBookmarksText,
|
||||||
Command = ReactiveCommand.CreateFromTask(() => new BookRecordsDialog(entry.LibraryBook).ShowDialog(VisualRoot as Window))
|
Command = ReactiveCommand.CreateFromTask(() => new BookRecordsDialog(entry.LibraryBook).ShowDialog(window))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,6 +409,9 @@ namespace LibationAvalonia.Views
|
|||||||
|
|
||||||
var HeaderCell_PI = typeof(DataGridColumn).GetProperty("HeaderCell", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
var HeaderCell_PI = typeof(DataGridColumn).GetProperty("HeaderCell", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||||
|
|
||||||
|
if (HeaderCell_PI is null)
|
||||||
|
return;
|
||||||
|
|
||||||
foreach (var column in productsGrid.Columns)
|
foreach (var column in productsGrid.Columns)
|
||||||
{
|
{
|
||||||
var itemName = column.SortMemberPath;
|
var itemName = column.SortMemberPath;
|
||||||
@ -406,8 +429,9 @@ namespace LibationAvalonia.Views
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
var headercell = HeaderCell_PI.GetValue(column) as DataGridColumnHeader;
|
var headerCell = HeaderCell_PI.GetValue(column) as DataGridColumnHeader;
|
||||||
headercell.ContextMenu = contextMenu;
|
if (headerCell is not null)
|
||||||
|
headerCell.ContextMenu = contextMenu;
|
||||||
|
|
||||||
column.IsVisible = gridColumnsVisibilities.GetValueOrDefault(itemName, true);
|
column.IsVisible = gridColumnsVisibilities.GetValueOrDefault(itemName, true);
|
||||||
}
|
}
|
||||||
@ -425,30 +449,30 @@ namespace LibationAvalonia.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ContextMenu_ContextMenuOpening(object sender, System.ComponentModel.CancelEventArgs e)
|
private void ContextMenu_ContextMenuOpening(object? sender, System.ComponentModel.CancelEventArgs e)
|
||||||
{
|
{
|
||||||
var contextMenu = sender as ContextMenu;
|
if (sender is not ContextMenu contextMenu)
|
||||||
|
return;
|
||||||
foreach (var mi in contextMenu.Items.OfType<MenuItem>())
|
foreach (var mi in contextMenu.Items.OfType<MenuItem>())
|
||||||
{
|
{
|
||||||
if (mi.Tag is DataGridColumn column)
|
if (mi.Tag is DataGridColumn column && mi.Icon is CheckBox cbox)
|
||||||
{
|
{
|
||||||
var cbox = mi.Icon as CheckBox;
|
|
||||||
cbox.IsChecked = column.IsVisible;
|
cbox.IsChecked = column.IsVisible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ContextMenu_MenuClosed(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private void ContextMenu_MenuClosed(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var contextMenu = sender as ContextMenu;
|
if (sender is not ContextMenu contextMenu)
|
||||||
|
return;
|
||||||
var config = Configuration.Instance;
|
var config = Configuration.Instance;
|
||||||
var dictionary = config.GridColumnsVisibilities;
|
var dictionary = config.GridColumnsVisibilities;
|
||||||
|
|
||||||
foreach (var mi in contextMenu.Items.OfType<MenuItem>())
|
foreach (var mi in contextMenu.Items.OfType<MenuItem>())
|
||||||
{
|
{
|
||||||
if (mi.Tag is DataGridColumn column)
|
if (mi.Tag is DataGridColumn column && mi.Icon is CheckBox cbox)
|
||||||
{
|
{
|
||||||
var cbox = mi.Icon as CheckBox;
|
|
||||||
column.IsVisible = cbox.IsChecked == true;
|
column.IsVisible = cbox.IsChecked == true;
|
||||||
dictionary[column.SortMemberPath] = cbox.IsChecked == true;
|
dictionary[column.SortMemberPath] = cbox.IsChecked == true;
|
||||||
}
|
}
|
||||||
@ -463,7 +487,7 @@ namespace LibationAvalonia.Views
|
|||||||
config.GridColumnsVisibilities = dictionary;
|
config.GridColumnsVisibilities = dictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProductsGrid_ColumnDisplayIndexChanged(object sender, DataGridColumnEventArgs e)
|
private void ProductsGrid_ColumnDisplayIndexChanged(object? sender, DataGridColumnEventArgs e)
|
||||||
{
|
{
|
||||||
var config = Configuration.Instance;
|
var config = Configuration.Instance;
|
||||||
|
|
||||||
@ -478,9 +502,10 @@ namespace LibationAvalonia.Views
|
|||||||
|
|
||||||
public async void LiberateButton_Click(object sender, EventArgs e)
|
public async void LiberateButton_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var button = sender as LiberateStatusButton;
|
if (sender is not LiberateStatusButton button)
|
||||||
|
return;
|
||||||
|
|
||||||
if (button.DataContext is ISeriesEntry sEntry)
|
if (button.DataContext is ISeriesEntry sEntry && _viewModel is not null)
|
||||||
{
|
{
|
||||||
await _viewModel.ToggleSeriesExpanded(sEntry);
|
await _viewModel.ToggleSeriesExpanded(sEntry);
|
||||||
|
|
||||||
@ -518,7 +543,7 @@ namespace LibationAvalonia.Views
|
|||||||
|
|
||||||
var picDef = new PictureDefinition(gEntry.LibraryBook.Book.PictureLarge ?? gEntry.LibraryBook.Book.PictureId, PictureSize.Native);
|
var picDef = new PictureDefinition(gEntry.LibraryBook.Book.PictureLarge ?? gEntry.LibraryBook.Book.PictureId, PictureSize.Native);
|
||||||
|
|
||||||
void PictureCached(object sender, PictureCachedEventArgs e)
|
void PictureCached(object? sender, PictureCachedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Definition.PictureId == picDef.PictureId)
|
if (e.Definition.PictureId == picDef.PictureId)
|
||||||
imageDisplayDialog.SetCoverBytes(e.Picture);
|
imageDisplayDialog.SetCoverBytes(e.Picture);
|
||||||
@ -558,7 +583,7 @@ namespace LibationAvalonia.Views
|
|||||||
DescriptionText = gEntry.Description,
|
DescriptionText = gEntry.Description,
|
||||||
};
|
};
|
||||||
|
|
||||||
void CloseWindow(object o, DataGridRowEventArgs e)
|
void CloseWindow(object? o, DataGridRowEventArgs e)
|
||||||
{
|
{
|
||||||
displayWindow.Close();
|
displayWindow.Close();
|
||||||
}
|
}
|
||||||
@ -572,21 +597,13 @@ namespace LibationAvalonia.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BookDetailsDialog bookDetailsForm;
|
|
||||||
|
|
||||||
public void OnTagsButtonClick(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
public void OnTagsButtonClick(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var button = args.Source as Button;
|
var button = args.Source as Button;
|
||||||
|
|
||||||
if (button.DataContext is ILibraryBookEntry lbEntry && VisualRoot is Window window)
|
if (button?.DataContext is ILibraryBookEntry lbEntry)
|
||||||
{
|
{
|
||||||
if (bookDetailsForm is null || !bookDetailsForm.IsVisible)
|
TagsButtonClicked?.Invoke(this, lbEntry.LibraryBook);
|
||||||
{
|
|
||||||
bookDetailsForm = new BookDetailsDialog(lbEntry.LibraryBook);
|
|
||||||
bookDetailsForm.Show(window);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
bookDetailsForm.LibraryBook = lbEntry.LibraryBook;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="LibationAvalonia.Views.SeriesViewDialog"
|
x:Class="LibationAvalonia.Views.SeriesViewDialog"
|
||||||
Icon="/Assets/libation.ico"
|
|
||||||
Title="View All Items in Series">
|
Title="View All Items in Series">
|
||||||
|
|
||||||
<TabControl
|
<TabControl
|
||||||
|
|||||||
@ -61,7 +61,6 @@ namespace LibationUiBase.GridView
|
|||||||
|| PdfStatus is not null and not LiberatedStatus.Liberated
|
|| PdfStatus is not null and not LiberatedStatus.Liberated
|
||||||
);
|
);
|
||||||
public double Opacity => !IsSeries && Book.UserDefinedItem.Tags.ContainsInsensitive("hidden") ? 0.4 : 1;
|
public double Opacity => !IsSeries && Book.UserDefinedItem.Tags.ContainsInsensitive("hidden") ? 0.4 : 1;
|
||||||
public abstract object BackgroundBrush { get; }
|
|
||||||
public object ButtonImage => GetLiberateIcon();
|
public object ButtonImage => GetLiberateIcon();
|
||||||
public string ToolTip => GetTooltip();
|
public string ToolTip => GetTooltip();
|
||||||
private Book Book { get; }
|
private Book Book { get; }
|
||||||
|
|||||||
@ -7,7 +7,7 @@ namespace LibationWinForms.GridView
|
|||||||
public class WinFormsEntryStatus : EntryStatus, IEntryStatus
|
public class WinFormsEntryStatus : EntryStatus, IEntryStatus
|
||||||
{
|
{
|
||||||
private static readonly Color SERIES_BG_COLOR = Color.FromArgb(230, 255, 230);
|
private static readonly Color SERIES_BG_COLOR = Color.FromArgb(230, 255, 230);
|
||||||
public override object BackgroundBrush => IsEpisode ? SERIES_BG_COLOR : SystemColors.ControlLightLight;
|
public Color BackgroundBrush => IsEpisode ? SERIES_BG_COLOR : SystemColors.ControlLightLight;
|
||||||
|
|
||||||
private WinFormsEntryStatus(LibraryBook libraryBook) : base(libraryBook) { }
|
private WinFormsEntryStatus(LibraryBook libraryBook) : base(libraryBook) { }
|
||||||
public static EntryStatus Create(LibraryBook libraryBook) => new WinFormsEntryStatus(libraryBook);
|
public static EntryStatus Create(LibraryBook libraryBook) => new WinFormsEntryStatus(libraryBook);
|
||||||
|
|||||||
@ -65,9 +65,9 @@ namespace LibationWinForms.ProcessQueue
|
|||||||
ProcessBookResult.Success => ("Finished", ProcessBookStatus.Completed),
|
ProcessBookResult.Success => ("Finished", ProcessBookStatus.Completed),
|
||||||
ProcessBookResult.Cancelled => ("Cancelled", ProcessBookStatus.Cancelled),
|
ProcessBookResult.Cancelled => ("Cancelled", ProcessBookStatus.Cancelled),
|
||||||
ProcessBookResult.FailedRetry => ("Error, will retry later", ProcessBookStatus.Failed),
|
ProcessBookResult.FailedRetry => ("Error, will retry later", ProcessBookStatus.Failed),
|
||||||
ProcessBookResult.FailedSkip => ("Error, Skippping", ProcessBookStatus.Failed),
|
ProcessBookResult.FailedSkip => ("Error, Skipping", ProcessBookStatus.Failed),
|
||||||
ProcessBookResult.FailedAbort => ("Error, Abort", ProcessBookStatus.Failed),
|
ProcessBookResult.FailedAbort => ("Error, Abort", ProcessBookStatus.Failed),
|
||||||
ProcessBookResult.ValidationFail => ("Validion fail", ProcessBookStatus.Failed),
|
ProcessBookResult.ValidationFail => ("Validation fail", ProcessBookStatus.Failed),
|
||||||
ProcessBookResult.LicenseDenied => ("License Denied", ProcessBookStatus.Failed),
|
ProcessBookResult.LicenseDenied => ("License Denied", ProcessBookStatus.Failed),
|
||||||
ProcessBookResult.LicenseDeniedPossibleOutage => ("Possible Service Interruption", ProcessBookStatus.Failed),
|
ProcessBookResult.LicenseDeniedPossibleOutage => ("Possible Service Interruption", ProcessBookStatus.Failed),
|
||||||
_ => ("UNKNOWN", ProcessBookStatus.Failed),
|
_ => ("UNKNOWN", ProcessBookStatus.Failed),
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 630 B |
|
After Width: | Height: | Size: 798 B |
|
After Width: | Height: | Size: 924 B |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 35 KiB |
@ -1,34 +1,36 @@
|
|||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 524 524" enable-background="new 0 0 524 524">
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 288 288" enable-background="new 0 0 288 288">
|
||||||
<defs>
|
<defs>
|
||||||
<g id="glass">
|
<g id="glass">
|
||||||
<path fill-rule="evenodd" d=
|
<path transform="translate(16 16)" fill-rule="evenodd" d=
|
||||||
"M262,8
|
"M177,16
|
||||||
h-117
|
H79
|
||||||
a 192,200 0 0 0 -36,82
|
A 32.0781 63.7932 -1.5106 0 0 66 80
|
||||||
a 222,334 41 0 0 138,236
|
A 158.789 471.1259 41.9466 0 0 90 131
|
||||||
v158
|
A 81.7197 122.0515 35.3745 0 0 128 143.3484
|
||||||
h-81
|
A 81.7197 122.0515 -35.3745 0 0 166 131
|
||||||
a 16,16 0 0 0 0,32
|
A 158.789 471.1259 -41.9466 0 0 190 80
|
||||||
h192
|
A 32.0781 63.7932 1.5106 0 0 177 16
|
||||||
a 16 16 0 0 0 0,-32
|
L 184 0
|
||||||
h-81
|
A 44.7901 78.5247 1.1521 0 1 194 122
|
||||||
v-158
|
A 97.0039 135.3148 -36.2124 0 1 136 159
|
||||||
a 222,334 -41 0 0 138,-236
|
V 240
|
||||||
a 192,200 0 0 0 -36,-82
|
H 176
|
||||||
h-117
|
A 8 8 0 0 1 176 256
|
||||||
m-99,30
|
H 80
|
||||||
a 192,200 0 0 0 -26,95
|
A 8 8 0 0 1 80 240
|
||||||
a 187.5,334 35 0 0 125,159
|
H 120
|
||||||
a 187.5,334 -35 0 0 125,-159
|
V 159
|
||||||
a 192,200 0 0 0 -26,-95
|
A 97.0039 135.3148 36.2124 0 1 62 122
|
||||||
h-198
|
A 44.7901 78.5247 -1.1521 0 1 72 0
|
||||||
|
H184
|
||||||
z"/>
|
z"/>
|
||||||
</g>
|
</g>
|
||||||
<g id="wine-level">
|
<g transform="translate(16 16)" id="wine-level">
|
||||||
<path d=
|
<path d=
|
||||||
"M158,136
|
"M182,64
|
||||||
a 168,305 35 0 0 104,136
|
H 74
|
||||||
a 168,305 -35 0 0 104,-136
|
A 115.9979 308.8033 38.9474 0 0 128 134.4277
|
||||||
|
A 115.9979 308.8033 -38.9474 0 0 182,64
|
||||||
z"/>
|
z"/>
|
||||||
</g>
|
</g>
|
||||||
</defs>
|
</defs>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 1.2 KiB |