Compare commits

...
Author SHA1 Message Date
Aigio Liu 9f171c740c 🐛 Linux IClipboardPlatformService 2023-08-27 23:52:26 +08:00
RMBGAME 83de4dacb9 🐛 ✨ 💄 消息通知设置
& 确认交易修复
& 游戏图片重置
& 主页滚动修复
2023-08-27 23:47:16 +08:00
Aigio Liu 719bd1e08f 🐛 fix DNS proxy 2023-08-27 18:44:39 +08:00
ff m 08655eb0c1 修改 AppHost 启动 2023-08-27 16:58:07 +08:00
ff m a886b21b79 修改 r.result == DbRowExecResult.Update 判断是否更新 2023-08-27 16:08:50 +08:00
Aigio Liu 27f113dce3 ✨ Startup.RunSelfProcess 2023-08-27 15:31:23 +08:00
RMBGAME a4adbac8d5 🐛 Fixed 确认交易 登录错误 Bug 2023-08-27 15:00:37 +08:00
Aigio Liu 6356d98a0c 🐛 18 2023-08-27 14:40:21 +08:00
RMBGAME a1ba6f22fb 🐛 修复加速项目重复情况 2023-08-27 14:30:21 +08:00
RMBGAME 2a9e3bc455 🐛 修复长时间运行后,加速页面GC回收造成的 UI 异常 2023-08-27 13:55:08 +08:00
RMBGAME d768bcfe7b 💄 优化窗口在非 GPU 渲染下的样式 2023-08-27 11:54:47 +08:00
RMBGAME b5d8cd1a55 🐛 Misc 2023-08-27 11:14:55 +08:00
RMBGAME fdd8c5f0bc 💄 HomePage UI 2023-08-27 10:41:36 +08:00
RMBGAME b5439743d1 ✨ 游戏工具 实现 2023-08-27 10:03:10 +08:00
RMBGAME a1de4f50a7 ✨ 游戏工具 2023-08-27 07:35:17 +08:00
Aigio Liu 3b266ba614 fix 2023-08-27 07:09:14 +08:00
Aigio Liu 3405117e86 🐛 12,16 2023-08-27 07:02:47 +08:00
RMBGAME 5bd21d4474 💄 🐛 Fixed Bugs & Update UI 2023-08-27 06:53:10 +08:00
ff m 4bd049d94a 关闭代理时IP null 2023-08-27 04:06:55 +08:00
RMBGAME 15a2b7f42a 🐛 Fixed Bugs 2023-08-27 00:54:03 +08:00
RMBGAME d6c88ac361 🐛 Fixed Bugs 2023-08-27 00:12:51 +08:00
ff m f1c58637aa 第一个和最后一个分别上移和下移不做处理 2023-08-27 00:07:51 +08:00
ff m fa5b21a2f9 修复 Linux 运行时启动 修复 Mac 系统代理不使用 0.0.0.0 XmlReader 没有 Async 实现 修复保存令牌 判断写反了 2023-08-27 00:07:50 +08:00
Aigio Liu 2894227048 🚧 3.0.0-preview.2 2023-08-26 18:34:09 +08:00
105 changed files with 2438 additions and 1177 deletions
+10 -1
View File
@@ -171,7 +171,7 @@
{
"TypeName": "string",
"PropertyName": "WindowBackgroundCustomImagePath",
"DefaultValue": "\"/UI/Assets/back.png\"",
"DefaultValue": "\"avares://BD.WTTS.Client.Avalonia/UI/Assets/back.png\"",
"DefaultValueIsConst": true,
"Summary": "自定义背景图像路径",
"IsRegionOrEndregion": null,
@@ -265,6 +265,15 @@
"IsRegionOrEndregion": null,
"IsValueType": null
},
{
"TypeName": "bool",
"PropertyName": "MessagePopupNotification",
"DefaultValue": "true",
"DefaultValueIsConst": true,
"Summary": "启用消息推送通知",
"IsRegionOrEndregion": null,
"IsValueType": null
},
{
"TypeName": "bool",
"PropertyName": "GameListUseLocalCache",
+10 -5
View File
@@ -14,7 +14,7 @@ public static partial class AssemblyInfo
/// <summary>
/// 预览版本号,范围 1~9
/// </summary>
const string ver_for_preview = "1";
const string ver_for_preview = "2";
/// <summary>
/// RC 版本号,范围 0~9
@@ -29,14 +29,19 @@ public static partial class AssemblyInfo
#if !APP_HOST
/// <summary>
/// 当前应用程序是否为预览版本
/// 当前应用程序是否为预览版本(Preview)
/// </summary>
public static bool IsPreview => InformationalVersion.Contains("preview", StringComparison.OrdinalIgnoreCase);
public static bool IsPreview { get; } = InformationalVersion.Contains("preview", StringComparison.OrdinalIgnoreCase);
/// <summary>
/// 当前应用程序是否为候选版本
/// 当前应用程序是否为候选版本(RC)
/// </summary>
public static bool IsReleaseCandidate => !IsPreview && InformationalVersion.Contains("rc", StringComparison.OrdinalIgnoreCase);
public static bool IsReleaseCandidate { get; } = !IsPreview && InformationalVersion.Contains("rc", StringComparison.OrdinalIgnoreCase);
/// <summary>
/// 当前应用程序是否为正式版本(GA)
/// </summary>
public static bool IsGeneralAvailability { get; } = !IsPreview && !IsReleaseCandidate;
#endif
/// <summary>
@@ -104,6 +104,9 @@ sealed partial class Program : Startup
services.AddSingleton<IApplication>(s => s.GetRequiredService<App>());
services.TryAddAvaloniaFilePickerPlatformService();
#if LINUX
services.AddSingleton<IClipboardPlatformService, AvaloniaClipboardPlatformService>();
#endif
#region WindowManager
@@ -36,7 +36,10 @@ public sealed class OnPlatformExtension : MarkupExtension<bool>
"DesktopBridge" => DesktopBridge.IsRunningAsUwp,
"SteamApp" => Startup.Instance.IsSteamRun,
"OfficialRelease" => !(Startup.Instance.IsSteamRun || DesktopBridge.IsRunningAsUwp),
"Preview" => AssemblyInfo.InformationalVersion.Contains("preview", StringComparison.OrdinalIgnoreCase),
"Preview" => AssemblyInfo.IsPreview,
"RC" => AssemblyInfo.IsReleaseCandidate,
"GA" => AssemblyInfo.IsGeneralAvailability,
"!GA" => !AssemblyInfo.IsGeneralAvailability,
_ => false,
};
}
@@ -92,7 +92,8 @@ sealed class AvaloniaWindowManagerImpl : IWindowManagerImpl
string? okButtonText = null,
string? retryButtonText = null,
string? moreInfoText = null,
Func<bool>? cancelCloseAction = null)
Func<bool>? cancelCloseAction = null,
bool disableScroll = false)
where TPageViewModel : ViewModelBase
{
var td = new TaskDialogEx
@@ -143,6 +144,10 @@ sealed class AvaloniaWindowManagerImpl : IWindowManagerImpl
{
td.Buttons.Add(new TaskDialogButton(Strings.Cancel, TaskDialogStandardResult.Cancel));
}
if (disableScroll)
{
td.Classes.Add("disableScroll");
}
//td.DataTemplates.Add(new FuncDataTemplate<DebugPageViewModel>((x, _) => new DebugPage(), true));
object? result = null;
@@ -0,0 +1,36 @@
#if LINUX
namespace BD.WTTS.Services.Implementation;
public sealed class AvaloniaClipboardPlatformService : IClipboardPlatformService
{
public bool PlatformHasText => !string.IsNullOrEmpty(PlatformGetTextAsync().GetAwaiter().GetResult());
public async Task<string> PlatformGetTextAsync()
{
TopLevel? topLevel = App.Instance.MainWindow;
if (topLevel != null)
{
var clipboard = topLevel.Clipboard;
if (clipboard != null)
{
var result = await clipboard.GetTextAsync();
return result ?? string.Empty;
}
}
return string.Empty;
}
public async Task PlatformSetTextAsync(string text)
{
TopLevel? topLevel = App.Instance.MainWindow;
if (topLevel != null)
{
var clipboard = topLevel.Clipboard;
if (clipboard != null)
{
await clipboard.SetTextAsync(text);
}
}
}
}
#endif
@@ -50,13 +50,24 @@ public sealed class NavigationService : INavigationService
_overlayHost = p;
}
public void Navigate(Type t, NavigationTransitionEffect effect = NavigationTransitionEffect.None)
public void Navigate(Type? t, NavigationTransitionEffect effect = NavigationTransitionEffect.None)
{
if (t == null)
{
_frame?.Navigate(typeof(ErrorPage), null, GetNavigationTransitionInfo(effect));
return;
}
if (t.IsSubclassOf(typeof(ViewModelBase)))
{
var pageType = GetViewModelToPageContent(t, false);
if (pageType == null) return;
t = (Type)pageType;
t = pageType as Type;
}
if (t == null)
{
_frame?.Navigate(typeof(ErrorPage), null, GetNavigationTransitionInfo(effect));
return;
}
if (_frame?.Content?.GetType() != t)
@@ -127,7 +127,6 @@ public sealed partial class App : Application
{
MainThread2.BeginInvokeOnMainThread(() =>
{
IPCMainProcessService.Instance.DisposeAsync();
desktop.Shutdown(exitCode);
});
return true;
@@ -385,6 +385,147 @@
</Style>
<Style Selector="^.disableScroll">
<Setter Property="Template">
<ControlTemplate>
<Panel Name="LayoutRoot" Background="{x:Null}">
<ui:FABorder
Name="ContentRoot"
MinWidth="{DynamicResource TaskDialogMinWidth}"
MinHeight="{DynamicResource TaskDialogMinHeight}"
MaxWidth="{DynamicResource TaskDialogMaxWidth}"
MaxHeight="{DynamicResource TaskDialogMaxHeight}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}">
<Grid RowDefinitions="Auto,Auto,*,Auto">
<Panel
Name="HeaderHost"
Grid.Row="0"
Background="{TemplateBinding HeaderBackground}"
IsVisible="False">
<Viewbox
Name="IconHost"
Width="{StaticResource TaskDialogIconSize}"
Height="{StaticResource TaskDialogIconSize}"
Margin="{DynamicResource TaskDialogIconMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="False">
<ui:IconSourceElement Name="IconElement" IconSource="{TemplateBinding IconSource}" />
</Viewbox>
<TextBlock
Name="HeaderText"
Margin="{DynamicResource TaskDialogHeaderPadding}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="{DynamicResource TaskDialogHeaderFontSize}"
FontWeight="{StaticResource TaskDialogHeaderFontWeight}"
IsVisible="False"
Text="{TemplateBinding Header}"
TextWrapping="Wrap" />
<Button
Name="CloseButton"
Margin="0,0,12,0"
HorizontalAlignment="Right"
Classes="Close"
Content="{StaticResource Close}"
Focusable="True"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
IsTabStop="True"
Theme="{StaticResource FA_SystemCaptionButton}" />
</Panel>
<TextBlock
Name="SubHeaderText"
Grid.Row="1"
Margin="{DynamicResource TaskDialogSubHeaderPadding}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="{StaticResource TaskDialogSubHeaderFontSize}"
FontWeight="{StaticResource TaskDialogSubHeaderFontWeight}"
IsVisible="False"
Text="{TemplateBinding SubHeader}"
TextWrapping="Wrap" />
<DockPanel Grid.Row="2" Margin="{StaticResource TaskDialogContentMargin}">
<ContentPresenter
Name="ContentPresenter"
Grid.Row="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}">
<ContentPresenter.Styles>
<Style Selector="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</ContentPresenter.Styles>
</ContentPresenter>
<StackPanel DockPanel.Dock="Bottom" Spacing="18">
<ProgressBar
Name="ProgressBar"
Grid.Row="3"
IsVisible="False" />
<ItemsControl
Name="CommandsHost"
Grid.Row="4"
IsVisible="{Binding !!$self.ItemCount}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<StackPanel
Name="MoreDetailsPanel"
Grid.Row="5"
IsVisible="False">
<Button
Name="MoreDetailsButton"
Classes="TaskDialog_MoreDetails"
Content="{x:Static spp:Strings.MoreInfo}"
IsVisible="False"
Theme="{StaticResource TaskDialogMoreDetailsButton}" />
<ContentPresenter
Name="FooterHost"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Top"
Content="{TemplateBinding Footer}"
ContentTemplate="{TemplateBinding FooterTemplate}"
IsVisible="False" />
</StackPanel>
</StackPanel>
</DockPanel>
<Border
Name="ButtonBorder"
Grid.Row="3"
Padding="{StaticResource TaskDialogButtonHostMargin}"
Background="{DynamicResource TaskDialogButtonAreaBackground}"
IsVisible="{Binding !!#ButtonsHost.ItemCount}">
<ItemsControl Name="ButtonsHost" KeyboardNavigation.TabNavigation="Continue">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<uip:TaskDialogButtonsPanel Spacing="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Grid>
</ui:FABorder>
</Panel>
</ControlTemplate>
</Setter>
</Style>
</ControlTheme>
</ResourceDictionary>
@@ -225,7 +225,7 @@
<Border Background="{TemplateBinding Background}" IsHitTestVisible="False" />
<Panel Name="WindowBackground" IsHitTestVisible="False">
<Border Background="{DynamicResource SolidBackgroundFillColorBaseBrush}" Opacity="{Binding Value, Source={x:Static ms:UISettings.WindowBackgroundOpacity}, Mode=OneWay}" />
<Image
<spp:Image2
IsVisible="{Binding Source={x:Static ms:UISettings.WindowBackgroundCustomImage}, Path=Value, Mode=OneWay}"
Opacity="{Binding Value, Source={x:Static ms:UISettings.WindowBackgroundCustomImageOpacity}, Mode=OneWay}"
RenderOptions.BitmapInterpolationMode="HighQuality"
@@ -19,6 +19,24 @@ public class ReactiveAppWindow<TViewModel> : AppWindow, IViewFor<TViewModel>, IV
/// </summary>
public ReactiveAppWindow() : base()
{
#if WINDOWS
if (OperatingSystem2.IsWindows() && !Design.IsDesignMode)
{
if (!GeneralSettings.GPU.Value)
{
var hWND = this.TryGetPlatformHandle()?.Handle;
if (hWND != null)
IPlatformService.Instance.BeautifyTheWindow(hWND.Value);
}
else if (UISettings.WindowBackgroundMaterial.Value is WindowBackgroundMaterial.None or WindowBackgroundMaterial.Blur)
{
var hWND = this.TryGetPlatformHandle()?.Handle;
if (hWND != null)
IPlatformService.Instance.BeautifyTheWindow(hWND.Value);
}
}
#endif
// This WhenActivated block calls ViewModel's WhenActivated
// block if the ViewModel implements IActivatableViewModel.
this.WhenActivated(disposables => { });
@@ -46,14 +64,6 @@ public class ReactiveAppWindow<TViewModel> : AppWindow, IViewFor<TViewModel>, IV
}
}
//this.GetObservable(TransparencyLevelHintProperty)
// .Subscribe(x =>
// {
// PseudoClasses.Set(":transparent", TransparencyLevelHint == WindowTransparencyLevel.Mica ||
// TransparencyLevelHint == WindowTransparencyLevel.Blur ||
// TransparencyLevelHint == WindowTransparencyLevel.AcrylicBlur);
// });
//UISettings.EnableCustomBackgroundImage.ValueChanged += (sender, e) =>
//{
// PseudoClasses.Set(":image", e.NewValue);
@@ -48,12 +48,6 @@ public class ContentLoader : ContentControl
public static readonly StyledProperty<bool> IsShowNoResultTextProperty =
AvaloniaProperty.Register<ContentLoader, bool>(nameof(IsShowNoResultText), false);
/// <summary>
/// Defines the <see cref="ItemsSource"/> property.
/// </summary>
public static readonly StyledProperty<IEnumerable?> ItemsSourceProperty =
ItemsControl.ItemsSourceProperty.AddOwner<ContentLoader>();
/// <summary>
/// 是否正在加载中
/// </summary>
@@ -105,12 +99,6 @@ public class ContentLoader : ContentControl
set => SetValue(MaximumProperty, value);
}
public IEnumerable? ItemsSource
{
get => GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
@@ -130,36 +118,12 @@ public class ContentLoader : ContentControl
}
}
}
else if (e.Property == IsLoadingProperty)
else if (e.Property == IsLoadingProperty || e.Property == IsShowNoResultTextProperty)
{
if (IsLoading && IsShowNoResultText)
{
IsShowNoResultText = false;
}
}
else if (e.Property == ItemsSourceProperty)
{
var items = e.GetNewValue<IEnumerable?>();
if (items == null)
{
IsLoading = true;
IsShowNoResultText = false;
}
else if (items.Count() == 0)
{
IsLoading = false;
IsShowNoResultText = true;
}
else if (items.Count() != 0)
{
IsLoading = false;
IsShowNoResultText = false;
}
else
{
IsLoading = true;
IsShowNoResultText = false;
}
}
}
}
@@ -77,8 +77,8 @@
VerticalAlignment="Top"
IsClosable="False"
IsOpen="True"
IsVisible="{spp:OnPlatform Preview}"
Message="当前使用的程序版本为预览版本,可能会有各种各样不可言明的 Bug,遇到问题请理性反馈,谢谢。"
IsVisible="{spp:OnPlatform !GA}"
Message="当前使用的程序版本为测试版本,可能会有各种各样不可言明的 Bug,遇到问题请理性反馈,谢谢。"
Severity="Warning" />
<spp:Snackbar
Title="{Binding Path=Res.Notice_Tray_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
@@ -268,6 +268,7 @@
</DockPanel>
<ScrollViewer
Name="ShopsScrollViewer"
Margin="0,3,0,20"
Padding="0,0,0,15"
HorizontalScrollBarVisibility="Auto"
@@ -334,7 +335,66 @@
</ItemsControl>
</ScrollViewer>
<TextBlock Text="联系我们" Theme="{StaticResource TitleTextBlockStyle}" />
<TextBlock
Margin="-2,0,0,0"
Text="已安装功能"
Theme="{StaticResource TitleTextBlockStyle}" />
<ItemsControl ItemsSource="{Binding MainWindow2.TabItems, Mode=OneWay, Source={x:Static s:IViewModelManager.Instance}}">
<ItemsControl.Resources>
<ControlTheme
x:Key="NavButtonStyle"
BasedOn="{StaticResource {x:Type Button}}"
TargetType="Button">
<Setter Property="Width" Value="120" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="6" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="ClipToBounds" Value="True" />
</Style>
</ControlTheme>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="spp:TabItemViewModel">
<Border spp:Animations.EnableAnimations="True">
<Button
Command="{Binding $parent[spp:HomePage].((spp:HomePageViewModel)DataContext).NavgationToMenuPageCommand}"
CommandParameter="{Binding}"
Theme="{StaticResource NavButtonStyle}">
<Grid RowDefinitions="3*,*">
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CornerRadius="4 4 0 0"
RenderOptions.BitmapInterpolationMode="HighQuality">
<spp:Image2
Width="96"
Height="96"
Margin="0,10,0,0"
Source="{Binding IconKey}"
Stretch="Uniform" />
</Border>
<TextBlock
Grid.Row="1"
Margin="12"
FontWeight="SemiBold"
Text="{Binding Name}" />
</Grid>
</Button>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--<TextBlock Text="联系我们" Theme="{StaticResource TitleTextBlockStyle}" />
<Grid Margin="0,0,0,20" ColumnDefinitions="*,10,*,10,*,10,*,10,*">
<spp:AppItem
@@ -387,7 +447,7 @@
</Border>
</spp:AppItem.Image>
</spp:AppItem>
</Grid>
</Grid>-->
</StackPanel>
@@ -9,8 +9,34 @@ public partial class HomePage : ReactiveUserControl<HomePageViewModel>
InitializeComponent();
this.SetViewModel<HomePageViewModel>();
//Subtitle = "11111111111111111111111111111";
//Description = "2222222222222222222222222222222";
//ContentDescription = "This is a description";
ShopsScrollViewer.PointerWheelChanged += ShopsScrollViewer_PointerWheelChanged;
}
private void ShopsScrollViewer_PointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
if (sender is ScrollViewer scroll)
if (scroll.IsPointerOver)
if (scroll.Extent.Height > scroll.Viewport.Height || scroll.Extent.Width > scroll.Viewport.Width)
{
var delta = e.Delta;
var isScrollStart = scroll.Offset.X == 0;
var isScrollEnd = scroll.Offset.X + scroll.Bounds.Size.Width >= scroll.Extent.Width;
if (isScrollStart && MathUtilities.IsOne(e.Delta.Y))
{
return;
}
if (isScrollEnd && e.Delta.Y == -1)
{
return;
}
if (MathUtilities.IsZero(delta.X))
{
var horizontalOffset = scroll.Offset.X;
horizontalOffset -= delta.Y * 50; // 根据事件参数来调整滚动的距离
scroll.Offset = new Vector(horizontalOffset, scroll.Offset.Y);
e.Handled = true;
}
}
}
}
@@ -135,7 +135,7 @@
Height="40"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Command="{Binding Path=MarkNotificationsHasRead, Mode=OneWay, Source={x:Static s:NotificationService.Current}}"
Command="{Binding Path=MarkNotificationsHasRead, Mode=OneWay, Source={x:Static s:NoticeService.Current}}"
Theme="{DynamicResource TransparentButton}">
<Panel>
<ui:InfoBadge
@@ -143,7 +143,7 @@
HorizontalAlignment="Right"
VerticalAlignment="Top"
Classes="Attention Dot"
IsVisible="{Binding Path=!!UnreadNotificationsCount, Mode=OneWay, Source={x:Static s:NotificationService.Current}}" />
IsVisible="{Binding Path=!!UnreadNotificationsCount, Mode=OneWay, Source={x:Static s:NoticeService.Current}}" />
<Viewbox
Width="22"
Height="22"
@@ -155,7 +155,7 @@
<DropDownButton.Flyout>
<!--<ui:FAMenuFlyout
FlyoutPresenterClasses="NoPadding"
ItemsSource="{Binding Path=Notices, Mode=OneWay, Source={x:Static s:NotificationService.Current}}"
ItemsSource="{Binding Path=Notices, Mode=OneWay, Source={x:Static s:NoticeService.Current}}"
Placement="BottomEdgeAlignedRight">
<ui:FAMenuFlyout.ItemTemplate>
<DataTemplate DataType="spp:OfficialMessageItemDTO">
@@ -72,6 +72,15 @@
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<spp:OptionsDisplayItem Header="启用消息推送通知">
<spp:OptionsDisplayItem.Icon>
<ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE7E7;" />
</spp:OptionsDisplayItem.Icon>
<spp:OptionsDisplayItem.ActionButton>
<ToggleSwitch Classes="LeftContent" IsChecked="{Binding Source={x:Static ms:GeneralSettings.MessagePopupNotification}, Path=Value, Mode=TwoWay}" />
</spp:OptionsDisplayItem.ActionButton>
</spp:OptionsDisplayItem>
<ui:SettingsExpander
Header="{Binding Path=Res.Settings_General_StorageSpace, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
IconSource="Globe"
@@ -15,7 +15,7 @@
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsVisible="{Binding Path=!!!Notices.Count, Mode=OneWay, Source={x:Static s:NotificationService.Current}}"
IsVisible="{Binding Path=!!!Notices.Count, Mode=OneWay, Source={x:Static s:NoticeService.Current}}"
Text="当前没有通知消息"
Theme="{StaticResource BodyStrongTextBlockStyle}" />
<DockPanel>
@@ -25,7 +25,7 @@
Cursor="Hand"
DockPanel.Dock="Top"
Theme="{StaticResource HyperlinkButton}" />-->
<ItemsControl ItemsSource="{Binding Path=Notices, Mode=OneWay, Source={x:Static s:NotificationService.Current}}">
<ItemsControl ItemsSource="{Binding Path=Notices, Mode=OneWay, Source={x:Static s:NoticeService.Current}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="3" />
@@ -41,7 +41,7 @@ public sealed partial class MainWindow : ReactiveAppWindow<MainWindowViewModel>
Task2.InBackground(async () =>
{
await AdvertiseService.Current.RefrshAdvertiseAsync();
NotificationService.Current.GetNewsAsync();
NoticeService.Current.GetNewsAsync();
});
}
}
@@ -139,7 +139,7 @@ public sealed class AppSplashScreen : IApplicationSplashScreen
});
AdvertiseService.Current.InitAdvertise();
NotificationService.Current.GetNewsAsync();
NoticeService.Current.GetNewsAsync();
var mainWindow = App.Instance.MainWindow;
mainWindow.ThrowIsNull();
@@ -195,6 +195,7 @@ public sealed class AppSplashScreen : IApplicationSplashScreen
await IViewModelManager.Instance.MainWindow.Initialize();
App.Instance.CompositeDisposable.Add(IViewModelManager.Instance.MainWindow);
App.Instance.CompositeDisposable.Add(SteamConnectService.Current.Dispose);
IsInitialized = true;
}, cancellationToken: token);
@@ -23,7 +23,7 @@ static void SetConsoleTitle(string title)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool IsProcessElevated_DEBUG_Only()
{
if (!OperatingSystem2.IsWindows())
if (!OperatingSystem.IsWindows())
{
return false;
}
@@ -122,24 +122,7 @@ public sealed class Plugin : PluginBase<Plugin>, IPlugin
public override async ValueTask OnExit()
{
var task1 = Task.Factory.StartNew(async () =>
{
IReverseProxyService? reverseProxyService = null;
try
{
reverseProxyService = Ioc.Get_Nullable<IReverseProxyService>();
}
catch
{
}
if (reverseProxyService != null)
{
await reverseProxyService.StopProxyAsync();
}
});
var task2 = Task.Factory.StartNew(ProxyService.OnExitRestoreHosts);
await Task.WhenAll(task1, task2);
await ProxyService.Current.ExitAsync();
}
string? subProcessPath;
@@ -14,6 +14,12 @@ partial class ProxyService
async Task<OperateProxyServiceResult> StartProxyServiceAsync()
{
IReadOnlyCollection<AccelerateProjectDTO>? proxyDomains = EnableProxyDomains;
if (!proxyDomains.Any_Nullable())
{
Toast.Show(ToastIcon.Warning, "你没有启用任何加速项!!");
}
IReadOnlyCollection<ScriptIPCDTO>? scripts = default;
bool isEnableScript = ProxySettings.IsEnableScript.Value;
bool isOnlyWorkSteamBrowser = ProxySettings.IsOnlyWorkSteamBrowser.Value;
@@ -140,9 +146,10 @@ partial class ProxyService
{
SetProxyIp(IPAddress.Loopback);
}
if (!platformService.SetAsSystemProxy(true,
var resultSetSystemProxy = await platformService.SetAsSystemProxyAsync(true,
proxyIp_.Value,
proxyPort))
proxyPort);
if (!resultSetSystemProxy)
{
return Strings.CommunityFix_SetAsSystemProxyFail;
}
@@ -154,7 +161,8 @@ partial class ProxyService
SetProxyIp(IPAddress.Loopback);
}
var pacUrl = $"http://{proxyIp_.Value}:{proxyPort}/pac";
if (!platformService.SetAsSystemPACProxy(true, pacUrl))
var resultSetSystemPACProxy = await platformService.SetAsSystemPACProxyAsync(true, pacUrl);
if (!resultSetSystemPACProxy)
{
return Strings.CommunityFix_SetAsSystemPACProxyFail;
}
@@ -268,10 +276,10 @@ partial class ProxyService
}
break;
case ProxyMode.System:
platformService.SetAsSystemProxy(false);
await platformService.SetAsSystemProxyAsync(false);
break;
case ProxyMode.PAC:
platformService.SetAsSystemPACProxy(false);
await platformService.SetAsSystemPACProxyAsync(false);
break;
}
StopTimer(); // 停止 UI 计时器
@@ -6,7 +6,7 @@ namespace BD.WTTS.Services;
public sealed partial class ProxyService
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
: ReactiveObject, IDisposable, IAsyncDisposable, IProxyService
: ReactiveObject, IProxyService
#endif
{
static ProxyService? mCurrent;
@@ -22,7 +22,7 @@ public sealed partial class ProxyService
{
mCurrent = this;
ProxyDomains = new SourceList<AccelerateProjectGroupDTO>();
ProxyDomains = new SourceCache<AccelerateProjectGroupDTO, string>(s => s.Name);
ProxyScripts = new SourceList<ScriptDTO>();
ProxyDomains
@@ -85,7 +85,7 @@ public sealed partial class ProxyService
}));
}
public SourceList<AccelerateProjectGroupDTO> ProxyDomains { get; }
public SourceCache<AccelerateProjectGroupDTO, string> ProxyDomains { get; }
private readonly ReadOnlyObservableCollection<AccelerateProjectGroupDTO> _ProxyDomainsList;
@@ -278,7 +278,7 @@ public sealed partial class ProxyService
}
}
ProxyDomains.AddRange(result.Content!);
ProxyDomains.AddOrUpdate(result.Content!);
}
LoadOrSaveLocalAccelerate();
@@ -321,7 +321,7 @@ public sealed partial class ProxyService
Log.Error(nameof(ProxyService), ex, nameof(LoadOrSaveLocalAccelerate));
}
if (accelerates.Any_Nullable())
ProxyDomains.AddRange(accelerates!);
ProxyDomains.AddOrUpdate(accelerates!);
}
}
}
@@ -571,8 +571,8 @@ public sealed partial class ProxyService
#if WINDOWS
{
platformService.SetAsSystemProxy(false);
platformService.SetAsSystemPACProxy(false);
await platformService.SetAsSystemProxyAsync(false);
await platformService.SetAsSystemPACProxyAsync(false);
await reverseProxyService.StopProxyAsync();
try
{
@@ -76,6 +76,11 @@ public partial class ScriptPageViewModel : TabItemViewModel
public async void OpenScriptStoreWindow()
{
if (UserService.Current.IsAuthenticated == false)
{
Toast.Show(ToastIcon.Error, "脚本商店需要登录账号才可访问");
return;
}
var model = new ScriptStorePageViewModel();
await IWindowManager.Instance.ShowTaskDialogAsync(model,
Strings.ScriptStore,
@@ -17,6 +17,7 @@
x:DataType="spp:AcceleratorPageViewModel"
Description="{Binding Description, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
OpenPaneLength="335"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Subtitle="{Binding Author, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource StringFormatConverter}, ConverterParameter=Plugin_Author}"
mc:Ignorable="d">
<spp:PageBase.PreviewImage>
@@ -163,8 +164,8 @@
</DataTemplate>
</ui:MenuFlyoutSubItem.ItemTemplate>
</ui:MenuFlyoutSubItem>-->
<ui:MenuFlyoutItem Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutSeparator />
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
<!--<ui:MenuFlyoutSeparator />-->
<!--<ui:MenuFlyoutItem Text="安装证书" />
<ui:MenuFlyoutItem Text="移除证书" />
<ui:MenuFlyoutItem Text="打开证书文件夹" />
@@ -187,9 +188,9 @@
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
@@ -202,6 +203,7 @@
<TabStrip
Background="{DynamicResource ControlOnImageFillColorDefaultBrush}"
CornerRadius="{StaticResource ControlCornerRadius}"
IsEnabled="{Binding Source={x:Static s:ProxyService.Current}, Path=!ProxyStatus, Mode=OneWay}"
ItemContainerTheme="{StaticResource TabItemStyle}"
ItemsSource="{Binding Source={x:Static ms:ProxySettings.ProxyModes}, Path=., Mode=OneWay}"
SelectedItem="{Binding Source={x:Static ms:ProxySettings.ProxyMode}, Path=Value, Mode=TwoWay}"
@@ -232,11 +234,11 @@
ToolTip.Tip="{Binding Source={x:Static s:ProxyService.Current}, Path=IPv6AddresString, Mode=OneWay}">
<TextBlock.Styles>
<Style Selector="TextBlock.Support">
<Setter Property="Text" Value="已支持 Ipv6 访问" />
<Setter Property="Text" Value="已支持 IPv6 访问" />
<Setter Property="Foreground" Value="{DynamicResource SystemFillColorSuccessBrush}" />
</Style>
<Style Selector="TextBlock.NotSupport">
<Setter Property="Text" Value="不支持 Ipv6 访问" />
<Setter Property="Text" Value="不支持 IPv6 访问" />
<Setter Property="Foreground" Value="{DynamicResource SystemFillColorCriticalBrush}" />
</Style>
</TextBlock.Styles>
@@ -321,80 +323,84 @@
</StackPanel>
</spp:PageBase.PaneContent>
<Grid ColumnDefinitions="*,Auto">
<Carousel
Name="AcceleratorCarousel"
Classes="AnimeSlide"
SelectedIndex="0">
<i:Interaction.Behaviors>
<ia:DataTriggerBehavior
Binding="{Binding Source={x:Static s:ProxyService.Current}, Path=!ProxyStatus, Mode=OneWay}"
ComparisonCondition="Equal"
Value="True">
<ia:ChangePropertyAction
PropertyName="SelectedIndex"
TargetObject="{Binding #AcceleratorCarousel}"
Value="0" />
</ia:DataTriggerBehavior>
<ia:DataTriggerBehavior
Binding="{Binding Source={x:Static s:ProxyService.Current}, Path=!ProxyStatus, Mode=OneWay}"
ComparisonCondition="Equal"
Value="False">
<ia:ChangePropertyAction
PropertyName="SelectedIndex"
TargetObject="{Binding #AcceleratorCarousel}"
Value="1" />
</ia:DataTriggerBehavior>
</i:Interaction.Behaviors>
<ScrollViewer
Padding="0,0,10,0"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Carousel
Name="AcceleratorCarousel"
Classes="AnimeSlide"
SelectedIndex="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyStatus, Mode=OneWay}">
<!--<i:Interaction.Behaviors>
<ia:DataTriggerBehavior
Binding="{Binding Source={x:Static s:ProxyService.Current}, Path=!ProxyStatus, Mode=OneWay}"
ComparisonCondition="Equal"
Value="True">
<ia:ChangePropertyAction
PropertyName="SelectedIndex"
TargetObject="{Binding #AcceleratorCarousel}"
Value="0" />
</ia:DataTriggerBehavior>
<ia:DataTriggerBehavior
Binding="{Binding Source={x:Static s:ProxyService.Current}, Path=!ProxyStatus, Mode=OneWay}"
ComparisonCondition="Equal"
Value="False">
<ia:ChangePropertyAction
PropertyName="SelectedIndex"
TargetObject="{Binding #AcceleratorCarousel}"
Value="1" />
</ia:DataTriggerBehavior>
</i:Interaction.Behaviors>-->
<spp:ContentLoader IsLoading="{Binding Source={x:Static s:ProxyService.Current}, Path=!!!ProxyDomainsList.Count, Mode=OneWay}" NoResultMessage="{Binding Path=Res.CommunityFix_ItemsEmpty, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<Grid ColumnDefinitions="*,Auto">
<ItemsControl ItemsSource="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyDomainsList, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Spacing="{StaticResource SettingsSpacing}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<spp:ContentLoader IsLoading="{Binding Source={x:Static s:ProxyService.Current}, Path=!!!ProxyDomainsList.Count, Mode=OneWay}" NoResultMessage="{Binding Path=Res.CommunityFix_ItemsEmpty, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<Grid ColumnDefinitions="*,Auto">
<ItemsControl ItemsSource="{Binding Source={x:Static s:ProxyService.Current}, Path=ProxyDomainsList, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Spacing="{StaticResource SettingsSpacing}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="spp:AccelerateProjectGroupDTO">
<Border>
<!--<ui:SettingsExpander ItemsSource="{Binding Items}">-->
<ui:SettingsExpander IsClickEnabled="False">
<ui:SettingsExpander.Header>
<StackPanel Orientation="Horizontal" Spacing="10">
<CheckBox IsChecked="{Binding ThreeStateEnable, Mode=TwoWay}" />
<spp:Image2
Width="30"
Height="30"
VerticalAlignment="Center"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="{Binding IconUrl}"
Stretch="UniformToFill" />
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" />
</StackPanel>
</ui:SettingsExpander.Header>
<ui:SettingsExpanderItem Theme="{StaticResource SettingsExpanderItem2}">
<TreeView ItemsSource="{Binding ObservableItems}" SelectionMode="Single">
<TreeView.ItemTemplate>
<TreeDataTemplate DataType="spp:AccelerateProjectDTO" ItemsSource="{Binding ObservableItems}">
<DockPanel Margin="8,0">
<CheckBox
HorizontalAlignment="Left"
DockPanel.Dock="Left"
IsChecked="{Binding ThreeStateEnable, Mode=TwoWay}">
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" />
</CheckBox>
<TextBlock
HorizontalAlignment="Right"
VerticalAlignment="Center"
DockPanel.Dock="Right"
Text="{Binding DomainNamesArray[0]}" />
</DockPanel>
</TreeDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</ui:SettingsExpanderItem>
<!--<ui:SettingsExpander.ItemTemplate>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="spp:AccelerateProjectGroupDTO">
<Border>
<!--<ui:SettingsExpander ItemsSource="{Binding Items}">-->
<ui:SettingsExpander IsClickEnabled="False">
<ui:SettingsExpander.Header>
<StackPanel Orientation="Horizontal" Spacing="10">
<CheckBox IsChecked="{Binding ThreeStateEnable, Mode=TwoWay}" />
<spp:Image2
Width="30"
Height="30"
VerticalAlignment="Center"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="{Binding IconUrl}"
Stretch="UniformToFill" />
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" />
</StackPanel>
</ui:SettingsExpander.Header>
<ui:SettingsExpanderItem Theme="{StaticResource SettingsExpanderItem2}">
<TreeView ItemsSource="{Binding ObservableItems}" SelectionMode="Single">
<TreeView.ItemTemplate>
<TreeDataTemplate DataType="spp:AccelerateProjectDTO" ItemsSource="{Binding ObservableItems}">
<DockPanel Margin="8,0">
<CheckBox
HorizontalAlignment="Left"
DockPanel.Dock="Left"
IsChecked="{Binding ThreeStateEnable, Mode=TwoWay}">
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" />
</CheckBox>
<TextBlock
HorizontalAlignment="Right"
VerticalAlignment="Center"
DockPanel.Dock="Right"
Text="{Binding DomainNamesArray[0]}" />
</DockPanel>
</TreeDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</ui:SettingsExpanderItem>
<!--<ui:SettingsExpander.ItemTemplate>
<DataTemplate DataType="spp:AccelerateProjectDTO">
<ui:SettingsExpanderItem IsClickEnabled="True">
<ui:SettingsExpanderItem.Content>
@@ -409,35 +415,35 @@
</ui:SettingsExpanderItem>
</DataTemplate>
</ui:SettingsExpander.ItemTemplate>-->
</ui:SettingsExpander>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ui:SettingsExpander>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</spp:ContentLoader>
</ItemsControl>
</Grid>
</spp:ContentLoader>
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*,Auto">
<TabStrip
x:Name="Tabs"
ItemContainerTheme="{StaticResource TabItemStyle}"
Theme="{StaticResource TabStripStyle}">
<TabStripItem Content="{Binding Path=Res.CommunityFix_FlowStatistics, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TabStripItem Content="{Binding Path=Res.CommunityFix_Services, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</TabStrip>
<StackPanel
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Spacing="8">
<StackPanel.Styles>
<Style Selector="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</StackPanel.Styles>
<!--<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*,Auto">
<TabStrip
x:Name="Tabs"
ItemContainerTheme="{StaticResource TabItemStyle}"
Theme="{StaticResource TabStripStyle}">
<TabStripItem Content="{Binding Path=Res.CommunityFix_FlowStatistics, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TabStripItem Content="{Binding Path=Res.CommunityFix_Services, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</TabStrip>
<StackPanel
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Spacing="8">
<StackPanel.Styles>
<Style Selector="TextBlock">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</StackPanel.Styles>
<!--<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<TextBlock FontSize="26" Text="{Binding Path=Res.CommunityFix_AlreadyProxy, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock
Margin="5,0,0,0"
@@ -446,97 +452,97 @@
Source={x:Static s:ProxyService.Current},
StringFormat={}{0:hh\\:mm\\:ss}}" />
</StackPanel>-->
<TextBlock>
<Run Text="{Binding Path=Res.CommunityFix_ProxyMode, StringFormat={}{0}:, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Run Text="{Binding Source={x:Static ms:ProxySettings.ProxyMode}, Path=Value, Mode=OneWay, Converter={StaticResource EnumLocalizationNameConverter}}" />
<TextBlock>
<Run Text="{Binding Path=Res.CommunityFix_ProxyMode, StringFormat={}{0}:, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Run Text="{Binding Source={x:Static ms:ProxySettings.ProxyMode}, Path=Value, Mode=OneWay, Converter={StaticResource EnumLocalizationNameConverter}}" />
<Run Text=" " />
<Run Text=" " />
<Run Text="{Binding Path=Res.CommunityFix_EnableScriptService, StringFormat={}{0}:, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Run Text="{Binding Source={x:Static s:ProxyService.Current}, Path=IsEnableScript, Converter={StaticResource StringFormatConverter}}" />
</TextBlock>
</StackPanel>
<Run Text="{Binding Path=Res.CommunityFix_EnableScriptService, StringFormat={}{0}:, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Run Text="{Binding Source={x:Static s:ProxyService.Current}, Path=IsEnableScript, Converter={StaticResource StringFormatConverter}}" />
</TextBlock>
</StackPanel>
<Carousel
Grid.Row="1"
Grid.ColumnSpan="2"
Classes="AnimeSlide"
SelectedIndex="{Binding #Tabs.SelectedIndex}">
<Carousel
Grid.Row="1"
Grid.ColumnSpan="2"
Classes="AnimeSlide"
SelectedIndex="{Binding #Tabs.SelectedIndex}">
<spp:ProxyChartView Margin="5,0" />
<spp:ProxyChartView Margin="5,0" />
<Grid ColumnDefinitions="*,20,*">
<Border
Grid.Column="0"
Padding="10"
Classes="paper">
<DockPanel>
<TextBlock
Margin="0,0,0,10"
DockPanel.Dock="Top"
FontSize="16"
Text="{Binding Path=Res.CommunityFix_AccelerationsEnable, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding EnableProxyDomains, Source={x:Static s:ProxyService.Current}}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type spp:AccelerateProjectDTO}">
<StackPanel Margin="10">
<DockPanel>
<TextBlock Text="{Binding Name}" />
</DockPanel>
<Separator Classes="Separator" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Border>
<Grid ColumnDefinitions="*,20,*">
<Border
Grid.Column="0"
Padding="10"
Classes="paper">
<DockPanel>
<TextBlock
Margin="0,0,0,10"
DockPanel.Dock="Top"
FontSize="16"
Text="{Binding Path=Res.CommunityFix_AccelerationsEnable, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding EnableProxyDomains, Source={x:Static s:ProxyService.Current}}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type spp:AccelerateProjectDTO}">
<StackPanel Margin="10">
<DockPanel>
<TextBlock Text="{Binding Name}" />
</DockPanel>
<Separator Classes="Separator" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Border>
<Border
Grid.Column="2"
Padding="10"
Classes="paper">
<DockPanel>
<TextBlock
Margin="0,0,0,10"
DockPanel.Dock="Top"
FontSize="16"
Text="{Binding Path=Res.CommunityFix_ScriptsEnable, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding EnableProxyScripts^, Source={x:Static s:ProxyService.Current}}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type spp:ScriptDTO}">
<StackPanel Margin="10">
<DockPanel>
<TextBlock Text="{Binding Name}" />
</DockPanel>
<Separator Classes="Separator" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Border>
</Grid>
<Border
Grid.Column="2"
Padding="10"
Classes="paper">
<DockPanel>
<TextBlock
Margin="0,0,0,10"
DockPanel.Dock="Top"
FontSize="16"
Text="{Binding Path=Res.CommunityFix_ScriptsEnable, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ScrollViewer>
<ItemsControl ItemsSource="{Binding EnableProxyScripts^, Source={x:Static s:ProxyService.Current}}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type spp:ScriptDTO}">
<StackPanel Margin="10">
<DockPanel>
<TextBlock Text="{Binding Name}" />
</DockPanel>
<Separator Classes="Separator" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Border>
</Grid>
<!--<TabItem Header="加速日志">
<!--<TabItem Header="加速日志">
</TabItem>-->
<!--<TabItem Header="AD">
<!--<TabItem Header="AD">
</TabItem>-->
</Carousel>
</Grid>
</Carousel>
</Carousel>
</Grid>
</Carousel>
</ScrollViewer>
<spp:AdControl
Grid.Column="1"
Width="165"
MaxHeight="390"
Margin="10,0,0,0"
VerticalAlignment="Top"
DockPanel.Dock="Right"
Standard="Vertical" />
@@ -96,10 +96,10 @@
Theme="{StaticResource TransparentButton}">
<DropDownButton.Flyout>
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
<ui:MenuFlyoutItem Text="{Binding Path=Res.Script_Settings, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem Text="{Binding Path=Res.Script_AutoUpdate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.CommunityFix_OnlySteamBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.Script_Settings, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
<!--<ui:MenuFlyoutSeparator />-->
<ui:ToggleMenuFlyoutItem IsChecked="{Binding Source={x:Static ms:ProxySettings.IsAutoCheckScriptUpdate}, Path=Value, Mode=TwoWay}" Text="{Binding Path=Res.Script_AutoUpdate, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:ToggleMenuFlyoutItem IsChecked="{Binding Source={x:Static ms:ProxySettings.IsOnlyWorkSteamBrowser}, Path=Value, Mode=TwoWay}" Text="{Binding Path=Res.CommunityFix_OnlySteamBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem IsEnabled="False">
<ui:MenuFlyoutItem.Text>
@@ -115,9 +115,9 @@
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
@@ -205,11 +205,11 @@
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem
<ui:MenuFlyoutItem
Command="{Binding $parent[spp:PageBase].((spp:ScriptPageViewModel)DataContext).DeleteScriptItemCommand}"
CommandParameter="{Binding}"
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
Text="{Binding Path=Res.Delete, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
Foreground="{DynamicResource SystemFillColorCriticalBrush}"
Text="{Binding Path=Res.Delete, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</ui:FAMenuFlyout>
</spp:AppItem.MoreFlyout>
<spp:AppItem.Image>
@@ -216,7 +216,6 @@ internal sealed class AccountPlatformAuthenticatorRepository : Repository<Accoun
public async Task<(bool isSuccess, bool isUpdate)> InsertOrUpdateAsync(IAuthenticatorDTO item, bool isLocal,
string? secondaryPassword = null)
{
var isUpdate = item.Id == default;
var entity = await ConvertAsync(item, isLocal, secondaryPassword);
@@ -224,7 +223,7 @@ internal sealed class AccountPlatformAuthenticatorRepository : Repository<Accoun
item.Id = entity.Id;
return (r.rowCount > 0, isUpdate);
return (r.rowCount > 0, r.result == DbRowExecResult.Update);
}
public async Task DeleteAsync(ushort id) => await base.DeleteAsync(id);
@@ -55,8 +55,6 @@ public sealed partial class AuthenticatorHomePageViewModel : ViewModelBase
var sourceList = await AuthenticatorHelper.GetAllSourceAuthenticatorAsync();
if (sourceList.Any_Nullable())
{
AuthenticatorIsEmpty = false;
(HasLocalPcEncrypt, HasPasswordEncrypt) = AuthenticatorHelper.HasEncrypt(sourceList);
if (HasPasswordEncrypt && IsVerificationPass == false)
@@ -439,6 +437,12 @@ public sealed partial class AuthenticatorHomePageViewModel : ViewModelBase
public async Task OpenExportWindow()
{
if (!Auths.Any_Nullable())
{
Toast.Show(ToastIcon.Warning, AppResources.Warning_RefuseOperate);
return;
}
await IWindowManager.Instance.ShowTaskDialogAsync(new AuthenticatorExportViewModel(), AppResources.ExportAuth,
pageContent: new AuthenticatorExportPage(), isOkButton: false);
}
@@ -538,6 +542,8 @@ public sealed partial class AuthenticatorHomePageViewModel : ViewModelBase
{
if (sender is not AuthenticatorItemModel authenticatorItemModel) return;
var index = Auths.IndexOf(authenticatorItemModel);
if (index == 0)
return;
if (authenticatorItemModel.AuthData.ServerId != null)
{
if (string.IsNullOrEmpty(_currentAnswer))
@@ -557,6 +563,8 @@ public sealed partial class AuthenticatorHomePageViewModel : ViewModelBase
{
if (sender is not AuthenticatorItemModel authenticatorItemModel) return;
var index = Auths.IndexOf(authenticatorItemModel);
if (index == Auths.Count)
return;
if (authenticatorItemModel.AuthData.ServerId != null)
{
if (string.IsNullOrEmpty(_currentAnswer))
@@ -21,7 +21,4 @@ public partial class AuthenticatorHomePageViewModel
[Reactive]
public bool IsVerificationPass { get; set; }
[Reactive]
public bool AuthenticatorIsEmpty { get; set; } = true;
}
@@ -175,20 +175,20 @@ public class AuthenticatorImportPageViewModel : ViewModelBase
if (!string.IsNullOrEmpty(authString))
{
var reader = XmlReader.Create(new StringReader(authString));
await reader.ReadAsync();
reader.Read();
while (reader is { EOF: false, IsEmptyElement: true })
{
await reader.ReadAsync();
}
await reader.MoveToContentAsync();
reader.MoveToContent();
while (reader.EOF == false)
{
if (reader.IsStartElement())
{
if (reader.Name == "Auth")
{
await reader.ReadAsync();
reader.Read();
}
if (reader.Name != "WinAuthAuthenticator") continue;
@@ -198,7 +198,7 @@ public class AuthenticatorImportPageViewModel : ViewModelBase
}
else
{
await reader.ReadAsync();
reader.Read();
break;
}
}
@@ -147,7 +147,7 @@ public sealed partial class SteamTradePageViewModel : WindowViewModel
else
{
Toast.Show(ToastIcon.Error, AppResources.Error_UnknownLogin_.Format(loginstate.Message));
await Logout();
_steamClient.Logout();
}
IsLoading = false;
@@ -167,6 +167,12 @@ public sealed partial class SteamTradePageViewModel : WindowViewModel
Confirmations.Add(result.Select(item => new SteamTradeConfirmationModel(_steamAuthenticator!, item)));
}
}
catch (WinAuthUnauthorisedSteamRequestException)
{
Toast.Show(ToastIcon.Error, AppResources.LocalAuth_AuthTrade_GetError3);
_steamClient?.Logout();
IsLogged = false;
}
catch (Exception ex)
{
ex.LogAndShowT();
@@ -181,10 +187,11 @@ public sealed partial class SteamTradePageViewModel : WindowViewModel
{
if (_steamClient == null) return;
if (await IWindowManager.Instance.ShowTaskDialogAsync(
new MessageBoxWindowViewModel() { Content = Strings.LocalAuth_LogoutTip, }, isCancelButton: true,
new MessageBoxWindowViewModel() { Content = Strings.LocalAuth_LogoutTip, }, AppResources.Logout, isCancelButton: true,
isDialog: false))
{
_steamClient.Logout();
IsLogged = false;
}
}
@@ -208,7 +208,7 @@
<StackPanel.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<CompiledBinding Path="!IsVerificationPass" />
<CompiledBinding Path="!AuthenticatorIsEmpty" />
<CompiledBinding Path="!!Auths.Count" />
</MultiBinding>
</StackPanel.IsVisible>
<TextBlock
@@ -224,11 +224,11 @@
IsEnabled="True" />
</StackPanel>
<spp:ContentLoader IsLoading="{Binding IsLoading}" IsShowNoResultText="{Binding AuthenticatorIsEmpty}">
<spp:ContentLoader IsLoading="{Binding IsLoading}" IsShowNoResultText="{Binding !!!Auths.Count}">
<spp:ContentLoader.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<CompiledBinding Path="IsVerificationPass" />
<CompiledBinding Path="!AuthenticatorIsEmpty" />
<CompiledBinding Path="!!Auths.Count" />
</MultiBinding>
</spp:ContentLoader.IsVisible>
<spp:ContentLoader.NoResultMessage>
@@ -250,7 +250,7 @@
ItemsJustification="Start"
ItemsStretch="Uniform"
MinColumnSpacing="10"
MinItemHeight="150"
MinItemWidth="362"
MinRowSpacing="10" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
@@ -66,6 +66,11 @@
DockPanel.Dock="Right"
IsSticky="False">
<ui:CommandBar.PrimaryCommands>
<ui:CommandBarButton Command="{Binding Logout}" Label="{Binding Path=Res.Logout, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:CommandBarButton.IconSource>
<ui:FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xEDE1;" />
</ui:CommandBarButton.IconSource>
</ui:CommandBarButton>
<ui:CommandBarButton
Command="{Binding Refresh}"
IconSource="Refresh"
@@ -91,111 +91,113 @@ public sealed class SteamPlatformSwitcher : IPlatformSwitcher
public async Task<IEnumerable<IAccount>?> GetUsers(PlatformAccount platform, Action? refreshUsers = null)
{
var users = steamService.GetRememberUserList();
//var users = steamService.GetRememberUserList();
await SteamConnectService.Current.RefreshSteamUsers();
var users = SteamConnectService.Current.SteamUsers.Items;
if (!users.Any_Nullable())
{
return null;
}
#region 加载备注信息和 JumpList
// #region 加载备注信息和 JumpList
var accountRemarks = Ioc.Get<IPartialGameAccountSettings>()?.AccountRemarks;
// var accountRemarks = Ioc.Get<IPartialGameAccountSettings>()?.AccountRemarks;
#if WINDOWS
List<(string title, string applicationPath, string iconResourcePath, string arguments, string description, string customCategory)>? jumplistData = new();
#endif
foreach (var user in users)
{
if (accountRemarks?.TryGetValue("Steam-" + user.SteamId64, out var remark) == true &&
!string.IsNullOrEmpty(remark))
user.Remark = remark;
//#if WINDOWS
// List<(string title, string applicationPath, string iconResourcePath, string arguments, string description, string customCategory)>? jumplistData = new();
//#endif
// foreach (var user in users)
// {
// if (accountRemarks?.TryGetValue("Steam-" + user.SteamId64, out var remark) == true &&
// !string.IsNullOrEmpty(remark))
// user.Remark = remark;
#if WINDOWS
{
var title = user.SteamNickName ?? user.SteamId64.ToString(CultureInfo.InvariantCulture);
if (!string.IsNullOrEmpty(user.Remark))
{
title = user.SteamNickName + "(" + user.Remark + ")";
}
//#if WINDOWS
// {
// var title = user.SteamNickName ?? user.SteamId64.ToString(CultureInfo.InvariantCulture);
// if (!string.IsNullOrEmpty(user.Remark))
// {
// title = user.SteamNickName + "(" + user.Remark + ")";
// }
var processPath = Environment.ProcessPath;
processPath.ThrowIsNull();
if (!string.IsNullOrEmpty(user.AccountName)) jumplistData!.Add((title,
applicationPath: processPath,
iconResourcePath: processPath,
arguments: $"-clt steam -account {user.AccountName}",
description: Strings.UserChange_BtnTootlip,
customCategory: Strings.UserFastChange));
}
#endif
}
// var processPath = Environment.ProcessPath;
// processPath.ThrowIsNull();
// if (!string.IsNullOrEmpty(user.AccountName)) jumplistData!.Add((title,
// applicationPath: processPath,
// iconResourcePath: processPath,
// arguments: $"-clt steam -account {user.AccountName}",
// description: Strings.UserChange_BtnTootlip,
// customCategory: Strings.UserFastChange));
// }
//#endif
// }
#if WINDOWS
if (jumplistData.Any())
{
MainThread2.BeginInvokeOnMainThread(async () =>
{
var s = IJumpListService.Instance;
await s.AddJumpItemsAsync(jumplistData);
});
}
#endif
#endregion
//#if WINDOWS
// if (jumplistData.Any())
// {
// MainThread2.BeginInvokeOnMainThread(async () =>
// {
// var s = IJumpListService.Instance;
// await s.AddJumpItemsAsync(jumplistData);
// });
// }
//#endif
// #endregion
#region 通过webapi加载头像图片用户信息
Task2.InBackground(async () =>
{
foreach (var user in users)
{
var temp = await swWebService.GetUserInfo(user.SteamId64);
if (!string.IsNullOrEmpty(temp.SteamID))
{
user.SteamID = temp.SteamID;
user.OnlineState = temp.OnlineState;
user.MemberSince = temp.MemberSince;
user.VacBanned = temp.VacBanned;
user.Summary = temp.Summary;
user.PrivacyState = temp.PrivacyState;
user.AvatarIcon = temp.AvatarIcon;
user.AvatarMedium = temp.AvatarMedium;
user.AvatarFull = temp.AvatarFull;
user.MiniProfile = temp.MiniProfile;
user.Level = user.MiniProfile?.Level;
// #region 通过webapi加载头像图片用户信息
// Task2.InBackground(async () =>
// {
// foreach (var user in users)
// {
// var temp = await swWebService.GetUserInfo(user.SteamId64);
// if (!string.IsNullOrEmpty(temp.SteamID))
// {
// user.SteamID = temp.SteamID;
// user.OnlineState = temp.OnlineState;
// user.MemberSince = temp.MemberSince;
// user.VacBanned = temp.VacBanned;
// user.Summary = temp.Summary;
// user.PrivacyState = temp.PrivacyState;
// user.AvatarIcon = temp.AvatarIcon;
// user.AvatarMedium = temp.AvatarMedium;
// user.AvatarFull = temp.AvatarFull;
// user.MiniProfile = temp.MiniProfile;
// user.Level = user.MiniProfile?.Level;
//if (user.MiniProfile != null && !string.IsNullOrEmpty(user.MiniProfile.AnimatedAvatar))
//{
// user.AvatarStream = imageHttpClientService.GetImageMemoryStreamAsync(user.MiniProfile.AnimatedAvatar, cache: true);
//}
//else
//{
// user.AvatarStream = imageHttpClientService.GetImageMemoryStreamAsync(temp.AvatarFull, cache: true);
//}
}
}
// //if (user.MiniProfile != null && !string.IsNullOrEmpty(user.MiniProfile.AnimatedAvatar))
// //{
// // user.AvatarStream = imageHttpClientService.GetImageMemoryStreamAsync(user.MiniProfile.AnimatedAvatar, cache: true);
// //}
// //else
// //{
// // user.AvatarStream = imageHttpClientService.GetImageMemoryStreamAsync(temp.AvatarFull, cache: true);
// //}
// }
// }
refreshUsers?.Invoke();
});
#endregion
// refreshUsers?.Invoke();
// });
// #endregion
#region 加载动态头像头像框数据
//foreach (var user in users)
//{
// if (user.MiniProfile == null)
// {
// user.MiniProfile = await swWebService.GetUserMiniProfile(user.SteamId32);
// }
// if (user.MiniProfile != null)
// {
// if (!string.IsNullOrEmpty(user.MiniProfile.AnimatedAvatar))
// user.AvatarStream = imageHttpClientService.GetImageMemoryStreamAsync(user.MiniProfile.AnimatedAvatar, cache: true);
// #region 加载动态头像头像框数据
// //foreach (var user in users)
// //{
// // if (user.MiniProfile == null)
// // {
// // user.MiniProfile = await swWebService.GetUserMiniProfile(user.SteamId32);
// // }
// // if (user.MiniProfile != null)
// // {
// // if (!string.IsNullOrEmpty(user.MiniProfile.AnimatedAvatar))
// // user.AvatarStream = imageHttpClientService.GetImageMemoryStreamAsync(user.MiniProfile.AnimatedAvatar, cache: true);
// if (!string.IsNullOrEmpty(user.MiniProfile.AvatarFrame))
// user.MiniProfile.AvatarFrameStream = imageHttpClientService.GetImageMemoryStreamAsync(user.MiniProfile.AvatarFrame, cache: true);
// // if (!string.IsNullOrEmpty(user.MiniProfile.AvatarFrame))
// // user.MiniProfile.AvatarFrameStream = imageHttpClientService.GetImageMemoryStreamAsync(user.MiniProfile.AvatarFrame, cache: true);
// user.Level = user.MiniProfile.Level;
// }
//}
#endregion
// // user.Level = user.MiniProfile.Level;
// // }
// //}
// #endregion
return users.Select(s => new SteamAccount(s));
}
@@ -140,9 +140,9 @@
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

@@ -13,8 +13,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
public ReadOnlyObservableCollection<SteamGridItem>? SteamGridItems => _SteamGridItems;
[Reactive]
public SteamApp App { get; set; }
public SteamApp App { get; }
[Reactive]
public SteamGridItem? SelectGrid { get; set; }
@@ -36,7 +35,7 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
public ICommand OpenSteamGridDBAuthorUrlCommand { get; }
public EditAppInfoPageViewModel(SteamApp app)
public EditAppInfoPageViewModel(ref SteamApp app)
{
App = app;
@@ -121,8 +120,9 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
public async void SaveEditAppInfo()
{
#region 自定义图片保存
var mostRecentUser = ISteamService.Instance.GetRememberUserList().Where(s => s.MostRecent).FirstOrDefault();
if (!(App.EditHeaderLogoStream is FileStream fs3))
//var mostRecentUser = ISteamService.Instance.GetRememberUserList().Where(s => s.MostRecent).FirstOrDefault();
var mostRecentUser = SteamConnectService.Current.SteamUsers.Items.Where(s => s.MostRecent).FirstOrDefault();
if (!(App.EditHeaderLogoStream is FileStream fs3 && fs3.Name == (await App.HeaderLogoStream)?.Name))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
@@ -130,10 +130,12 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
if (await ISteamService.Instance.SaveAppImageToSteamFile(App.EditHeaderLogoStream,
mostRecentUser!, App.AppId, SteamGridItemType.Header) == false)
{
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Logo)));
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Header)));
}
else
this.RaisePropertyChanged(nameof(App.HeaderLogoStream));
}
if (!(App.EditLibraryLogoStream is FileStream fs))
if (!(App.EditLibraryLogoStream is FileStream fs && fs.Name == (await App.LibraryLogoStream)?.Name))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
@@ -143,8 +145,10 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
{
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Logo)));
}
else
this.RaisePropertyChanged(nameof(App.LibraryLogoStream));
}
if (!(App.EditLibraryHeroStream is FileStream fs1))
if (!(App.EditLibraryHeroStream is FileStream fs1 && fs1.Name == (await App.LibraryHeroStream)?.Name))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
@@ -154,8 +158,10 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
{
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Hero)));
}
else
this.RaisePropertyChanged(nameof(App.LibraryHeroStream));
}
if (!(App.EditLibraryGridStream is FileStream fs2))
if (!(App.EditLibraryGridStream is FileStream fs2 && fs2.Name == (await App.LibraryGridStream)?.Name))
{
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
@@ -165,6 +171,8 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
{
Toast.Show(ToastIcon.Error, string.Format(Strings.SaveImageFileFailed, nameof(SteamGridItemType.Grid)));
}
else
this.RaisePropertyChanged(nameof(App.LibraryGridStream));
}
#endregion
@@ -228,6 +236,11 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
if (SelectGrid == null)
{
//Toast.Show(ToastIcon.Warning, Strings.SaveEditedAppInfo_SelectImageFailed);
var mostRecentUser = SteamConnectService.Current.SteamUsers.Items.Where(s => s.MostRecent).FirstOrDefault();
if (!CheckCurrentSteamUserStats(mostRecentUser))
return;
await ISteamService.Instance.SaveAppImageToSteamFile(null, mostRecentUser!, App.AppId, type);
switch (type)
{
case SteamGridItemType.Header:
@@ -266,14 +279,6 @@ public sealed class EditAppInfoPageViewModel : WindowViewModel
public override void OnClosing(object? sender, CancelEventArgs e)
{
if (App != null && !App.IsEdited)
{
App.EditLibraryGridStream = null;
App.EditLibraryHeroStream = null;
App.EditLibraryLogoStream = null;
App.EditHeaderLogoStream = null;
}
_SteamGridItemSourceList.Dispose();
}
@@ -16,6 +16,8 @@ public sealed class EditAppsPageViewModel : ViewModelBase
public bool IsSteamEditedAppsEmpty => !SteamEditedApps.Any_Nullable();
public ICommand EditAppInfoClickCommand { get; }
public EditAppsPageViewModel()
{
SteamConnectService.Current.SteamApps
@@ -26,6 +28,8 @@ public sealed class EditAppsPageViewModel : ViewModelBase
.Bind(out _SteamEditedApps)
.Subscribe(_ => this.RaisePropertyChanged(nameof(IsSteamEditedAppsEmpty)));
EditAppInfoClickCommand = ReactiveCommand.Create<SteamApp>(GameListPageViewModel.EditAppInfoClick);
LoadSteamEditedApps();
}
@@ -194,9 +194,9 @@ public sealed partial class GameListPageViewModel : TabItemViewModel
public static async void EditAppInfoClick(SteamApp app)
{
if (app == null) return;
var vm = new EditAppInfoPageViewModel(app);
var vm = new EditAppInfoPageViewModel(ref app);
var result = await IWindowManager.Instance.ShowTaskDialogAsync(vm, Strings.GameList_EditAppInfo,
pageContent: new EditAppInfoPage(), okButtonText: Strings.Save, isCancelButton: true);
pageContent: new EditAppInfoPage(), okButtonText: Strings.Save, isCancelButton: true, disableScroll: true);
if (result)
{
@@ -6,9 +6,6 @@ public sealed partial class GameListPageViewModel : TabItemViewModel
{
public override string Name => Strings.GameList;
[Reactive]
public bool IsOpenFilter { get; set; }
[Reactive]
public bool IsInstalledFilter { get; set; }
@@ -210,6 +210,8 @@ public sealed class IdleAppsPageViewModel : ViewModelBase
{
var list = new ObservableCollection<SteamApp>();
if (GameLibrarySettings.AFKAppList.Any_Nullable())
{
int runOtherAppCount = 0;
foreach (var item in GameLibrarySettings.AFKAppList.Value!)
{
var appInfo = SteamConnectService.Current.SteamApps.Items.FirstOrDefault(x => x.AppId == item.Key);
@@ -239,6 +241,7 @@ public sealed class IdleAppsPageViewModel : ViewModelBase
}
else
{
runOtherAppCount++;
SteamConnectService.Current.RuningSteamApps.TryGetValue(item.Key, out var runState);
runState?.Process?.KillEntireProcessTree();
list.Add(new SteamApp
@@ -249,6 +252,11 @@ public sealed class IdleAppsPageViewModel : ViewModelBase
}
}
if (runOtherAppCount > 0)
{
Toast.Show(ToastIcon.Warning, Strings.GameList_RunOtherAppCount_.Format(runOtherAppCount));
}
}
IdleGameList = list;
ChangeRunTxt(true);
}
@@ -32,7 +32,7 @@
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}" />-->
<ToggleSplitButton IsChecked="{Binding Source={x:Static s:SteamConnectService.Current}, Path=IsWatchSteamDownloading, Mode=TwoWay}">
<ToggleSplitButton IsChecked="{Binding Source={x:Static s:SteamConnectService.Current}, Path=IsWatchSteamDownloading, Mode=TwoWay}" ToolTip.Tip="启用此按钮才会启动监听 Steam 下载数据并在下载完时执行对应操作!">
<ToggleSplitButton.Content>
<TextBlock>
<TextBlock.Text>
@@ -169,7 +169,7 @@
Width="250"
Height="116"
DecodeWidth="250"
Source="{Binding HeaderLogoUrl}"
Source="{Binding HeaderLogoStream^}"
Stretch="UniformToFill" />
<Border
@@ -15,505 +15,534 @@
mc:Ignorable="d">
<TabControl DockPanel.Dock="Top">
<TabItem Header="{Binding Path=Res.Info, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<StackPanel Classes="InfoBox">
<ScrollViewer>
<StackPanel Classes="InfoBox">
<Border Classes="Item">
<DockPanel>
<TextBlock Text="AppId" />
<TextBlock Classes="right" Text="{Binding App.AppId}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="AppId" />
<TextBlock Classes="right" Text="{Binding App.AppId}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.Name, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding App.Name}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.Name, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding App.Name}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.SortAs, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding App.SortAs}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.SortAs, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding App.SortAs}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.Developer, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding App.Developer}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.Developer, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding App.Developer}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.Publisher, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding App.Publisher}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.Publisher, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding App.Publisher}" />
</DockPanel>
</Border>
<!--<Border Classes="Item">
<!--<Border Classes="Item">
<DockPanel>
<TextBlock Text="发布日期"/>
<TextBox Text="{Binding App.SteamReleaseDate}"/>
</DockPanel>
</Border>-->
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.SupportSystem, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Classes="right" Text="{Binding App.OSList}" />
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<TextBlock Text="{Binding Path=Res.SupportSystem, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Classes="right" Text="{Binding App.OSList}" />
</DockPanel>
</Border>
<Border Classes="Item" IsVisible="{Binding App.IsInstalled}">
<DockPanel>
<TextBlock Text="{Binding Path=Res.DiskUsage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Classes="right" Text="{Binding App.SizeOnDisk, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}" />
</DockPanel>
</Border>
<Border Classes="Item" IsVisible="{Binding App.IsInstalled}">
<DockPanel>
<TextBlock Text="{Binding Path=Res.DiskUsage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Classes="right" Text="{Binding App.SizeOnDisk, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}" />
</DockPanel>
</Border>
<Border Classes="Item" IsVisible="{Binding App.IsInstalled}">
<DockPanel>
<TextBlock Text="{Binding Path=Res.InstalledPath, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:HyperlinkButton
HorizontalAlignment="Right"
VerticalContentAlignment="Stretch"
Command="{Binding OpenFolder}"
CommandParameter="{Binding App.InstalledDir}"
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Null}}">
<TextBlock Text="{Binding App.InstalledDir}" TextWrapping="Wrap" />
</ui:HyperlinkButton>
</DockPanel>
</Border>
<Border Classes="Item" IsVisible="{Binding App.IsInstalled}">
<DockPanel>
<TextBlock Text="{Binding Path=Res.InstalledPath, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:HyperlinkButton
HorizontalAlignment="Right"
VerticalContentAlignment="Stretch"
Command="{Binding OpenFolder}"
CommandParameter="{Binding App.InstalledDir}"
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Null}}">
<TextBlock Text="{Binding App.InstalledDir}" TextWrapping="Wrap" />
</ui:HyperlinkButton>
</DockPanel>
</Border>
<Border Classes="Item" IsVisible="{Binding App.IsInstalled}">
<DockPanel>
<TextBlock Text="{Binding Path=Res.LastUpdatedDateTime, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Classes="right" Text="{Binding App.LastUpdated, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" />
</DockPanel>
</Border>
<Border Classes="Item" IsVisible="{Binding App.IsInstalled}">
<DockPanel>
<TextBlock Text="{Binding Path=Res.LastUpdatedDateTime, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Classes="right" Text="{Binding App.LastUpdated, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" />
</DockPanel>
</Border>
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="{Binding Path=Res.Media, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<Panel>
<spp:ContentLoader
x:Name="SteamGridDBContent"
MinWidth="300"
IsLoading="{Binding IsLoadingSteamGrid}"
IsShowNoResultText="{Binding IsSteamGridEmpty}"
IsVisible="False"
NoResultMessage="{Binding Path=Res.FailedGetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ItemsControl
ItemsSource="{Binding SteamGridItems}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type spp:SteamGridItem}">
<spp:AppItem
Title="{Binding Author.Name}"
MaxWidth="150"
Margin="5"
Classes="Vertical"
Cursor="Hand"
Tapped="SteamGridDBItem_Tapped"
ToolTip.Tip="{Binding Author.Name}">
<!-- Description="{Binding Style}" -->
<spp:AppItem.MoreFlyout>
<MenuFlyout>
<MenuItem
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).OpenSteamGridDBImageUrlCommand}"
CommandParameter="{Binding}"
Header="{Binding Path=Res.OpenImageInBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<MenuItem
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).OpenSteamGridDBAppUrlCommand}"
CommandParameter="{Binding}"
Header="{Binding Path=Res.OpenSteamGridDBInBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<MenuItem
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).OpenSteamGridDBAuthorUrlCommand}"
CommandParameter="{Binding}"
Header="{Binding Path=Res.OpenTheAuthorPageInBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</MenuFlyout>
</spp:AppItem.MoreFlyout>
<spp:AppItem.Image>
<spp:Image2
Width="150"
DecodeWidth="150"
Source="{Binding Thumb}"
Stretch="Uniform" />
</spp:AppItem.Image>
</spp:AppItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<DockPanel>
<Button
Height="30"
Margin="5,5,5,10"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Click="HideGridDialog_Click"
Content="{Binding Path=Res.Back, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
DockPanel.Dock="Top" />
<ScrollViewer DockPanel.Dock="Bottom">
<ItemsControl
ItemsSource="{Binding SteamGridItems}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type spp:SteamGridItem}">
<spp:AppItem
Title="{Binding Author.Name}"
MaxWidth="150"
Margin="5"
Classes="Vertical"
Cursor="Hand"
Tapped="SteamGridDBItem_Tapped"
ToolTip.Tip="{Binding Author.Name}">
<!-- Description="{Binding Style}" -->
<spp:AppItem.MoreFlyout>
<MenuFlyout>
<MenuItem
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).OpenSteamGridDBImageUrlCommand}"
CommandParameter="{Binding}"
Header="{Binding Path=Res.OpenImageInBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<MenuItem
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).OpenSteamGridDBAppUrlCommand}"
CommandParameter="{Binding}"
Header="{Binding Path=Res.OpenSteamGridDBInBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<MenuItem
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).OpenSteamGridDBAuthorUrlCommand}"
CommandParameter="{Binding}"
Header="{Binding Path=Res.OpenTheAuthorPageInBrowser, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</MenuFlyout>
</spp:AppItem.MoreFlyout>
<spp:AppItem.Image>
<spp:Image2
Width="150"
DecodeWidth="150"
Source="{Binding Thumb}"
Stretch="Uniform" />
</spp:AppItem.Image>
</spp:AppItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</spp:ContentLoader>
<StackPanel x:Name="MediaContent" Classes="InfoBox">
<Border Classes="Item">
<DockPanel>
<StackPanel Margin="0">
<WrapPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.HorizontalTitlePage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Margin="5,0"
Click="ShowGridDialog_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Header}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.GetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudDownload" />
</Button>
<Button
Click="ResetGridImage_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Header}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Reset, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="Redo" />
</Button>
</WrapPanel>
<!--<TextBlock Text="{Binding Path=Res.HorizontalTitlePage,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"/>
<ScrollViewer x:Name="MediaContent">
<StackPanel Classes="InfoBox">
<Border Classes="Item">
<DockPanel>
<StackPanel Margin="0">
<WrapPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.HorizontalTitlePage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Margin="5,0"
Padding="5"
Click="ShowGridDialog_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Header}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.GetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudDownload" />
</Button>
<Button
Padding="5"
Click="ResetGridImage_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Header}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Reset, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="Redo" />
</Button>
</WrapPanel>
<!--<TextBlock Text="{Binding Path=Res.HorizontalTitlePage,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"/>
<Button Classes="Hyperlink"
FontSize="12"
Content="{Binding Path=Res.GetImageFromSteamGridDB,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"
Tag="{x:Static spp:SteamGridItemType.Header}"
Click="ShowGridDialog_Click"/>-->
<TextBlock
Classes="prompt"
Text="{Binding Path=Res.RecommendedResolution, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock
Classes="prompt"
Text="460 x 215 px"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<TextBlock
Classes="prompt"
Text="{Binding Path=Res.RecommendedResolution, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock
Classes="prompt"
Text="460 x 215 px"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<spp:CustomFilePicker
MaxHeight="260"
Margin="50,0,0,0"
HorizontalAlignment="Right"
FileStream="{Binding App.EditHeaderLogoStream, Mode=TwoWay}">
<spp:Image2
DecodeHeight="260"
Source="{Binding App.EditHeaderLogoStream}"
Stretch="Uniform" />
</spp:CustomFilePicker>
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<StackPanel Margin="0">
<WrapPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.VerticalTitlePage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Margin="5,0"
Click="ShowGridDialog_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Grid}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.GetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudDownload" />
</Button>
<Button
Click="ResetGridImage_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Header}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Reset, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="Redo" />
</Button>
</WrapPanel>
<!--<TextBlock Text="{Binding Path=Res.VerticalTitlePage,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"/>
<spp:CustomFilePicker
MaxHeight="260"
Margin="50,0,0,0"
HorizontalAlignment="Right"
FileStream="{Binding App.EditHeaderLogoStream, Mode=TwoWay}">
<spp:Image2
DecodeHeight="260"
Source="{Binding App.EditHeaderLogoStream}"
Stretch="Uniform" />
</spp:CustomFilePicker>
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<StackPanel Margin="0">
<WrapPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.VerticalTitlePage, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Margin="5,0"
Padding="5"
Click="ShowGridDialog_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Grid}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.GetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudDownload" />
</Button>
<Button
Padding="5"
Click="ResetGridImage_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Grid}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Reset, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="Redo" />
</Button>
</WrapPanel>
<!--<TextBlock Text="{Binding Path=Res.VerticalTitlePage,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"/>
<Button Classes="Hyperlink"
FontSize="12"
Content="{Binding Path=Res.GetImageFromSteamGridDB,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"
Tag="{x:Static spp:SteamGridItemType.Grid}"
Click="ShowGridDialog_Click"/>-->
<TextBlock
Classes="prompt"
Text="{Binding Path=Res.RecommendedResolution, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock
Classes="prompt"
Text="600 x 900 px"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<TextBlock
Classes="prompt"
Text="{Binding Path=Res.RecommendedResolution, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock
Classes="prompt"
Text="600 x 900 px"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<spp:CustomFilePicker
MaxHeight="260"
Margin="50,0,0,0"
HorizontalAlignment="Right"
FileStream="{Binding App.EditLibraryGridStream, Mode=TwoWay}">
<spp:Image2
DecodeHeight="260"
Source="{Binding App.EditLibraryGridStream}"
Stretch="Uniform" />
</spp:CustomFilePicker>
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<StackPanel Margin="0">
<WrapPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.Background, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Margin="5,0"
Click="ShowGridDialog_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Hero}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.GetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudDownload" />
</Button>
<Button
Click="ResetGridImage_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Header}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Reset, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="Redo" />
</Button>
</WrapPanel>
<!--<TextBlock Text="{Binding Path=Res.Background,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"/>
<spp:CustomFilePicker
MaxHeight="260"
Margin="50,0,0,0"
HorizontalAlignment="Right"
FileStream="{Binding App.EditLibraryGridStream, Mode=TwoWay}">
<spp:Image2
DecodeHeight="260"
Source="{Binding App.EditLibraryGridStream}"
Stretch="Uniform" />
</spp:CustomFilePicker>
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<StackPanel Margin="0">
<WrapPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.Background, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Margin="5,0"
Padding="5"
Click="ShowGridDialog_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Hero}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.GetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudDownload" />
</Button>
<Button
Padding="5"
Click="ResetGridImage_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Hero}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Reset, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="Redo" />
</Button>
</WrapPanel>
<!--<TextBlock Text="{Binding Path=Res.Background,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"/>
<Button Classes="Hyperlink"
FontSize="12"
Content="{Binding Path=Res.GetImageFromSteamGridDB,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"
Tag="{x:Static spp:SteamGridItemType.Hero}"
Click="ShowGridDialog_Click"/>-->
<TextBlock
Classes="prompt"
Text="{Binding Path=Res.RecommendedResolution, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock
Classes="prompt"
Text="1920 x 620 px"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<TextBlock
Classes="prompt"
Text="{Binding Path=Res.RecommendedResolution, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock
Classes="prompt"
Text="1920 x 620 px"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<spp:CustomFilePicker
MaxHeight="260"
Margin="50,0,0,0"
HorizontalAlignment="Right"
FileStream="{Binding App.EditLibraryHeroStream, Mode=TwoWay}">
<spp:Image2
DecodeHeight="260"
Source="{Binding App.EditLibraryHeroStream}"
Stretch="Uniform" />
</spp:CustomFilePicker>
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<StackPanel Margin="0">
<WrapPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.Icon, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Margin="5,0"
Click="ShowGridDialog_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Logo}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.GetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudDownload" />
</Button>
<Button
Click="ResetGridImage_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Header}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Reset, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="Redo" />
</Button>
</WrapPanel>
<!--<Button Classes="Hyperlink"
<spp:CustomFilePicker
MaxHeight="260"
Margin="50,0,0,0"
HorizontalAlignment="Right"
FileStream="{Binding App.EditLibraryHeroStream, Mode=TwoWay}">
<spp:Image2
DecodeHeight="260"
Source="{Binding App.EditLibraryHeroStream}"
Stretch="Uniform" />
</spp:CustomFilePicker>
</DockPanel>
</Border>
<Border Classes="Item">
<DockPanel>
<StackPanel Margin="0">
<WrapPanel>
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Res.Icon, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Margin="5,0"
Padding="5"
Click="ShowGridDialog_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Logo}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.GetImageFromSteamGridDB, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudDownload" />
</Button>
<Button
Padding="5"
Click="ResetGridImage_Click"
Cursor="Hand"
Tag="{x:Static spp:SteamGridItemType.Logo}"
Theme="{StaticResource TransparentButton}"
ToolTip.Tip="{Binding Path=Res.Reset, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="Redo" />
</Button>
</WrapPanel>
<!--<Button Classes="Hyperlink"
FontSize="12"
Content="{Binding Path=Res.GetImageFromSteamGridDB,Mode=OneWay,Source={x:Static s:ResourceService.Current}}"
Tag="{x:Static spp:SteamGridItemType.Logo}"
Click="ShowGridDialog_Click"/>-->
<TextBlock
Classes="prompt"
Text="{Binding Path=Res.RecommendedResolution, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock
Classes="prompt"
Text="120 x 120 px"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<TextBlock
Classes="prompt"
Text="{Binding Path=Res.RecommendedResolution, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Theme="{StaticResource CaptionTextBlockStyle}" />
<TextBlock
Classes="prompt"
Text="120 x 120 px"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<spp:CustomFilePicker
MaxWidth="220"
Margin="50,0,0,0"
HorizontalAlignment="Right"
FileStream="{Binding App.EditLibraryLogoStream, Mode=TwoWay}">
<spp:Image2
DecodeWidth="220"
Source="{Binding App.EditLibraryLogoStream}"
Stretch="Uniform" />
</spp:CustomFilePicker>
</DockPanel>
</Border>
</StackPanel>
<spp:CustomFilePicker
MaxWidth="220"
Margin="50,0,0,0"
HorizontalAlignment="Right"
FileStream="{Binding App.EditLibraryLogoStream, Mode=TwoWay}">
<spp:Image2
DecodeWidth="220"
Source="{Binding App.EditLibraryLogoStream}"
Stretch="Uniform" />
</spp:CustomFilePicker>
</DockPanel>
</Border>
</StackPanel>
</ScrollViewer>
</Panel>
</TabItem>
<TabItem Header="{Binding Path=Res.LaunchItem, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsVisible="{Binding App.IsInstalled}">
<StackPanel Classes="InfoBox">
<ItemsRepeater ItemsSource="{Binding App.LaunchItems}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Vertical" Spacing="{StaticResource SettingsSpacing}" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type spp:SteamAppLaunchItem}">
<Border Classes="Item">
<StackPanel Spacing="5">
<DockPanel>
<Label Content="{Binding Path=Res.Name, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding Label}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.ExecutableFile, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding Executable}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.Arguments, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding Arguments}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.WorkingDir, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding WorkingDir}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.SupportSystem, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:FAComboBox IsEditable="True" Text="{Binding Platform, Mode=TwoWay}">
<ui:FAComboBox.Items>
<x:String>windows</x:String>
<x:String>linux</x:String>
<x:String>macos</x:String>
</ui:FAComboBox.Items>
</ui:FAComboBox>
</DockPanel>
<ScrollViewer>
<StackPanel Classes="InfoBox">
<ItemsRepeater ItemsSource="{Binding App.LaunchItems}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Vertical" Spacing="{StaticResource SettingsSpacing}" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type spp:SteamAppLaunchItem}">
<Border Classes="Item">
<StackPanel Spacing="5">
<DockPanel>
<Label Content="{Binding Path=Res.Name, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding Label}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.ExecutableFile, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding Executable}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.Arguments, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding Arguments}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.WorkingDir, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBox Text="{Binding WorkingDir}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.SupportSystem, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:FAComboBox IsEditable="True" Text="{Binding Platform, Mode=TwoWay}">
<ui:FAComboBox.Items>
<x:String>windows</x:String>
<x:String>linux</x:String>
<x:String>macos</x:String>
</ui:FAComboBox.Items>
</ui:FAComboBox>
</DockPanel>
<Grid ColumnDefinitions="*,*,*">
<Button
Margin="5"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).UpLaunchItemCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Res.MoveUp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Cursor="Hand" />
<Button
Grid.Column="1"
Margin="5"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).DownLaunchItemCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Res.MoveDown, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Cursor="Hand" />
<Button
Grid.Column="2"
Margin="5"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).DeleteLaunchItemCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Res.Delete, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Cursor="Hand" />
</Grid>
</StackPanel>
</Border>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<Border Classes="Item">
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Command="{Binding AddLaunchItem}"
Cursor="Hand">
<ui:SymbolIcon
Margin="0,20"
FontSize="32"
Symbol="Add" />
</Button>
</Border>
</StackPanel>
<Grid ColumnDefinitions="*,*,*">
<Button
Margin="5"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).UpLaunchItemCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Res.MoveUp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Cursor="Hand" />
<Button
Grid.Column="1"
Margin="5"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).DownLaunchItemCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Res.MoveDown, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Cursor="Hand" />
<Button
Grid.Column="2"
Margin="5"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).DeleteLaunchItemCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Res.Delete, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
Cursor="Hand" />
</Grid>
</StackPanel>
</Border>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<Border Classes="Item">
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Command="{Binding AddLaunchItem}"
Cursor="Hand">
<ui:SymbolIcon
Margin="0,20"
FontSize="32"
Symbol="Add" />
</Button>
</Border>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="{Binding Path=Res.SaveFile, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" IsVisible="{Binding App.IsCloudArchive}">
<StackPanel Classes="InfoBox">
<Border Classes="Item">
<StackPanel Spacing="5">
<DockPanel>
<StackPanel Orientation="Horizontal" Spacing="10">
<Label Content="{Binding Path=Res.CloudSaveFileSize, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Command="{Binding ManageCloudArchive_Click}"
Cursor="Hand"
ToolTip.Tip="{Binding Path=Res.GameList_CloudArchiveManager, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudBackup" />
</Button>
</StackPanel>
<TextBlock Classes="right" Text="{Binding App.CloudQuota, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.CloudMaxnumFiles, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Classes="right" Text="{Binding App.CloudMaxnumFiles}" />
</DockPanel>
</StackPanel>
</Border>
<ItemsRepeater ItemsSource="{Binding App.SaveFiles}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Vertical" Spacing="10" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type spp:SteamAppSaveFile}">
<Border Classes="Item">
<StackPanel Spacing="5">
<DockPanel>
<Label Content="Root" />
<TextBlock Classes="right" Text="{Binding Root}" />
</DockPanel>
<DockPanel>
<Label Content="Path" />
<TextBlock Classes="right" Text="{Binding Path}" />
</DockPanel>
<DockPanel>
<Label Content="Pattern" />
<TextBlock Classes="right" Text="{Binding Pattern}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.DirPath, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:HyperlinkButton
HorizontalAlignment="Right"
HorizontalContentAlignment="Stretch"
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).OpenFolder}"
CommandParameter="{Binding FormatDirPath}"
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Null}}">
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding FormatDirPath}"
TextWrapping="Wrap" />
</ui:HyperlinkButton>
</DockPanel>
<ScrollViewer>
<StackPanel Classes="InfoBox">
<Border Classes="Item">
<StackPanel Spacing="5">
<DockPanel>
<StackPanel Orientation="Horizontal" Spacing="10">
<Label Content="{Binding Path=Res.CloudSaveFileSize, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Command="{Binding ManageCloudArchive_Click}"
Cursor="Hand"
ToolTip.Tip="{Binding Path=Res.GameList_CloudArchiveManager, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<ui:SymbolIcon FontSize="18" Symbol="CloudBackup" />
</Button>
</StackPanel>
</Border>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</StackPanel>
<TextBlock Classes="right" Text="{Binding App.CloudQuota, Converter={StaticResource StringFormatConverter}, ConverterParameter=size}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.CloudMaxnumFiles, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<TextBlock Classes="right" Text="{Binding App.CloudMaxnumFiles}" />
</DockPanel>
</StackPanel>
</Border>
<ItemsRepeater ItemsSource="{Binding App.SaveFiles}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Vertical" Spacing="10" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type spp:SteamAppSaveFile}">
<Border Classes="Item">
<StackPanel Spacing="5">
<DockPanel>
<Label Content="Root" />
<TextBlock Classes="right" Text="{Binding Root}" />
</DockPanel>
<DockPanel>
<Label Content="Path" />
<TextBlock Classes="right" Text="{Binding Path}" />
</DockPanel>
<DockPanel>
<Label Content="Pattern" />
<TextBlock Classes="right" Text="{Binding Pattern}" />
</DockPanel>
<DockPanel>
<Label Content="{Binding Path=Res.DirPath, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:HyperlinkButton
HorizontalAlignment="Right"
HorizontalContentAlignment="Stretch"
Command="{Binding $parent[UserControl].((spp:EditAppInfoPageViewModel)DataContext).OpenFolder}"
CommandParameter="{Binding FormatDirPath}"
ToolTip.Tip="{Binding Path=Res.Settings_General_BrowseCustomFolder, Mode=OneWay, Source={x:Static s:ResourceService.Current}, Converter={StaticResource StringFormatConverter}, ConverterParameter={x:Null}}">
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding FormatDirPath}"
TextWrapping="Wrap" />
</ui:HyperlinkButton>
</DockPanel>
</StackPanel>
</Border>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</StackPanel>
</ScrollViewer>
</TabItem>
<!--<TabItem Header="DLC">
@@ -1,7 +1,10 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.ReactiveUI;
using Avalonia.VisualTree;
using BD.SteamClient.Enums.SteamGridDB;
using BD.SteamClient.Models.SteamGridDB;
using BD.WTTS.UI.Views.Controls;
namespace BD.WTTS.UI.Views.Pages;
@@ -24,6 +27,12 @@ public partial class EditAppInfoPage : ReactiveUserControl<EditAppInfoPageViewMo
}
}
public void HideGridDialog_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
SteamGridDBContent.IsVisible = false;
MediaContent.IsVisible = true;
}
public void ShowGridDialog_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
if (ViewModel != null)
@@ -88,9 +88,9 @@
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
@@ -113,33 +113,13 @@
Title="{Binding DisplayName}"
Height="270"
MaxWidth="120"
Classes="Vertical">
Classes="Vertical"
ClickCommand="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).EditAppInfoClickCommand}"
ClickCommandParameter="{Binding}"
Cursor="Hand">
<spp:AppItem.Status>
<Border Classes="Status">
<Panel>
<WrapPanel IsVisible="{Binding !Process, Converter={StaticResource IsNullConverter}}">
<Ellipse
Width="6"
Height="6"
Margin="0,0,5,0"
Fill="#47A42B" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Text="{Binding Path=Res.GameList_RunState, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
<WrapPanel IsVisible="{Binding Process, Converter={StaticResource IsNullConverter}}">
<Ellipse
Width="6"
Height="6"
Margin="0,0,5,0"
Fill="#FF4F58" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Text="{Binding Path=Res.GameList_StopState, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
</Panel>
<TextBlock Text="{Binding Path=Res.Edit, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</Border>
</spp:AppItem.Status>
<!--<spp:AppItem.ActionButton>
@@ -147,9 +127,7 @@
Command="{Binding $parent[spp:PageBase].((spp:EditAppsPageViewModel)DataContext).DeleteButtonCommand}"
CommandParameter="{Binding}"
Cursor="Hand"
ToolTip.Tip="{Binding Path=Res.GameList_RemoveItemBtn,
Mode=OneWay,
Source={x:Static s:ResourceService.Current}}">
ToolTip.Tip="{Binding Path=Res.GameList_RemoveItemBtn, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<Viewbox>
<ui:SymbolIcon Symbol="Delete" />
</Viewbox>
@@ -162,7 +140,7 @@
DecodeWidth="120"
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="{Binding LibraryGridUrl}"
Source="{Binding LibraryGridStream^}"
Stretch="UniformToFill" />
<TextBlock
Margin="8,0"
@@ -343,112 +343,6 @@
</StackPanel>
</spp:PageBase.PaneContent>
<DockPanel>
<!--<Grid
Margin="0,0,0,10"
ColumnDefinitions="Auto,*,Auto"
DockPanel.Dock="Top"
RowDefinitions="Auto,10,Auto">
<AutoCompleteBox
MinWidth="300"
VerticalAlignment="Center"
Classes="Search" />
<TabStrip
Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{DynamicResource ControlOnImageFillColorDefaultBrush}"
CornerRadius="{StaticResource ControlCornerRadius}"
ItemContainerTheme="{StaticResource TabItemStyle}"
Theme="{StaticResource TabStripStyle}">
<TabStripItem Content="&#xE8E4;" FontFamily="{StaticResource SymbolThemeFontFamily}" />
<TabStripItem Content="&#xF0E2;" FontFamily="{StaticResource SymbolThemeFontFamily}" />
<TabStripItem Content="&#xE80A;" FontFamily="{StaticResource SymbolThemeFontFamily}" />
</TabStrip>
<TextBlock
Grid.Row="2"
VerticalAlignment="Center"
Text="{Binding SteamApps.Count, Converter={StaticResource StringFormatConverter}, ConverterParameter='找到 {0} 个应用'}" />
<StackPanel
Grid.Row="2"
Grid.Column="2"
Orientation="Horizontal"
Spacing="4">
<ToggleButton
Padding="8"
Theme="{StaticResource TransparentToggleButton}"
ToolTip.Tip="仅显示已安装的游戏">
<Viewbox Width="20" Height="20">
<ui:SymbolIcon Symbol="Play" />
</Viewbox>
</ToggleButton>
<ToggleButton
Padding="8"
Theme="{StaticResource TransparentToggleButton}"
ToolTip.Tip="仅显示支持 Steam 云存档游戏">
<Viewbox Width="20" Height="20">
<ui:SymbolIcon Symbol="CloudSyncComplete" />
</Viewbox>
</ToggleButton>
<DropDownButton
Width="200"
VerticalAlignment="Center"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Center">
<DropDownButton.Content>
<TextBlock
VerticalAlignment="Center"
Classes="h8"
DockPanel.Dock="Left"
Text="{Binding TypeFilterString}"
TextTrimming="CharacterEllipsis" />
</DropDownButton.Content>
<DropDownButton.Styles>
<Style Selector="FlyoutPresenter">
<Setter Property="Padding" Value="0" />
</Style>
</DropDownButton.Styles>
<DropDownButton.Flyout>
<Flyout FlyoutPresenterClasses="NoPadding" Placement="Bottom">
<Border Width="{Binding $parent.Width}" Height="200">
<ScrollViewer Padding="10,5">
<ItemsControl ItemsSource="{Binding AppTypeFiltres}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.DataType>
<x:Type x:TypeArguments="spp:SteamAppType" TypeName="spp:EnumModel" />
</DataTemplate.DataType>
<DockPanel>
<CheckBox VerticalAlignment="Center" IsChecked="{Binding Enable, Mode=TwoWay}">
<StackPanel Orientation="Horizontal">
<StackPanel.Styles>
<Style Selector="TextBlock.count">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
</Style>
</StackPanel.Styles>
<TextBlock VerticalAlignment="Center" Text="{Binding LocalizationName}" />
<TextBlock
Margin="5,0,0,0"
Classes="count"
Text="(" />
<TextBlock Classes="count" Text="{Binding Count}" />
<TextBlock Classes="count" Text=")" />
</StackPanel>
</CheckBox>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Border>
</Flyout>
</DropDownButton.Flyout>
</DropDownButton>
</StackPanel>
</Grid>-->
<spp:ContentLoader
IsLoading="{Binding Source={x:Static s:SteamConnectService.Current}, Path=IsLoadingGameList, Mode=OneWay}"
IsShowNoResultText="{Binding IsSteamAppsEmpty}"
@@ -529,7 +423,7 @@
DecodeWidth="169"
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="{Binding LibraryGridUrl}"
Source="{Binding LibraryGridStream^}"
Stretch="UniformToFill" />
<TextBlock
Margin="8,0"
@@ -121,9 +121,9 @@
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
@@ -150,7 +150,7 @@
<spp:AppItem.Status>
<Border Classes="Status">
<Panel>
<WrapPanel IsVisible="{Binding !Process, Converter={StaticResource IsNullConverter}}">
<WrapPanel IsVisible="{Binding Process, Converter={StaticResource IsNullConverter}, ConverterParameter=invert, Mode=OneWay}">
<Ellipse
Width="6"
Height="6"
@@ -161,7 +161,7 @@
FontSize="12"
Text="{Binding Path=Res.GameList_RunState, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
<WrapPanel IsVisible="{Binding Process, Converter={StaticResource IsNullConverter}}">
<WrapPanel IsVisible="{Binding Process, Converter={StaticResource IsNullConverter}, Mode=OneWay}">
<Ellipse
Width="6"
Height="6"
@@ -210,7 +210,7 @@
DecodeWidth="120"
FallbackSource="avares://BD.WTTS.Client.Plugins.GameList/UI/Assets/defaultappimage.png"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="{Binding LibraryGridUrl}"
Source="{Binding LibraryGridStream^}"
Stretch="UniformToFill" />
<TextBlock
Margin="8,0"
@@ -8,6 +8,17 @@
</PropertyGroup>
<ItemGroup>
<None Remove="UI\Assets\BorderlessWindow.png" />
<None Remove="UI\Assets\CounterStrike.png" />
<None Remove="UI\Assets\CPU.png" />
<None Remove="UI\Assets\movecross.png" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Include="UI\Assets\BorderlessWindow.png" />
<AvaloniaResource Include="UI\Assets\CounterStrike.png" />
<AvaloniaResource Include="UI\Assets\CPU.png" />
<AvaloniaResource Include="UI\Assets\movecross.png" />
<AvaloniaResource Include="UI\Assets\toolbox.ico" />
</ItemGroup>
@@ -75,17 +86,15 @@
<ProjectReference Include="..\BD.WTTS.Client.Avalonia\BD.WTTS.Client.Avalonia.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="UI\ViewModels\" />
<Folder Include="UI\Views\" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="UI\Views\GameToolsPage.axaml.cs">
<DependentUpon>GameToolsPage.axaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
@@ -95,6 +104,6 @@
</EmbeddedResource>
</ItemGroup>
<Import Project="..\TFM_NETX_WITH_ALL.props" />
<Import Project="..\TFM_NETX_WITH_DESKTOP.props" />
</Project>
@@ -0,0 +1,23 @@
namespace BD.WTTS;
#if WINDOWS
public static partial class HandleWindowExtensions
{
public static bool IsHasProcessExits(this NativeWindowModel window)
{
if (window?.Process?.HasExited == false && window.Name != Process.GetCurrentProcess().ProcessName)
{
return false;
}
return true;
}
public static void Kill(this NativeWindowModel window)
{
if (!window.IsHasProcessExits())
{
window.Process?.Kill();
}
}
}
#endif
@@ -1,4 +1,5 @@
using BD.WTTS.Properties;
using BD.WTTS.UI.Views.Pages;
namespace BD.WTTS.Plugins;
@@ -15,7 +16,7 @@ public sealed class Plugin : PluginBase<Plugin>, IPlugin
public sealed override string UniqueEnglishName => moduleName;
public sealed override string Description => "游戏工具";
public sealed override string Description => "通用游戏工具";
protected sealed override string? AuthorOriginalString => null;
@@ -25,7 +26,7 @@ public sealed class Plugin : PluginBase<Plugin>, IPlugin
{
yield return new MenuTabItemViewModel(this, nameof(Strings.GameRelated))
{
PageType = null,
PageType = typeof(GameToolsPage),
IsResourceGet = true,
IconKey = Icon,
};
@@ -1,77 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

@@ -0,0 +1,127 @@
using Avalonia.Platform;
using BD.WTTS.UI.Views.Pages;
namespace BD.WTTS.UI.ViewModels;
public sealed partial class BorderlessGamePageViewModel : ViewModelBase
{
#if WINDOWS
readonly IPlatformService windowApi = IPlatformService.Instance!;
#region SelectWindow 变更通知
private NativeWindowModel? _SelectWindow;
public NativeWindowModel? SelectWindow
{
get => _SelectWindow;
set
{
if (_SelectWindow != value)
{
_SelectWindow = value;
this.RaisePropertyChanged();
}
}
}
#endregion
ObservableCollection<NativeWindowModel> _WindowList = new();
public ObservableCollection<NativeWindowModel> WindowList
{
get => _WindowList;
set
{
if (_WindowList != value)
{
_WindowList = value;
this.RaisePropertyChanged();
}
}
}
public BorderlessGamePageViewModel()
{
}
public void Cross_MouseDown()
{
windowApi.GetMoveMouseDownWindow((window) =>
{
SelectWindow = window;
WindowList.Insert(0, window);
});
}
public void BorderlessWindow_Click()
{
if (SelectWindow == null)
{
return;
}
windowApi.BorderlessWindow(SelectWindow);
}
public void MaximizeWindow_Click()
{
if (SelectWindow == null)
{
return;
}
windowApi.MaximizeWindow(SelectWindow);
}
public void NormalWindow_Click()
{
if (SelectWindow == null)
{
return;
}
windowApi.NormalWindow(SelectWindow);
}
public void WindowKill_Click()
{
if (SelectWindow == null)
{
return;
}
SelectWindow.Kill();
}
public void ShowWindow_Click()
{
if (SelectWindow == null)
{
return;
}
windowApi.ShowWindow(SelectWindow);
}
public void HideWindow_Click()
{
if (SelectWindow == null)
{
return;
}
windowApi.HideWindow(SelectWindow);
}
public void ToWallerpaperWindow_Click()
{
if (SelectWindow == null)
{
return;
}
windowApi.ToWallerpaperWindow(SelectWindow);
}
public void ResetWallerpaper_Click()
{
windowApi.ResetWallerpaper();
}
#endif
}
@@ -0,0 +1,173 @@
<spp:PageBase
x:Class="BD.WTTS.UI.Views.Pages.BorderlessGamePage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="using:Avalonia.Xaml.Interactivity"
xmlns:ia="using:Avalonia.Xaml.Interactions.Core"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="https://steampp.net/services"
xmlns:spp="https://steampp.net/ui"
xmlns:ui="using:FluentAvalonia.UI.Controls"
Title="游戏无边框窗口化"
d:DesignHeight="450"
d:DesignWidth="800"
x:CompileBindings="True"
x:DataType="spp:BorderlessGamePageViewModel"
Description="该功能可以获取游戏窗口句柄执行一些对应操作"
Subtitle="{Binding Author, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource StringFormatConverter}, ConverterParameter=Plugin_Author}"
mc:Ignorable="d">
<spp:PageBase.PreviewImage>
<ui:ImageIconSource Source="avares://BD.WTTS.Client.Plugins.GameTools/UI/Assets/BorderlessWindow.png" />
</spp:PageBase.PreviewImage>
<spp:PageBase.ActionContent>
<StackPanel Orientation="Horizontal" Spacing="2">
<ToggleButton
Padding="8,4"
DockPanel.Dock="Right"
IsChecked="{Binding $parent[spp:PageBase].IsPaneOpen, Mode=TwoWay}"
Theme="{StaticResource TransparentButton}">
<Panel>
<Viewbox
Width="16"
Height="16"
HorizontalAlignment="Left">
<ui:FontIcon Glyph="&#xE81C;" />
</Viewbox>
<TextBlock
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="查看捕获窗口记录" />
</Panel>
</ToggleButton>
<DropDownButton
Padding="8"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="{StaticResource More}"
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="20"
Theme="{StaticResource TransparentButton}">
<DropDownButton.Flyout>
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
<!--<ui:MenuFlyoutItem x:Name="PluginSettingButton" Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay,Source={x:Static s:ResourceService.Current}}" />-->
<!--<ui:MenuFlyoutSeparator />-->
<ui:MenuFlyoutItem IsEnabled="False">
<ui:MenuFlyoutItem.Text>
<MultiBinding StringFormat="{}{0}{1}">
<CompiledBinding
Mode="OneWay"
Path="Res.Plugin_Version"
Source="{x:Static s:ResourceService.Current}" />
<CompiledBinding
Mode="OneWay"
Path="Version"
Source="{x:Static s:Plugin.Instance}" />
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
</StackPanel>
</spp:PageBase.ActionContent>
<spp:PageBase.PaneContent>
<StackPanel Margin="15,20" Spacing="10">
<TextBlock
VerticalAlignment="Center"
Text="历史记录"
Theme="{StaticResource CaptionTextBlockStyle}" />
<ListBox ItemsSource="{ReflectionBinding WindowList, Mode=OneWay}" SelectedItem="{ReflectionBinding SelectWindow}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Spacing="5" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel VerticalAlignment="Center">
<TextBlock>
<Run Text="{ReflectionBinding Name, Mode=OneWay}" />
<Run Text="(" />
<Run Text="{ReflectionBinding Title, Mode=OneWay}" />
<Run Text=")" />
</TextBlock>
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</spp:PageBase.PaneContent>
<ScrollViewer Padding="10">
<StackPanel Spacing="20">
<WrapPanel HorizontalAlignment="Center">
<Image
x:Name="move"
Width="64"
HorizontalAlignment="Left"
Source="avares://BD.WTTS.Client.Plugins.GameTools/UI/Assets/movecross.png">
<i:Interaction.Behaviors>
<ia:EventTriggerBehavior EventName="PointerPressed" SourceObject="{Binding #move}">
<ia:InvokeCommandAction Command="{ReflectionBinding $parent[spp:PageBase].((spp:BorderlessGamePageViewModel)DataContext).Cross_MouseDown}" />
</ia:EventTriggerBehavior>
</i:Interaction.Behaviors>
</Image>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
Text="{Binding Path=Res.GameRelated_Borderless_MoveMouseTip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
<StackPanel
HorizontalAlignment="Center"
IsVisible="{ReflectionBinding !!SelectWindow}"
Orientation="Vertical"
Spacing="10">
<TextBlock TextWrapping="WrapWithOverflow">
<Run Text="{Binding Path=Res.GameRelated_Borderless_Title, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<Run Text="{ReflectionBinding SelectWindow.Title, Mode=OneWay}" />
</TextBlock>
<TextBlock TextWrapping="WrapWithOverflow">
<Run Text="{Binding Path=Res.GameRelated_Borderless_ClassName, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<Run Text="{ReflectionBinding SelectWindow.ClassName, Mode=OneWay}" />
</TextBlock>
<TextBlock TextWrapping="WrapWithOverflow">
<Run Text="{Binding Path=Res.GameRelated_Borderless_Process, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<Run Text="{ReflectionBinding SelectWindow.Name, Mode=OneWay}" />
</TextBlock>
<TextBlock TextWrapping="WrapWithOverflow">
<Run Text="{Binding Path=Res.GameRelated_Borderless_Path, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat={}{0}:}" />
<Run Text="{ReflectionBinding SelectWindow.Path, Mode=OneWay}" />
</TextBlock>
</StackPanel>
<WrapPanel HorizontalAlignment="Center" Orientation="Horizontal">
<WrapPanel.Styles>
<Style Selector="Button">
<Setter Property="Height" Value="30" />
<Setter Property="Margin" Value="3" />
</Style>
</WrapPanel.Styles>
<Button Command="{ReflectionBinding WindowKill_Click}" Content="{Binding Path=Res.GameRelated_Borderless_KillProcess, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Command="{ReflectionBinding BorderlessWindow_Click}" Content="{Binding Path=Res.GameRelated_Borderless_BorderlessWindow, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Command="{ReflectionBinding MaximizeWindow_Click}" Content="{Binding Path=Res.GameRelated_Borderless_Maximize, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Command="{ReflectionBinding NormalWindow_Click}" Content="{Binding Path=Res.GameRelated_Borderless_Normal, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Command="{ReflectionBinding HideWindow_Click}" Content="{Binding Path=Res.GameRelated_Borderless_Hide, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Command="{ReflectionBinding ShowWindow_Click}" Content="{Binding Path=Res.GameRelated_Borderless_Show, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button
Command="{ReflectionBinding ToWallerpaperWindow_Click}"
Content="{Binding Path=Res.GameRelated_Borderless_ToWallerpaper, Mode=OneWay, Source={x:Static s:ResourceService.Current}}"
ToolTip.Tip="{Binding Path=Res.GameRelated_Borderless_ToWallerpaperTip, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<Button Command="{ReflectionBinding ResetWallerpaper_Click}" Content="{Binding Path=Res.GameRelated_Borderless_ResetWallerpaper, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</WrapPanel>
<!--<TextBlock Text="{ReflectionBinding Path=Res.GameRelated_Borderless_ToWallerpaperTip, Mode=OneWay, Source={x:Static s:ResourceService.Current}, StringFormat=*{0}}" TextWrapping="WrapWithOverflow" />-->
</StackPanel>
</ScrollViewer>
</spp:PageBase>
@@ -0,0 +1,10 @@
namespace BD.WTTS.UI.Views.Pages;
public partial class BorderlessGamePage : PageBase<BorderlessGamePageViewModel>
{
public BorderlessGamePage()
{
InitializeComponent();
DataContext ??= new BorderlessGamePageViewModel();
}
}
@@ -0,0 +1,190 @@
<spp:PageBase
x:Class="BD.WTTS.UI.Views.Pages.GameToolsPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="https://steampp.net/services"
xmlns:spp="https://steampp.net/ui"
xmlns:ui="using:FluentAvalonia.UI.Controls"
Title="{Binding Name, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
d:DesignHeight="450"
d:DesignWidth="800"
Description="{Binding Description, Source={x:Static s:Plugin.Instance}, Mode=OneWay}"
Subtitle="{Binding Author, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource StringFormatConverter}, ConverterParameter=Plugin_Author}"
mc:Ignorable="d">
<spp:PageBase.PreviewImage>
<ui:ImageIconSource Source="{Binding Icon, Source={x:Static s:Plugin.Instance}, Mode=OneWay, Converter={StaticResource BitmapAssetValueConverter}}" />
</spp:PageBase.PreviewImage>
<spp:PageBase.TabContent>
<!--<TabStrip
Margin="4"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{DynamicResource ControlOnImageFillColorDefaultBrush}"
CornerRadius="{StaticResource ControlCornerRadius}"
ItemContainerTheme="{StaticResource TabItemStyle}">
<TabStripItem Content="多平台账号切换" />
<TabStripItem Content="Steam 家庭共享库管理" />
</TabStrip>-->
</spp:PageBase.TabContent>
<spp:PageBase.ActionContent>
<StackPanel Orientation="Horizontal" Spacing="2">
<Button
Padding="8,4"
DockPanel.Dock="Right"
Theme="{StaticResource TransparentButton}">
<Panel>
<Viewbox
Width="16"
Height="16"
HorizontalAlignment="Left">
<ui:SymbolIcon Symbol="Refresh" />
</Viewbox>
<TextBlock
Margin="28,4,0,4"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding Path=Res.Refresh, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
</Panel>
</Button>
<DropDownButton
Padding="8"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="{StaticResource More}"
FontFamily="{StaticResource SymbolThemeFontFamily}"
FontSize="20"
Theme="{StaticResource TransparentButton}">
<DropDownButton.Flyout>
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
<!--<ui:MenuFlyoutItem x:Name="PluginSettingButton" Text="{Binding Path=Res.Settings_Plugin, Mode=OneWay,Source={x:Static s:ResourceService.Current}}" />-->
<!--<ui:MenuFlyoutSeparator />-->
<ui:MenuFlyoutItem IsEnabled="False">
<ui:MenuFlyoutItem.Text>
<MultiBinding StringFormat="{}{0}{1}">
<CompiledBinding
Mode="OneWay"
Path="Res.Plugin_Version"
Source="{x:Static s:ResourceService.Current}" />
<CompiledBinding
Mode="OneWay"
Path="Version"
Source="{x:Static s:Plugin.Instance}" />
</MultiBinding>
</ui:MenuFlyoutItem.Text>
</ui:MenuFlyoutItem>
<!--<ui:MenuFlyoutItem Text="{Binding Path=Res.ViewInStore, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.UseHelp, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />
<ui:MenuFlyoutItem Text="{Binding Path=Res.AboutThisPlugin, Mode=OneWay, Source={x:Static s:ResourceService.Current}}" />-->
</ui:FAMenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
</StackPanel>
</spp:PageBase.ActionContent>
<ScrollViewer>
<ScrollViewer.Resources>
<ControlTheme
x:Key="NavButtonStyle"
BasedOn="{StaticResource {x:Type Button}}"
TargetType="Button">
<Setter Property="Width" Value="160" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="6" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="ClipToBounds" Value="True" />
</Style>
</ControlTheme>
</ScrollViewer.Resources>
<WrapPanel>
<Border spp:Animations.EnableAnimations="True">
<Button
Click="GameToolsPage_Click"
IsEnabled="{spp:OnPlatform Windows}"
Theme="{StaticResource NavButtonStyle}">
<Button.Tag>
<x:Type TypeName="spp:BorderlessGamePage" />
</Button.Tag>
<Grid RowDefinitions="3*,*">
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CornerRadius="4 4 0 0"
RenderOptions.BitmapInterpolationMode="HighQuality">
<spp:Image2 Source="avares://BD.WTTS.Client.Plugins.GameTools/UI/Assets/BorderlessWindow.png" Stretch="Uniform" />
</Border>
<StackPanel
Grid.Row="1"
Margin="12"
Spacing="5">
<TextBlock FontWeight="SemiBold" Text="游戏无边框窗口化" />
<TextBlock
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
Text="仅 Windows 可用"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
</Grid>
</Button>
</Border>
<Border spp:Animations.EnableAnimations="True">
<Button
Click="GameToolsPage_Click"
IsEnabled="{spp:OnPlatform Windows}"
Tag="{x:Null}"
Theme="{StaticResource NavButtonStyle}">
<Grid RowDefinitions="3*,*">
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CornerRadius="4 4 0 0"
RenderOptions.BitmapInterpolationMode="HighQuality">
<spp:Image2 Source="avares://BD.WTTS.Client.Plugins.GameTools/UI/Assets/CounterStrike.png" Stretch="Uniform" />
</Border>
<StackPanel
Grid.Row="1"
Margin="12"
Spacing="5">
<TextBlock FontWeight="SemiBold" Text="CS:GO VAC 修复" />
<TextBlock
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
Text="仅 Windows 可用"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
</Grid>
</Button>
</Border>
<Border spp:Animations.EnableAnimations="True">
<Button
Click="GameToolsPage_Click"
IsEnabled="{spp:OnPlatform Windows}"
Tag="{x:Null}"
Theme="{StaticResource NavButtonStyle}">
<Grid RowDefinitions="3*,*">
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CornerRadius="4 4 0 0"
RenderOptions.BitmapInterpolationMode="HighQuality">
<spp:Image2 Source="avares://BD.WTTS.Client.Plugins.GameTools/UI/Assets/CPU.png" Stretch="Uniform" />
</Border>
<StackPanel
Grid.Row="1"
Margin="12"
Spacing="5">
<TextBlock FontWeight="SemiBold" Text="CPU 游戏调度优化" />
<TextBlock
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
Text="仅 Windows 可用"
Theme="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
</Grid>
</Button>
</Border>
</WrapPanel>
</ScrollViewer>
</spp:PageBase>
@@ -0,0 +1,23 @@
using Avalonia.Controls;
namespace BD.WTTS.UI.Views.Pages;
public partial class GameToolsPage : PageBase
{
public GameToolsPage()
{
InitializeComponent();
}
private void GameToolsPage_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{
if (sender is Control c && c.Tag is Type t)
{
NavigationService.Instance.Navigate(t, NavigationTransitionEffect.FromRight);
}
else
{
NavigationService.Instance.Navigate(null, NavigationTransitionEffect.FromRight);
}
}
}
@@ -0,0 +1,41 @@
<UserControl
x:Class="BD.WTTS.UI.Views.Pages.MainFramePage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="https://steampp.net/services"
xmlns:spp="https://steampp.net/ui"
xmlns:ui="using:FluentAvalonia.UI.Controls"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Panel>
<TabStrip
Name="Tabs"
Margin="9"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{DynamicResource ControlOnImageFillColorDefaultBrush}"
CornerRadius="{StaticResource ControlCornerRadius}"
ItemContainerTheme="{StaticResource TabItemStyle}"
Theme="{StaticResource TabStripStyle}"
ZIndex="100">
<TabStrip.ItemsPanel>
<OnFormFactor Default="{StaticResource DefaultPanel}">
<On Options="Mobile">
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</On>
</OnFormFactor>
</TabStrip.ItemsPanel>
<TabStripItem Content="{Binding Path=Res.Library, Mode=OneWay, Source={x:Static s:ResourceService.Current}}">
<TabStripItem.Tag>
<x:Type TypeName="spp:BorderlessGamePage" />
</TabStripItem.Tag>
</TabStripItem>
</TabStrip>
<ui:Frame Name="InnerNavFrame" IsNavigationStackEnabled="False" />
</Panel>
</UserControl>
@@ -0,0 +1,39 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using FluentAvalonia.UI.Media.Animation;
namespace BD.WTTS.UI.Views.Pages;
public partial class MainFramePage : UserControl
{
int lastIndex = -1;
public MainFramePage()
{
InitializeComponent();
Tabs.SelectionChanged += Tabs_SelectionChanged;
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
Tabs_SelectionChanged(null, null);
}
private void Tabs_SelectionChanged(object? sender, Avalonia.Controls.SelectionChangedEventArgs? e)
{
if (Tabs.SelectedItem is TabStripItem tab && tab.Tag is Type t)
{
InnerNavFrame.Navigate(t, null, new SlideNavigationTransitionInfo
{
Effect = FrameEffect.GetEffect(lastIndex, Tabs.SelectedIndex),
FromHorizontalOffset = 70,
});
lastIndex = Tabs.SelectedIndex;
}
}
}
@@ -7,6 +7,13 @@
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaXaml Remove="UI\Views\Windows\**" />
<Compile Remove="UI\Views\Windows\**" />
<EmbeddedResource Remove="UI\Views\Windows\**" />
<None Remove="UI\Views\Windows\**" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\AssemblyInfo.Xaml.cs">
<LinkBase>Properties</LinkBase>
@@ -73,7 +80,6 @@
<ItemGroup>
<Folder Include="Models\" />
<Folder Include="UI\Views\Windows\" />
</ItemGroup>
<ItemGroup>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

@@ -19,7 +19,6 @@ public sealed class IdleAppsPageViewModel : ViewModelBase
RunOrStopAutoNext(x.Value);
this.IsAutoNextOnTxt = x.Value ? Strings.Idle_StopAutoNext : Strings.Idle_OpenAutoNext;
});
IdleRunStartOrStop_Click();
}
[Reactive]
@@ -79,6 +78,13 @@ public sealed class IdleAppsPageViewModel : ViewModelBase
private double SwitchTime { get; set; } = 500;
#endregion
public override void Activation()
{
base.Activation();
IdleRunStartOrStop_Click();
}
/// <summary>
/// 启动或停止挂卡
/// </summary>
@@ -1,10 +1,11 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using BD.SteamClient.Models;
namespace BD.WTTS.UI.Views.Pages;
public partial class MainFramePage : UserControl
public partial class MainFramePage : ReactiveUserControl<IdleAppsPageViewModel>
{
public MainFramePage()
{
+8 -1
View File
@@ -69,7 +69,14 @@ static partial class MouseHook
MouseUp?.Invoke(null, new PointDouble(mouseHookStruct.pt.X, mouseHookStruct.pt.Y));
}
}
return PInvoke.User32.CallNextHookEx(_mouseHookHandle.ThrowIsNull().DangerousGetHandle(), nCode, wParam, lParam);
if (_mouseHookHandle != null)
{
return PInvoke.User32.CallNextHookEx(_mouseHookHandle.DangerousGetHandle(), nCode, wParam, lParam);
}
else
{
return 0;
}
}
const int WM_LBUTTONUP = 0x0202;
+145 -1
View File
@@ -1512,7 +1512,7 @@ namespace BD.WTTS.Client.Resources {
}
/// <summary>
/// 查找类似 选择文件 的本地化字符串。
/// 查找类似 选择或拖拽文件到此 的本地化字符串。
/// </summary>
public static string CustomFilePicker_Tip {
get {
@@ -2888,6 +2888,15 @@ namespace BD.WTTS.Client.Resources {
}
}
/// <summary>
/// 查找类似 挂时长列表存在不属于当前 Steam 账号的 {0} 个游戏 的本地化字符串。
/// </summary>
public static string GameList_RunOtherAppCount_ {
get {
return ResourceManager.GetString("GameList_RunOtherAppCount_", resourceCulture);
}
}
/// <summary>
/// 查找类似 运行中 的本地化字符串。
/// </summary>
@@ -3015,6 +3024,141 @@ namespace BD.WTTS.Client.Resources {
}
}
/// <summary>
/// 查找类似 无边框窗口化 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless {
get {
return ResourceManager.GetString("GameRelated_Borderless", resourceCulture);
}
}
/// <summary>
/// 查找类似 无边框窗口化 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_BorderlessWindow {
get {
return ResourceManager.GetString("GameRelated_Borderless_BorderlessWindow", resourceCulture);
}
}
/// <summary>
/// 查找类似 类名 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_ClassName {
get {
return ResourceManager.GetString("GameRelated_Borderless_ClassName", resourceCulture);
}
}
/// <summary>
/// 查找类似 隐藏 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_Hide {
get {
return ResourceManager.GetString("GameRelated_Borderless_Hide", resourceCulture);
}
}
/// <summary>
/// 查找类似 结束进程 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_KillProcess {
get {
return ResourceManager.GetString("GameRelated_Borderless_KillProcess", resourceCulture);
}
}
/// <summary>
/// 查找类似 最大化 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_Maximize {
get {
return ResourceManager.GetString("GameRelated_Borderless_Maximize", resourceCulture);
}
}
/// <summary>
/// 查找类似 拖拽十字到游戏窗口,即可获取游戏信息执行对应操作。 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_MoveMouseTip {
get {
return ResourceManager.GetString("GameRelated_Borderless_MoveMouseTip", resourceCulture);
}
}
/// <summary>
/// 查找类似 默认大小 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_Normal {
get {
return ResourceManager.GetString("GameRelated_Borderless_Normal", resourceCulture);
}
}
/// <summary>
/// 查找类似 程序路径 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_Path {
get {
return ResourceManager.GetString("GameRelated_Borderless_Path", resourceCulture);
}
}
/// <summary>
/// 查找类似 进程 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_Process {
get {
return ResourceManager.GetString("GameRelated_Borderless_Process", resourceCulture);
}
}
/// <summary>
/// 查找类似 刷新壁纸 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_ResetWallerpaper {
get {
return ResourceManager.GetString("GameRelated_Borderless_ResetWallerpaper", resourceCulture);
}
}
/// <summary>
/// 查找类似 显示 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_Show {
get {
return ResourceManager.GetString("GameRelated_Borderless_Show", resourceCulture);
}
}
/// <summary>
/// 查找类似 标题 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_Title {
get {
return ResourceManager.GetString("GameRelated_Borderless_Title", resourceCulture);
}
}
/// <summary>
/// 查找类似 置于壁纸层 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_ToWallerpaper {
get {
return ResourceManager.GetString("GameRelated_Borderless_ToWallerpaper", resourceCulture);
}
}
/// <summary>
/// 查找类似 置于壁纸层的游戏可能会无法操控,这个问题可以解决但是目前开发精力不在这,目前这个功能就这样了。游戏结束后桌面会依然留下冻结的画面,这时可以点击刷新壁纸来恢复默认壁纸。并且将窗口放置于桌面壁纸层功能与 Wallpaper Engine 和其它动态桌面软件不兼容,使用时注意关掉 Wallpaper Engine 等软件。 的本地化字符串。
/// </summary>
public static string GameRelated_Borderless_ToWallerpaperTip {
get {
return ResourceManager.GetString("GameRelated_Borderless_ToWallerpaperTip", resourceCulture);
}
}
/// <summary>
/// 查找类似 性别 的本地化字符串。
/// </summary>
+49 -1
View File
@@ -1397,7 +1397,7 @@
<value>请在弹出的控制台窗口中输入密码或者手动执行</value>
</data>
<data name="CustomFilePicker_Tip" xml:space="preserve">
<value>选择文件</value>
<value>选择或拖拽文件到此</value>
</data>
<data name="Info" xml:space="preserve">
<value>信息</value>
@@ -2738,5 +2738,53 @@
</data>
<data name="User_Rememberme" xml:space="preserve">
<value>记住登录状态</value>
</data>
<data name="GameList_RunOtherAppCount_" xml:space="preserve">
<value>挂时长列表存在不属于当前 Steam 账号的 {0} 个游戏</value>
</data>
<data name="GameRelated_Borderless" xml:space="preserve">
<value>无边框窗口化</value>
</data>
<data name="GameRelated_Borderless_BorderlessWindow" xml:space="preserve">
<value>无边框窗口化</value>
</data>
<data name="GameRelated_Borderless_ClassName" xml:space="preserve">
<value>类名</value>
</data>
<data name="GameRelated_Borderless_Hide" xml:space="preserve">
<value>隐藏</value>
</data>
<data name="GameRelated_Borderless_KillProcess" xml:space="preserve">
<value>结束进程</value>
</data>
<data name="GameRelated_Borderless_Maximize" xml:space="preserve">
<value>最大化</value>
</data>
<data name="GameRelated_Borderless_MoveMouseTip" xml:space="preserve">
<value>拖拽十字到游戏窗口,即可获取游戏信息执行对应操作。</value>
</data>
<data name="GameRelated_Borderless_Normal" xml:space="preserve">
<value>默认大小</value>
</data>
<data name="GameRelated_Borderless_Path" xml:space="preserve">
<value>程序路径</value>
</data>
<data name="GameRelated_Borderless_Process" xml:space="preserve">
<value>进程</value>
</data>
<data name="GameRelated_Borderless_ResetWallerpaper" xml:space="preserve">
<value>刷新壁纸</value>
</data>
<data name="GameRelated_Borderless_Show" xml:space="preserve">
<value>显示</value>
</data>
<data name="GameRelated_Borderless_Title" xml:space="preserve">
<value>标题</value>
</data>
<data name="GameRelated_Borderless_ToWallerpaper" xml:space="preserve">
<value>置于壁纸层</value>
</data>
<data name="GameRelated_Borderless_ToWallerpaperTip" xml:space="preserve">
<value>置于壁纸层的游戏可能会无法操控,这个问题可以解决但是目前开发精力不在这,目前这个功能就这样了。游戏结束后桌面会依然留下冻结的画面,这时可以点击刷新壁纸来恢复默认壁纸。并且将窗口放置于桌面壁纸层功能与 Wallpaper Engine 和其它动态桌面软件不兼容,使用时注意关掉 Wallpaper Engine 等软件。</value>
</data>
</root>
@@ -156,11 +156,11 @@ public sealed partial class IPCMainProcessServiceImpl : IPCMainProcessService
// 构建要执行的 shell 命令
var shellStr =
$"if [ -x \"{fileName}\" ]; then echo \"文件具有执行权限。\"; else chmod +x \"{fileName}\"; echo \"文件没有执行权限。\"; fi";
//Linux 启动子模块前需要判断是否有 Exec 执行权限
// Linux 启动子模块前需要判断是否有 Exec 执行权限
Process.Start(Process2.BinBash, new string[]
{
"-c",
shellStr
shellStr,
}).WaitForExit();
#endif
@@ -176,8 +176,13 @@ public sealed partial class IPCMainProcessServiceImpl : IPCMainProcessService
psi.ArgumentList.Add(mSubProcessArgumentIndex2Model.Value);
configure?.Invoke(psi);
DotNetRuntimeHelper.AddEnvironment(psi);
if (!string.IsNullOrWhiteSpace(Startup.NativeLibraryPath))
psi.Environment.TryAdd(IPCSubProcessService.EnvKey_NativeLibraryPath, Startup.NativeLibraryPath);
var nativeLibraryPath = Startup.NativeLibraryPath;
if (!string.IsNullOrWhiteSpace(nativeLibraryPath))
{
psi.Environment.TryAdd(
IPCSubProcessService.EnvKey_NativeLibraryPath,
nativeLibraryPath);
}
if (isAdministrator
#if WINDOWS
&& !WindowsPlatformServiceImpl.IsPrivilegedProcess
@@ -1,6 +1,9 @@
#if LINUX
// ReSharper disable once CheckNamespace
using System.Collections;
namespace BD.WTTS.Services.Implementation
{
partial class LinuxPlatformServiceImpl
@@ -39,9 +42,7 @@ namespace BD.WTTS.Services.Implementation
"-c",
$"{Certutil} -A -d sql:$HOME/.pki/nssdb -n \"{CertificateConstants.CertificateName}\" -t C,, -i \"{cerPath}\""
}).WaitForExit();
if (string.IsNullOrWhiteSpace(Environment.ProcessPath))
return false;
return RunRootCommand(PkexecPath, new string[] { Environment.ProcessPath!, "-ceri", CertificateConstants.AppDataDirectory }) == 0;
return RunRootCommand(PkexecPath, new string[] { GetAppHostPath(), "-ceri", CertificateConstants.AppDataDirectory }) == 0;
}
public void RemoveCertificate(byte[] certificate2)
@@ -51,14 +52,20 @@ namespace BD.WTTS.Services.Implementation
"-c",
$"{Certutil} -D -d sql:$HOME/.pki/nssdb -n \"{CertificateConstants.CertificateName}\""
}).WaitForExit();
if (string.IsNullOrWhiteSpace(Environment.ProcessPath))
return;
RunRootCommand(PkexecPath, new string[] { Environment.ProcessPath!, "-cerd", CertificateConstants.AppDataDirectory });
RunRootCommand(PkexecPath, new string[] { GetAppHostPath(), "-cerd", CertificateConstants.AppDataDirectory });
}
public static int RunRootCommand(string path, string[] args)
{
var p = Process.Start(path, args);
var psi = new ProcessStartInfo
{
FileName = path,
UseShellExecute = false,
CreateNoWindow = false
};
args.ForEach(psi.ArgumentList.Add);
DotNetRuntimeHelper.AddEnvironment(psi);
var p = Process.Start(psi);
p.WaitForExit();
return p.ExitCode;
}
@@ -4,6 +4,13 @@ namespace BD.WTTS.Services.Implementation;
partial class LinuxPlatformServiceImpl
{
const string AppHost = "Steam++.sh";
public string GetAppHostPath()
{
return Path.Combine(AppContext.BaseDirectory, AppHost);
}
public ValueTask RunShellAsync(string script, bool requiredAdministrator)
=> UnixHelper.RunShellAsync(script, requiredAdministrator);
}
@@ -4,7 +4,7 @@ namespace BD.WTTS.Services.Implementation;
partial class LinuxPlatformServiceImpl
{
public bool SetAsSystemProxy(bool state, IPAddress? ip, int port)
public Task<bool> SetAsSystemProxyAsync(bool state, IPAddress? ip, int port)
{
var shellContent = new StringBuilder();
if (state)
@@ -25,7 +25,7 @@ partial class LinuxPlatformServiceImpl
}
IPlatformService @this = this;
@this.RunShell(shellContent.ToString(), false);
return true;
return Task.FromResult(true);
}
}
#endif
@@ -4,7 +4,7 @@ namespace BD.WTTS.Services.Implementation;
partial class MacCatalystPlatformServiceImpl
{
public bool SetAsSystemProxy(bool state, IPAddress? ip, int port)
public Task<bool> SetAsSystemProxyAsync(bool state, IPAddress? ip, int port)
{
#if MACOS || MACCATALYST
IPlatformService @this = this;
@@ -16,10 +16,18 @@ partial class MacCatalystPlatformServiceImpl
{
if (state)
{
shellContent.AppendLine($"networksetup -setwebproxy '{item}' '{ip}' {port}");
shellContent.AppendLine($"networksetup -setwebproxystate '{item}' on");
shellContent.AppendLine($"networksetup -setsecurewebproxy '{item}' '{ip}' {port}");
shellContent.AppendLine($"networksetup -setsecurewebproxystate '{item}' on");
if (ip != null)
{
var bindIP = ip?.ToString() == IPAddress.Any.ToString() ? IPAddress.Loopback : ip;
shellContent.AppendLine($"networksetup -setwebproxy '{item}' '{bindIP}' {port}");
shellContent.AppendLine($"networksetup -setwebproxystate '{item}' on");
shellContent.AppendLine($"networksetup -setsecurewebproxy '{item}' '{bindIP}' {port}");
shellContent.AppendLine($"networksetup -setsecurewebproxystate '{item}' on");
}
else
{
return Task.FromResult(false);
}
}
else
{
@@ -29,7 +37,7 @@ partial class MacCatalystPlatformServiceImpl
}
}
@this.RunShell(shellContent.ToString(), false);
return true;
return Task.FromResult(true);
#else
throw new PlatformNotSupportedException();
#endif
@@ -16,6 +16,22 @@ partial class WindowsPlatformServiceImpl : IDisposable
isLightOrDarkThemeWatch?.Dispose();
IOPath.TryDeleteCacheSubDir(CacheTempDirName);
if (IsPrivilegedProcess)
{
IEnumerable<Task> ProxyDispose()
{
if (SetAsSystemProxyStatus)
{
yield return SetAsSystemProxyAsync(false, default, default);
}
if (SetAsSystemPACProxyStatus)
{
yield return SetAsSystemPACProxyAsync(false, default);
}
}
Task.WaitAll(ProxyDispose().ToArray());
}
}
isLightOrDarkThemeWatch = null;
@@ -1,6 +1,7 @@
#if WINDOWS
using PInvoke;
using System.Windows;
using Vanara.PInvoke;
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Services.Implementation;
@@ -68,6 +69,23 @@ partial class WindowsPlatformServiceImpl
MouseHook.OnMouseUp += MouseHook_OnMouseUp;
}
/// <summary>
/// 将传入句柄窗口设置无标题栏和标题栏区域按钮
/// </summary>
/// <param name="window"></param>
public void BeautifyTheWindow(nint hWnd)
{
if (hWnd != nint.Zero)
{
var windowStyle = User32.GetWindowLong(hWnd, User32.WindowLongIndexFlags.GWL_STYLE);
// 移除最大化、关闭和最小化按钮
windowStyle = windowStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX & ~WS_CAPTION;
User32.SetWindowLongPtr(hWnd, User32.WindowLongIndexFlags.GWL_STYLE, windowStyle);
}
}
/// <summary>
/// 将传入窗口设置为无边框窗口化
/// </summary>
@@ -27,41 +27,44 @@ partial class WindowsPlatformServiceImpl : IRegistryService
/// <summary>
/// 带参数(可选/null)启动 %windir%\regedit.exe 并等待退出后删除文件
/// </summary>
/// <param name="path"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StartProcessRegedit(
public static async Task<byte> StartProcessRegeditAsync(
string path,
string contents,
int millisecondsDelay = 3700)
int millisecondsDelay = 3700,
string? markKey = null,
string? markValue = null)
{
File.WriteAllText(path, contents, Encoding.UTF8);
if (IsPrivilegedProcess)
{
// 管理员权限则直接执行
StartProcessRegeditCore(path, millisecondsDelay);
await StartProcessRegeditCoreAsync(path, millisecondsDelay);
return 200;
}
else
{
// 通过 IPC 调用管理员服务进程执行
var ipcPlatformService = IPlatformService.IPCRoot.Instance.GetAwaiter().GetResult();
ipcPlatformService.StartProcessRegeditCoreIPC(path, millisecondsDelay);
var r = await ipcPlatformService.StartProcessRegeditCoreIPCAsync(markKey, markValue, path, millisecondsDelay);
return r;
}
}
void IRegistryService.StartProcessRegedit(
Task<byte> IRegistryService.StartProcessRegeditAsync(
string path,
string contents,
int millisecondsDelay) => StartProcessRegedit(path, contents, millisecondsDelay);
int millisecondsDelay) => StartProcessRegeditAsync(path, contents, millisecondsDelay);
/// <inheritdoc cref="StartProcessRegedit(string, string, int)"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void StartProcessRegeditCore(
internal static async Task StartProcessRegeditCoreAsync(
string path,
int millisecondsDelay = 3700)
{
var args = $"/s \"{path}\"";
var p = Process2.Start(Regedit, args, workingDirectory: _windir.Value);
IOPath.TryDeleteInDelay(p, path, millisecondsDelay, millisecondsDelay);
await IOPath.TryDeleteInDelayAsync(p, path, millisecondsDelay, millisecondsDelay);
}
#region Registry2
@@ -7,17 +7,13 @@ partial class WindowsPlatformServiceImpl
{
const string ProxyOverride = "\"ProxyOverride\"=\"localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*\"";
/// <summary>
/// 设置启用或关闭系统代理
/// </summary>
/// <param name="state"></param>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <returns></returns>
public bool SetAsSystemProxy(bool state, IPAddress? ip, int port)
internal static bool SetAsSystemProxyStatus;
public async Task<bool> SetAsSystemProxyAsync(bool state, IPAddress? ip, int port)
{
try
{
SetAsSystemProxyStatus = state;
var proxyEnable = $"\"ProxyEnable\"=dword:{(state ? "00000001" : "00000000")}";
var hasIpAndProt = ip != null && port >= 0;
var proxyServer = hasIpAndProt ? $"\"proxyServer\"=\"{ip}:{port}\"" : "";
@@ -30,8 +26,8 @@ Windows Registry Editor Version 5.00
{(state ? $"{ProxyOverride}{Environment.NewLine}" : "")}{proxyServer}
""";
var path = IOPath.GetCacheFilePath(CacheTempDirName, "SwitchProxy", FileEx.Reg);
StartProcessRegedit(path, contents);
return true;
var r = await StartProcessRegeditAsync(path, contents, markKey: nameof(SetAsSystemProxyAsync), markValue: state.ToString());
return r == 200;
}
catch (Exception ex)
{
@@ -40,16 +36,13 @@ Windows Registry Editor Version 5.00
}
}
/// <summary>
/// 设置启用或关闭 PAC 代理
/// </summary>
/// <param name="state"></param>
/// <param name="url"></param>
/// <returns></returns>
public bool SetAsSystemPACProxy(bool state, string? url = null)
internal static bool SetAsSystemPACProxyStatus;
public async Task<bool> SetAsSystemPACProxyAsync(bool state, string? url = null)
{
try
{
SetAsSystemPACProxyStatus = state;
var regAutoConfigUrl = state ? $"\"AutoConfigURL\"=\"{url}\"" : "\"AutoConfigURL\"=\"\"";
string contents =
$"""
@@ -59,8 +52,8 @@ Windows Registry Editor Version 5.00
{regAutoConfigUrl}
""";
var path = IOPath.GetCacheFilePath(CacheTempDirName, "SwitchPacProxy", FileEx.Reg);
StartProcessRegedit(path, contents);
return true;
var r = await StartProcessRegeditAsync(path, contents, markKey: nameof(SetAsSystemPACProxyAsync), markValue: state.ToString());
return r == 200;
}
catch (Exception ex)
{
@@ -37,7 +37,8 @@ partial class WindowsPlatformServiceImpl
public const int WS_MAXIMIZEBOX = 0x10000;
public const int WS_MINIMIZEBOX = 0x20000;
public const int WS_CAPTION = 0x00C00000;
public const int WS_BORDER = 0x00800000;
public const int WS_SYSMENU = 0x80000;
public static void EnableMinimizeButton(IntPtr hWnd)
@@ -49,7 +49,7 @@ sealed class SteamServiceImpl2 : SteamServiceBaseImpl, ISteamConnectService
return platform.StartAsInvoker(fileName, arguments);
}
protected sealed override void SetSteamCurrentUser(string userName)
protected sealed override async void SetSteamCurrentUser(string userName)
{
#if WINDOWS
if (DesktopBridge.IsRunningAsUwp)
@@ -63,7 +63,7 @@ Windows Registry Editor Version 5.00
"RememberPassword"=dword:00000001
""";
var path = IOPath.GetCacheFilePath(WindowsPlatformServiceImpl.CacheTempDirName, "SwitchSteamUser", FileEx.Reg);
WindowsPlatformServiceImpl.StartProcessRegedit(path, contents);
await WindowsPlatformServiceImpl.StartProcessRegeditAsync(path, contents);
return;
}
#endif
@@ -1,7 +1,7 @@
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Services;
public interface IProxyService
public interface IProxyService : IDisposable, IAsyncDisposable
{
bool ProxyStatus { get; set; }
}
@@ -45,10 +45,10 @@ public sealed class UserService : ReactiveObject
{
var cUser = await userManager.GetCurrentUserAsync();
if (cUser.HasValue()) return;
//var vm = new LoginOrRegisterWindowViewModel();
//await windowManager.ShowTaskDialogAsync(vm, vm.Title, isOkButton: false);
var vm = new LoginOrRegisterWindowViewModel();
await windowManager.ShowTaskDialogAsync(vm, vm.Title, isOkButton: false);
INavigationService.Instance.Navigate(typeof(LoginOrRegisterWindowViewModel), NavigationTransitionEffect.DrillIn);
//INavigationService.Instance.Navigate(typeof(LoginOrRegisterWindowViewModel), NavigationTransitionEffect.DrillIn);
}
public void NavigateUserCenterPage()
@@ -4,11 +4,11 @@ using AppResources = BD.WTTS.Client.Resources.Strings;
// ReSharper disable once CheckNamespace
namespace BD.WTTS.Services;
public sealed class NotificationService : ReactiveObject
public sealed class NoticeService : ReactiveObject
{
static NotificationService? mCurrent;
static NoticeService? mCurrent;
public static NotificationService Current => mCurrent ?? new();
public static NoticeService Current => mCurrent ?? new();
//readonly INotificationRepository notificationRepo = Ioc.Get<INotificationRepository>();
@@ -22,7 +22,7 @@ public sealed class NotificationService : ReactiveObject
//public NoticeGroupDTO DefaultType { get; } = new NoticeGroupDTO() { Name = AppResources.AllTyoe };
public NotificationService()
public NoticeService()
{
mCurrent = this;
Notices = new ObservableCollection<OfficialMessageItemDTO>();
@@ -53,6 +53,11 @@ public sealed class NotificationService : ReactiveObject
if (item.PushTime > GeneralSettings.LastLookNoticeDateTime.Value)
{
item.Unread = true;
//只弹最新的一条通知
if (UnreadNotificationsCount == 0 && GeneralSettings.MessagePopupNotification)
INotificationService.Instance.Notify(item.Content, NotificationType.Announcement, title: item.Title);
UnreadNotificationsCount++;
}
}
@@ -312,10 +312,10 @@ public sealed class SteamConnectService
InitializeDownloadGameList();
}
//if (!SteamUsers.Lookup(id).HasValue)
//{
// RefreshSteamUsers();
//}
if (!SteamUsers.Lookup(id).HasValue)
{
_ = RefreshSteamUsers();
}
//var mainViewModel = (IWindowService.Instance.MainWindow as WindowViewModel);
//await mainViewModel.SteamAppPage.Initialize();
@@ -489,7 +489,7 @@ public sealed class SteamConnectService
}
}
public async void RefreshSteamUsers()
public async Task RefreshSteamUsers()
{
var list = stmService.GetRememberUserList();
@@ -4,13 +4,50 @@ namespace BD.WTTS.Services;
partial interface IPCPlatformService
{
#if !LIB_CLIENT_IPC
/// <inheritdoc cref="WindowsPlatformServiceImpl.StartProcessRegedit(string, string, int)"/>
/// <inheritdoc cref="WindowsPlatformServiceImpl.StartProcessRegeditAsync(string, string, int)"/>
#endif
[SupportedOSPlatform("windows")]
void StartProcessRegeditCoreIPC(string path, int millisecondsDelay)
{
#if !LIB_CLIENT_IPC && WINDOWS
WindowsPlatformServiceImpl.StartProcessRegeditCore(path, millisecondsDelay);
async
#endif
Task<byte> StartProcessRegeditCoreIPCAsync(
string? markKey,
string? markValue,
string path,
int millisecondsDelay)
{
const byte Code_Ok = 200;
#if !LIB_CLIENT_IPC && WINDOWS
switch (markKey)
{
case nameof(WindowsPlatformServiceImpl.SetAsSystemProxyAsync):
if (markValue == bool.TrueString)
{
WindowsPlatformServiceImpl.SetAsSystemProxyStatus = true;
}
else if (markValue == bool.FalseString)
{
WindowsPlatformServiceImpl.SetAsSystemProxyStatus = false;
}
break;
case nameof(WindowsPlatformServiceImpl.SetAsSystemPACProxyAsync):
if (markValue == bool.TrueString)
{
WindowsPlatformServiceImpl.SetAsSystemPACProxyStatus = true;
}
else if (markValue == bool.FalseString)
{
WindowsPlatformServiceImpl.SetAsSystemPACProxyStatus = false;
}
break;
}
await WindowsPlatformServiceImpl.StartProcessRegeditCoreAsync(path, millisecondsDelay);
return Code_Ok;
#else
return Task.FromResult(Code_Ok);
#endif
}
@@ -13,6 +13,12 @@ partial interface IPlatformService
/// <param name="action">目标窗口回调</param>
void GetMoveMouseDownWindow(Action<NativeWindowModel> action);
/// <summary>
/// 将传入句柄窗口设置无标题栏和标题栏区域按钮
/// </summary>
/// <param name="window"></param>
void BeautifyTheWindow(nint hWnd);
/// <summary>
/// 将传入窗口设置为无边框窗口化
/// </summary>
@@ -7,7 +7,7 @@ partial interface IPlatformService
/// <summary>
/// 设置启用或关闭系统代理
/// </summary>
bool SetAsSystemProxy(bool state, IPAddress? ip = null, int port = -1) => false;
Task<bool> SetAsSystemProxyAsync(bool state, IPAddress? ip = null, int port = -1) => Task.FromResult(false);
/// <summary>
/// 设置启用或关闭 PAC 代理
@@ -15,5 +15,5 @@ partial interface IPlatformService
/// <param name="state"></param>
/// <param name="url"></param>
/// <returns></returns>
bool SetAsSystemPACProxy(bool state, string? url = null) => false;
Task<bool> SetAsSystemPACProxyAsync(bool state, string? url = null) => Task.FromResult(false);
}
@@ -34,7 +34,8 @@ public interface IWindowManager
string? okButtonText = null,
string? retryButtonText = null,
string? moreInfoText = null,
Func<bool>? cancelCloseAction = null)
Func<bool>? cancelCloseAction = null,
bool disableScroll = false)
where TPageViewModel : ViewModelBase;
/// <summary>
@@ -6,7 +6,7 @@ public interface INavigationService
public object? GetViewModelToPageContent(object viewModel, bool isCreateInstance = true);
public void Navigate(Type t, NavigationTransitionEffect effect = NavigationTransitionEffect.None);
public void Navigate(Type? t, NavigationTransitionEffect effect = NavigationTransitionEffect.None);
public void GoBack();
@@ -45,6 +45,11 @@ public partial interface IGeneralSettings
/// </summary>
bool TrayIcon { get; set; }
/// <summary>
/// 启用消息推送通知
/// </summary>
bool MessagePopupNotification { get; set; }
/// <summary>
/// 游戏列表使用本地缓存
/// </summary>
@@ -159,6 +164,11 @@ public partial interface IGeneralSettings
/// </summary>
const bool DefaultTrayIcon = true;
/// <summary>
/// 启用消息推送通知的默认值
/// </summary>
const bool DefaultMessagePopupNotification = true;
/// <summary>
/// 游戏列表使用本地缓存的默认值
/// </summary>
@@ -191,7 +191,7 @@ public partial interface IUISettings
/// <summary>
/// 自定义背景图像路径的默认值
/// </summary>
const string DefaultWindowBackgroundCustomImagePath = "/UI/Assets/back.png";
const string DefaultWindowBackgroundCustomImagePath = "avares://BD.WTTS.Client.Avalonia/UI/Assets/back.png";
/// <summary>
/// 自定义背景图像不透明度的默认值
+29 -17
View File
@@ -74,57 +74,63 @@ public sealed partial class GeneralSettings_ : IGeneralSettings, ISettings, ISet
public bool TrayIcon { get; set; } = IGeneralSettings.DefaultTrayIcon;
/// <summary>
/// 游戏列表使用本地缓存
/// 启用消息推送通知
/// </summary>
[MPKey(5), MP2Key(5), JsonPropertyOrder(5)]
public bool MessagePopupNotification { get; set; } = IGeneralSettings.DefaultMessagePopupNotification;
/// <summary>
/// 游戏列表使用本地缓存
/// </summary>
[MPKey(6), MP2Key(6), JsonPropertyOrder(6)]
public bool GameListUseLocalCache { get; set; } = IGeneralSettings.DefaultGameListUseLocalCache;
/// <summary>
/// 文本阅读器提供商,值为程序路径
/// </summary>
[MPKey(6), MP2Key(6), JsonPropertyOrder(6)]
[MPKey(7), MP2Key(7), JsonPropertyOrder(7)]
public Dictionary<Platform, string>? TextReaderProvider { get; set; } = IGeneralSettings.DefaultTextReaderProvider;
/// <summary>
/// Hosts 文件编码类型
/// </summary>
[MPKey(7), MP2Key(7), JsonPropertyOrder(7)]
[MPKey(8), MP2Key(8), JsonPropertyOrder(8)]
public EncodingType HostsFileEncodingType { get; set; } = IGeneralSettings.DefaultHostsFileEncodingType;
/// <summary>
/// 是否使用硬件加速
/// </summary>
[MPKey(8), MP2Key(8), JsonPropertyOrder(8)]
[MPKey(9), MP2Key(9), JsonPropertyOrder(9)]
public bool GPU { get; set; } = IGeneralSettings.DefaultGPU;
/// <summary>
/// 使用本机 OpenGL
/// </summary>
[MPKey(9), MP2Key(9), JsonPropertyOrder(9)]
[MPKey(10), MP2Key(10), JsonPropertyOrder(10)]
public bool NativeOpenGL { get; set; } = IGeneralSettings.DefaultNativeOpenGL;
/// <summary>
/// 屏幕捕获/允许截图,在一些含有机密的页面上是否允许截图,默认为 <see langword="false"/>
/// </summary>
[MPKey(10), MP2Key(10), JsonPropertyOrder(10)]
[MPKey(11), MP2Key(11), JsonPropertyOrder(11)]
public bool ScreenCapture { get; set; } = IGeneralSettings.DefaultScreenCapture;
/// <summary>
/// 禁用插件
/// </summary>
[MPKey(11), MP2Key(11), JsonPropertyOrder(11)]
[MPKey(12), MP2Key(12), JsonPropertyOrder(12)]
public HashSet<string>? DisablePlugins { get; set; } = IGeneralSettings.DefaultDisablePlugins;
/// <summary>
/// 插件安全模式
/// </summary>
[MPKey(12), MP2Key(12), JsonPropertyOrder(12)]
[MPKey(13), MP2Key(13), JsonPropertyOrder(13)]
public bool PluginSafeMode { get; set; } = IGeneralSettings.DefaultPluginSafeMode;
/// <summary>
/// 最后查看通知时间
/// </summary>
[MPKey(13), MP2Key(13), JsonPropertyOrder(13)]
[MPKey(14), MP2Key(14), JsonPropertyOrder(14)]
public DateTimeOffset LastLookNoticeDateTime { get; set; } = IGeneralSettings.DefaultLastLookNoticeDateTime;
#region 配置应用程序的 Web 代理
@@ -132,49 +138,49 @@ public sealed partial class GeneralSettings_ : IGeneralSettings, ISettings, ISet
/// <summary>
/// Web 代理模式
/// </summary>
[MPKey(14), MP2Key(14), JsonPropertyOrder(14)]
[MPKey(15), MP2Key(15), JsonPropertyOrder(15)]
public AppWebProxyMode WebProxyMode { get; set; } = IGeneralSettings.DefaultWebProxyMode;
/// <summary>
/// Web 自定义代理 Host
/// </summary>
[MPKey(15), MP2Key(15), JsonPropertyOrder(15)]
[MPKey(16), MP2Key(16), JsonPropertyOrder(16)]
public string? CustomWebProxyModeHost { get; set; } = IGeneralSettings.DefaultCustomWebProxyModeHost;
/// <summary>
/// Web 自定义代理 Port
/// </summary>
[MPKey(16), MP2Key(16), JsonPropertyOrder(16)]
[MPKey(17), MP2Key(17), JsonPropertyOrder(17)]
public int CustomWebProxyModePort { get; set; } = IGeneralSettings.DefaultCustomWebProxyModePort;
/// <summary>
/// Web 自定义代理 Address
/// </summary>
[MPKey(17), MP2Key(17), JsonPropertyOrder(17)]
[MPKey(18), MP2Key(18), JsonPropertyOrder(18)]
public string? CustomWebProxyModeAddress { get; set; } = IGeneralSettings.DefaultCustomWebProxyModeAddress;
/// <summary>
/// Web 自定义代理 BypassOnLocal
/// </summary>
[MPKey(18), MP2Key(18), JsonPropertyOrder(18)]
[MPKey(19), MP2Key(19), JsonPropertyOrder(19)]
public bool CustomWebProxyModeBypassOnLocal { get; set; } = IGeneralSettings.DefaultCustomWebProxyModeBypassOnLocal;
/// <summary>
/// Web 自定义代理 NetworkCredential.UserName
/// </summary>
[MPKey(19), MP2Key(19), JsonPropertyOrder(19)]
[MPKey(20), MP2Key(20), JsonPropertyOrder(20)]
public string? CustomWebProxyModeCredentialUserName { get; set; } = IGeneralSettings.DefaultCustomWebProxyModeCredentialUserName;
/// <summary>
/// Web 自定义代理 NetworkCredential.Password
/// </summary>
[MPKey(20), MP2Key(20), JsonPropertyOrder(20)]
[MPKey(21), MP2Key(21), JsonPropertyOrder(21)]
public string? CustomWebProxyModeCredentialPassword { get; set; } = IGeneralSettings.DefaultCustomWebProxyModeCredentialPassword;
/// <summary>
/// Web 自定义代理 NetworkCredential.Domain
/// </summary>
[MPKey(21), MP2Key(21), JsonPropertyOrder(21)]
[MPKey(22), MP2Key(22), JsonPropertyOrder(22)]
public string? CustomWebProxyModeCredentialDomain { get; set; } = IGeneralSettings.DefaultCustomWebProxyModeCredentialDomain;
#endregion
@@ -213,6 +219,12 @@ public static partial class GeneralSettings
public static SettingsStructProperty<bool, GeneralSettings_> TrayIcon { get; }
= new(DefaultTrayIcon);
/// <summary>
/// 启用消息推送通知
/// </summary>
public static SettingsStructProperty<bool, GeneralSettings_> MessagePopupNotification { get; }
= new(DefaultMessagePopupNotification);
/// <summary>
/// 游戏列表使用本地缓存
/// </summary>
@@ -66,10 +66,6 @@ public class SettingsStructPropertyBase<TValue, [DynamicallyAccessedMembers(Dyna
this.value = value; // 赋值当前字段
var setter_value = value;
if (EqualityComparer<TValue>.Default.Equals(value, Default))
{
setter_value = default;
}
setter(monitor.CurrentValue, setter_value); // 赋值模型类属性
OnValueChanged(oldValue, value); // 调用变更事件
@@ -48,7 +48,7 @@ partial class Startup // 自定义控制台命令参数
devtools.AddOption(new Option<bool>("-disable_gpu", () => false, "禁用 GPU 硬件加速"));
devtools.AddOption(new Option<bool>("-use_wgl", () => false, "使用 Native OpenGL(仅 Windows)"));
devtools.AddOption(new Option<bool>("-use_localhost", () => false, "使用本机服务端"));
devtools.AddOption(new Option<bool>("-steamrun", () => true, "Steam 内启动"));
devtools.AddOption(new Option<bool>("-steamrun", () => false, "Steam 内启动"));
devtools.Handler = CommandHandler.Create((bool disable_gpu, bool use_wgl, bool use_localhost, bool steamrun) =>
{
#if DEBUG
@@ -152,8 +152,10 @@ partial class Startup // 自定义控制台命令参数
RunUIApplication(AppServicesLevel.Steam);
await WaitConfiguredServices;
SteamConnectService.Current.Initialize(id);
await new TaskCompletionSource().Task;
if (SteamConnectService.Current.Initialize(id))
{
await new TaskCompletionSource().Task;
}
}
return exitCode;
@@ -9,8 +9,14 @@ partial class Startup // OnStartup
}
protected static void ShowSettingsModifiedRestartThisSoft()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ShowSettingsModifiedRestartThisSoft()
{
if (Ioc.Get_Nullable<IToastIntercept>() is StartupToastIntercept intercept
&& !intercept.IsStartuped)
{
return;
}
Toast.Show(ToastIcon.Info, Strings.SettingsModifiedRestartThisSoft);
}

Some files were not shown because too many files have changed in this diff Show More