mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-09-16 13:29:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0203eef006 | ||
|
|
4ab50ee191 | ||
|
|
06c1ec1ac2 | ||
|
|
ef4befdc38 | ||
|
|
33f49ddffa | ||
|
|
49aa249b5b | ||
|
|
c387d44890 | ||
|
|
d4d09e7267 | ||
|
|
dd7bf02a97 | ||
|
|
6d0eeb478f | ||
|
|
d4e9de48b1 | ||
|
|
6bd9877925 | ||
|
|
d38df6596f | ||
|
|
b4e31e859c | ||
|
|
1acd07c1be | ||
|
|
b9b25bb098 | ||
|
|
5ea2981dc7 | ||
|
|
4fb96ca3a4 | ||
|
|
6de52f5585 | ||
|
|
6622e41f64 | ||
|
|
8405e4d1e5 | ||
|
|
0edc3f1560 | ||
|
|
c109fe7561 | ||
|
|
b5413dca5c | ||
|
|
6c2d4a1e70 | ||
|
|
be03f1a32d | ||
|
|
3ed1cc453a | ||
|
|
16030063ed | ||
|
|
458696ad8f | ||
|
|
6201702563 | ||
|
|
c5bea23a8f | ||
|
|
e5f38f77ef |
@@ -0,0 +1,96 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT license.
|
||||
|
||||
# This continuous integration pipeline is triggered anytime a user pushes code to the repo.
|
||||
# This pipeline builds the Wpf project, runs unit tests, then saves the MSIX build artifact.
|
||||
name: Daybreak CD Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
targetplatform: [x64]
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
env:
|
||||
Configuration: Release
|
||||
Solution_Path: Daybreak.sln
|
||||
Test_Project_Path: Daybreak.Tests\Daybreak.Tests.csproj
|
||||
Wpf_Project_Path: Daybreak\Daybreal.csproj
|
||||
Actions_Allow_Unsecure_Commands: true
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get Latest Tag
|
||||
id: getLatestTag
|
||||
uses: WyriHaximus/github-action-get-previous-tag@v1
|
||||
|
||||
- name: Generate changelog
|
||||
id: gen_changelog
|
||||
run: |
|
||||
$changeLog = git log --no-merges --pretty="%h - %s (%an)<br />" ${{ env.LatestReleaseTag }}..HEAD
|
||||
echo "::set-env name=Changelog::$changeLog"
|
||||
env:
|
||||
LatestReleaseTag: ${{steps.getLatestTag.outputs.tag}}
|
||||
|
||||
- name: Print changelog
|
||||
run: |
|
||||
echo "${{ env.Changelog }}"
|
||||
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.202'
|
||||
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.0.1
|
||||
|
||||
- name: Restore project
|
||||
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
|
||||
env:
|
||||
RuntimeIdentifier: win-${{ matrix.targetplatform }}
|
||||
|
||||
- name: Build Daybreak project
|
||||
run: dotnet build Daybreak -c $env:Configuration
|
||||
|
||||
- name: Set version variable
|
||||
run: |
|
||||
$version = .\Scripts\GetBuildVersion.ps1
|
||||
echo "::set-env name=Version::$version"
|
||||
|
||||
- name: Create publish files
|
||||
run: dotnet publish .\Daybreak\Daybreak.csproj -c $env:Configuration -r $env:RuntimeIdentifier -p:PublishReadyToRun=true -p:PublishSingleFile=true --self-contained true -o .\Publish
|
||||
env:
|
||||
RuntimeIdentifier: win-${{ matrix.targetplatform }}
|
||||
|
||||
- name: Pack publish files
|
||||
run: |
|
||||
Write-Host $env
|
||||
.\Scripts\BuildRelease.ps1 -version $env:Version
|
||||
shell: pwsh
|
||||
|
||||
- name: Publish release
|
||||
uses: Xotl/cool-github-releases@v1.1.2
|
||||
with:
|
||||
mode: update
|
||||
tag_name: v${{ env.Version }}
|
||||
release_name: Daybreak v${{ env.Version }}
|
||||
assets: .\Publish\daybreakv${{ env.Version }}.zip
|
||||
github_token: ${{ env.GITHUB_TOKEN }}
|
||||
replace_assets: true
|
||||
body_mrkdwn: ${{ env.Changelog }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT license.
|
||||
|
||||
# This continuous integration pipeline is triggered anytime a user pushes code to the repo.
|
||||
# This pipeline builds the Wpf project, runs unit tests, then saves the MSIX build artifact.
|
||||
name: Daybreak CI Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
targetplatform: [x64]
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
env:
|
||||
Solution_Path: Daybreak.sln
|
||||
Test_Project_Path: Daybreak.Tests\Daybreak.Tests.csproj
|
||||
Wpf_Project_Path: Daybreak\Daybreal.csproj
|
||||
Actions_Allow_Unsecure_Commands: true
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.202'
|
||||
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.0.1
|
||||
|
||||
- name: Execute Unit Tests
|
||||
run: dotnet test $env:Test_Project_Path
|
||||
|
||||
- name: Restore the Wpf application to populate the obj folder
|
||||
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
|
||||
env:
|
||||
Configuration: Debug
|
||||
RuntimeIdentifier: win-${{ matrix.targetplatform }}
|
||||
@@ -9,6 +9,7 @@
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
*.version
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.3.0" />
|
||||
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
using Daybreak.Models.Versioning;
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Version = Daybreak.Models.Versioning.Version;
|
||||
|
||||
namespace Daybreak.Tests.Models
|
||||
{
|
||||
[TestClass]
|
||||
public class VersionTests
|
||||
{
|
||||
[DataRow("v0.1.0", "v0.1")]
|
||||
[DataRow("0.1.0", "0.1")]
|
||||
[DataRow("v0.1.0.0.0", "v0.1")]
|
||||
[DataRow("0.1.0.0.0", "0.1")]
|
||||
[DataRow("v0", "v0")]
|
||||
[DataRow("0", "0")]
|
||||
[DataRow("v0.1", "v0.1")]
|
||||
[DataRow("0.1-release", "0.1-release")]
|
||||
[DataRow("v0.1-release", "v0.1-release")]
|
||||
[TestMethod]
|
||||
public void VersionToString(string version, string expectedString)
|
||||
{
|
||||
var parsedVersion = Version.Parse(version);
|
||||
parsedVersion.ToString().Should().Be(expectedString);
|
||||
}
|
||||
|
||||
[DataRow("v0", false)]
|
||||
[DataRow("0", false)]
|
||||
[DataRow("v0.1", false)]
|
||||
[DataRow("0.1-release", true)]
|
||||
[DataRow("v0.1-release", true)]
|
||||
[TestMethod]
|
||||
public void VersionHasStringSuffix(string version, bool hasSuffix)
|
||||
{
|
||||
var parsedVersion = Version.Parse(version);
|
||||
if (hasSuffix)
|
||||
{
|
||||
parsedVersion.VersionTokens.Last().Should().BeOfType<VersionStringToken>();
|
||||
}
|
||||
else
|
||||
{
|
||||
parsedVersion.VersionTokens.Last().Should().BeOfType<VersionNumberToken>();
|
||||
}
|
||||
}
|
||||
|
||||
[DataRow("v0", true)]
|
||||
[DataRow("0", false)]
|
||||
[DataRow("v0.1", true)]
|
||||
[DataRow("0.1", false)]
|
||||
[TestMethod]
|
||||
public void VersionTokensHasPrefix(string version, bool hasPrefix)
|
||||
{
|
||||
var parsedVersion = Version.Parse(version);
|
||||
parsedVersion.HasPrefix.Should().Be(hasPrefix);
|
||||
}
|
||||
|
||||
[DataRow(null, false)]
|
||||
[DataRow("", false)]
|
||||
[DataRow("asd", false)]
|
||||
[DataRow("ads.pls.er", false)]
|
||||
[DataRow("0.0.1", true)]
|
||||
[DataRow("1", true)]
|
||||
[DataRow("0.1.asd", false)]
|
||||
[DataRow("0.", false)]
|
||||
[DataRow(".1", false)]
|
||||
[DataRow("0.1-", false)]
|
||||
[DataRow("0.1-release", true)]
|
||||
[DataRow("0..1", false)]
|
||||
[TestMethod]
|
||||
public void TryParseVersionTest(string version, bool result)
|
||||
{
|
||||
var success = Version.TryParse(version, out var parsedVersion);
|
||||
success.Should().Be(result);
|
||||
if (success is true)
|
||||
{
|
||||
parsedVersion.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[DataRow(null, false)]
|
||||
[DataRow("", false)]
|
||||
[DataRow("asd", false)]
|
||||
[DataRow("ads.pls.er", false)]
|
||||
[DataRow("0.0.1", true)]
|
||||
[DataRow("1", true)]
|
||||
[DataRow("0.1.asd", false)]
|
||||
[DataRow("0.", false)]
|
||||
[DataRow(".1", false)]
|
||||
[DataRow("0.1-", false)]
|
||||
[DataRow("0.1-release", true)]
|
||||
[DataRow("0..1", false)]
|
||||
[TestMethod]
|
||||
public void ParseVersionTest(string version, bool shouldNotThrow)
|
||||
{
|
||||
var action = new Action(() => Version.Parse(version));
|
||||
if (shouldNotThrow is false)
|
||||
{
|
||||
action.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
else
|
||||
{
|
||||
action.Should().NotThrow<InvalidOperationException>();
|
||||
}
|
||||
}
|
||||
|
||||
[DataRow(null, false)]
|
||||
[DataRow("", false)]
|
||||
[DataRow("asd", false)]
|
||||
[DataRow("ads.pls.er", false)]
|
||||
[DataRow("0.0.1", true)]
|
||||
[DataRow("1", true)]
|
||||
[DataRow("0.1.asd", false)]
|
||||
[DataRow("0.", false)]
|
||||
[DataRow(".1", false)]
|
||||
[DataRow("0.1-", false)]
|
||||
[DataRow("0.1-release", true)]
|
||||
[DataRow("0..1", false)]
|
||||
[TestMethod]
|
||||
public void VersionConstructorTest(string version, bool shouldNotThrow)
|
||||
{
|
||||
var action = new Action(() => new Version(version));
|
||||
if (shouldNotThrow is false)
|
||||
{
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
else
|
||||
{
|
||||
action.Should().NotThrow<ArgumentException>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Services.BuildTemplates;
|
||||
using Daybreak.Services.Logging;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using System.Collections.Generic;
|
||||
@@ -17,7 +17,7 @@ namespace Daybreak.Tests.Services
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
buildTemplateManager = new BuildTemplateManager(new Mock<ILogger>().Object);
|
||||
buildTemplateManager = new BuildTemplateManager(new Mock<ILogger<BuildTemplateManager>>().Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using Daybreak.Services.Logging;
|
||||
using FluentAssertions;
|
||||
using LiteDB;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.IO;
|
||||
|
||||
namespace Daybreak.Tests.Services
|
||||
{
|
||||
[TestClass]
|
||||
public class JsonLoggerProviderTests
|
||||
{
|
||||
private ILogsManager logsManager;
|
||||
private ILoggerProvider loggerProvider;
|
||||
private ILiteDatabase liteDatabase;
|
||||
|
||||
[TestInitialize]
|
||||
public void InitializeProvider()
|
||||
{
|
||||
File.Delete("Daybreak.db");
|
||||
this.liteDatabase = new LiteDatabase("Daybreak.db");
|
||||
this.logsManager = new JsonLogsManager(this.liteDatabase);
|
||||
this.loggerProvider = new JsonLoggerProvider(this.logsManager);
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void TestCleanup()
|
||||
{
|
||||
this.liteDatabase.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateLoggerReturnsLogger()
|
||||
{
|
||||
var logger = this.loggerProvider.CreateLogger("SomeCategory");
|
||||
logger.Should().NotBeNull();
|
||||
}
|
||||
[TestMethod]
|
||||
public void LoggerLogsAndReaderReads()
|
||||
{
|
||||
var logger = this.loggerProvider.CreateLogger("SomeCategory");
|
||||
logger.LogInformation("Logging some stuff");
|
||||
logger.LogError("Logging some error");
|
||||
|
||||
this.logsManager.GetLogs().Should().HaveCount(2);
|
||||
}
|
||||
[TestMethod]
|
||||
public void DeletingLogsShouldDeleteLogs()
|
||||
{
|
||||
var logger = this.loggerProvider.CreateLogger("SomeCategory");
|
||||
logger.LogInformation("Logging some stuff");
|
||||
logger.LogError("Logging some error");
|
||||
|
||||
this.logsManager.GetLogs().Should().HaveCount(2);
|
||||
|
||||
this.logsManager.DeleteLogs();
|
||||
this.logsManager.GetLogs().Should().HaveCount(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
using Daybreak.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Configuration
|
||||
{
|
||||
public sealed class ApplicationConfiguration
|
||||
{
|
||||
[JsonProperty("SetGuildwarsWindowSizeOnLaunch")]
|
||||
public bool SetGuildwarsWindowSizeOnLaunch { get; set; }
|
||||
[JsonProperty("DesiredGuildwarsScreen")]
|
||||
public int DesiredGuildwarsScreen { get; set; }
|
||||
[JsonProperty("BrowsersEnabled")]
|
||||
public bool BrowsersEnabled { get; set; } = true;
|
||||
[JsonProperty("ToolboxPath")]
|
||||
@@ -26,5 +31,11 @@ namespace Daybreak.Configuration
|
||||
public bool AddressBarReadonly { get; set; } = true;
|
||||
[JsonProperty("ExperimentalFeatures")]
|
||||
public ExperimentalFeatures ExperimentalFeatures { get; set; } = new();
|
||||
[JsonProperty("ShortcutLocation")]
|
||||
public string ShortcutLocation { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
[JsonProperty("PlaceShortcut")]
|
||||
public bool PlaceShortcut { get; set; }
|
||||
[JsonProperty("AutoCheckUpdate")]
|
||||
public bool AutoCheckUpdate { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Newtonsoft.Json;
|
||||
using Daybreak.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Configuration
|
||||
{
|
||||
@@ -10,5 +12,11 @@ namespace Daybreak.Configuration
|
||||
public int ToolboxAutoLaunchDelay { get; set; } = 5000;
|
||||
[JsonProperty("DynamicBuildLoading")]
|
||||
public bool DynamicBuildLoading { get; set; } = true;
|
||||
[JsonProperty("LaunchGuildwarsAsCurrentUser")]
|
||||
public bool LaunchGuildwarsAsCurrentUser { get; set; } = true;
|
||||
[JsonProperty("CanInterceptKeys")]
|
||||
public bool CanInterceptKeys { get; set; }
|
||||
[JsonProperty("Macros")]
|
||||
public List<KeyMacro> Macros { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,25 +7,53 @@ using Daybreak.Services.Credentials;
|
||||
using Daybreak.Services.IconRetrieve;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.Mutex;
|
||||
using Daybreak.Services.Privilege;
|
||||
using Daybreak.Services.Runtime;
|
||||
using Daybreak.Services.Screens;
|
||||
using Daybreak.Services.Screenshots;
|
||||
using Daybreak.Services.Shortcuts;
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Views;
|
||||
using Microsoft.Extensions.Http.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Slim;
|
||||
using System.Extensions;
|
||||
using System.Net.Http;
|
||||
using System.Windows.Extensions.Http;
|
||||
using System.Windows.Extensions;
|
||||
using LiteDB;
|
||||
|
||||
namespace Daybreak.Configuration
|
||||
{
|
||||
public static class ProjectConfiguration
|
||||
{
|
||||
public static void RegisterResolvers(IServiceManager serviceManager)
|
||||
{
|
||||
serviceManager.ThrowIfNull(nameof(serviceManager));
|
||||
|
||||
serviceManager.RegisterLoggerFactory((sp) =>
|
||||
{
|
||||
var factory = new LoggerFactory();
|
||||
factory.AddProvider(new JsonLoggerProvider(sp.GetService<ILogsManager>()));
|
||||
return factory;
|
||||
});
|
||||
serviceManager.RegisterResolver(
|
||||
new HttpClientResolver()
|
||||
.WithHttpMessageHandlerFactory((serviceProvider, categoryType) =>
|
||||
{
|
||||
var loggerType = typeof(ILogger<>).MakeGenericType(categoryType);
|
||||
var logger = serviceProvider.GetService(loggerType).As<ILogger>();
|
||||
var handler = new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
|
||||
return handler;
|
||||
}));
|
||||
}
|
||||
|
||||
public static void RegisterServices(IServiceProducer serviceProducer)
|
||||
{
|
||||
serviceProducer.ThrowIfNull(nameof(serviceProducer));
|
||||
|
||||
serviceProducer.RegisterSingleton<ICredentialManager, CredentialManager>();
|
||||
serviceProducer.RegisterSingleton<ILoggingDatabase, FlatLoggingDatabase>();
|
||||
serviceProducer.RegisterSingleton<ILogger, Logger>();
|
||||
serviceProducer.RegisterSingleton<ApplicationLifetimeManager>();
|
||||
serviceProducer.RegisterSingleton<ViewManager>();
|
||||
serviceProducer.RegisterSingleton<IApplicationLauncher, ApplicationLauncher>();
|
||||
@@ -37,14 +65,19 @@ namespace Daybreak.Configuration
|
||||
serviceProducer.RegisterSingleton<IRuntimeStore, RuntimeStore>();
|
||||
serviceProducer.RegisterSingleton<IBuildTemplateManager, BuildTemplateManager>();
|
||||
serviceProducer.RegisterSingleton<IIconRetriever, IconRetriever>();
|
||||
serviceProducer.RegisterSingleton<IPrivilegeManager, PrivilegeManager>();
|
||||
serviceProducer.RegisterSingleton<IScreenManager, ScreenManager>();
|
||||
serviceProducer.RegisterSingleton<IShortcutManager, ShortcutManager>();
|
||||
serviceProducer.RegisterSingleton<ILogsManager, JsonLogsManager>();
|
||||
serviceProducer.RegisterSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
|
||||
}
|
||||
public static void RegisterLifetimeServices(IApplicationLifetimeProducer applicationLifetimeProducer)
|
||||
{
|
||||
applicationLifetimeProducer.ThrowIfNull(nameof(applicationLifetimeProducer));
|
||||
|
||||
applicationLifetimeProducer.RegisterService<ILoggingDatabase>();
|
||||
applicationLifetimeProducer.RegisterService<IScreenshotProvider>();
|
||||
applicationLifetimeProducer.RegisterService<IApplicationUpdater>();
|
||||
applicationLifetimeProducer.RegisterService<IShortcutManager>();
|
||||
}
|
||||
public static void RegisterViews(IViewProducer viewProducer)
|
||||
{
|
||||
@@ -60,6 +93,10 @@ namespace Daybreak.Configuration
|
||||
viewProducer.RegisterView<ExecutablesView>();
|
||||
viewProducer.RegisterView<BuildTemplateView>();
|
||||
viewProducer.RegisterView<BuildsListView>();
|
||||
viewProducer.RegisterView<RequestElevationView>();
|
||||
viewProducer.RegisterView<ScreenChoiceView>();
|
||||
viewProducer.RegisterView<VersionManagementView>();
|
||||
viewProducer.RegisterView<LogsView>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<UserControl x:Class="Daybreak.Controls.HelpButton"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Viewbox>
|
||||
<Grid>
|
||||
<Ellipse x:Name="BackgroundEllipse" Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Visibility="Visible" Opacity="0.1" Width="30" Height="30" />
|
||||
<Ellipse Width="30" Height="30" StrokeThickness="2" Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></Ellipse>
|
||||
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Data="m4.99353,17.899l0,-1.47c0,-1.436 0.322,-2.188 1.075,-3.229l2.404,-3.3c1.254,-1.721 1.684,-2.546 1.684,-3.766c0,-2.044 -1.434,-3.335 -3.479,-3.335c-2.008,0 -3.299,1.219 -3.729,3.407c-0.036,0.215 -0.179,0.323 -0.395,0.287l-2.259,-0.395c-0.216,-0.036 -0.323,-0.179 -0.288,-0.395c0.539,-3.443 3.014,-5.703 6.744,-5.703c3.872,0 6.49,2.546 6.49,6.097c0,1.722 -0.608,2.977 -1.828,4.663l-2.403,3.3c-0.717,0.968 -0.933,1.47 -0.933,2.689l0,1.147c0,0.215 -0.143,0.358 -0.358,0.358l-2.367,0c-0.215,0.004 -0.358,-0.14 -0.358,-0.355zm-0.179,3.444c0,-0.215 0.143,-0.358 0.359,-0.358l2.726,0c0.215,0 0.358,0.144 0.358,0.358l0,3.084c0,0.216 -0.144,0.358 -0.358,0.358l-2.726,0c-0.217,0 -0.359,-0.143 -0.359,-0.358l0,-3.084z"></Path>
|
||||
<Ellipse Fill="Transparent" MouseEnter="Ellipse_MouseEnter" MouseLeave="Ellipse_MouseLeave" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"></Ellipse>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Daybreak.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for AddButton.xaml
|
||||
/// </summary>
|
||||
public partial class HelpButton : UserControl
|
||||
{
|
||||
public event EventHandler Clicked;
|
||||
|
||||
public HelpButton()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Ellipse_MouseEnter(object sender, MouseEventArgs e)
|
||||
{
|
||||
this.BackgroundEllipse.Opacity = 0.6;
|
||||
}
|
||||
|
||||
private void Ellipse_MouseLeave(object sender, MouseEventArgs e)
|
||||
{
|
||||
this.BackgroundEllipse.Opacity = 0;
|
||||
}
|
||||
|
||||
private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ChangedButton == MouseButton.Left)
|
||||
{
|
||||
this.Clicked?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ using Daybreak.Models.Browser;
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Services.BuildTemplates;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
@@ -19,79 +19,44 @@ namespace Daybreak.Controls
|
||||
/// <summary>
|
||||
/// Interaction logic for ChromiumBrowserWrapper.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class ChromiumBrowserWrapper : UserControl
|
||||
{
|
||||
private const string BrowserDownloadLink = "https://developer.microsoft.com/en-us/microsoft-edge/webview2/";
|
||||
|
||||
public readonly static DependencyProperty AddressProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, string>(nameof(Address));
|
||||
public readonly static DependencyProperty FavoriteAddressProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, string>(nameof(FavoriteAddress));
|
||||
public readonly static DependencyProperty NavigatingProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(Navigating));
|
||||
public readonly static DependencyProperty BrowserEnabledProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(BrowserEnabled));
|
||||
public readonly static DependencyProperty AddressBarReadonlyProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(AddressBarReadonly));
|
||||
public readonly static DependencyProperty BrowserSupportedProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(BrowserSupported), new PropertyMetadata(true));
|
||||
public readonly static DependencyProperty ControlsEnabledProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(ControlsEnabled), new PropertyMetadata(true));
|
||||
public readonly static DependencyProperty CanNavigateProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(CanNavigate), new PropertyMetadata(true));
|
||||
public readonly static DependencyProperty CanDownloadBuildProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(CanDownloadBuild), new PropertyMetadata(false));
|
||||
|
||||
public event EventHandler<string> FavoriteUriChanged;
|
||||
public event EventHandler MaximizeClicked;
|
||||
public event EventHandler<Build> BuildDecoded;
|
||||
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<ChromiumBrowserWrapper> logger;
|
||||
private readonly IBuildTemplateManager buildTemplateManager;
|
||||
private CoreWebView2Environment coreWebView2Environment;
|
||||
|
||||
public bool CanDownloadBuild
|
||||
{
|
||||
get => this.GetTypedValue<bool>(CanDownloadBuildProperty);
|
||||
set => this.SetTypedValue<bool>(CanDownloadBuildProperty, value);
|
||||
}
|
||||
public bool CanNavigate
|
||||
{
|
||||
get => this.GetTypedValue<bool>(CanNavigateProperty);
|
||||
set => this.SetTypedValue<bool>(CanNavigateProperty, value);
|
||||
}
|
||||
public string Address
|
||||
{
|
||||
get => this.GetTypedValue<string>(AddressProperty);
|
||||
set => this.SetTypedValue(AddressProperty, value);
|
||||
}
|
||||
public string FavoriteAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(FavoriteAddressProperty);
|
||||
set => this.SetTypedValue(FavoriteAddressProperty, value);
|
||||
}
|
||||
public bool Navigating
|
||||
{
|
||||
get => this.GetTypedValue<bool>(NavigatingProperty);
|
||||
private set => this.SetTypedValue<bool>(NavigatingProperty, value);
|
||||
}
|
||||
public bool AddressBarReadonly
|
||||
{
|
||||
get => this.GetTypedValue<bool>(AddressBarReadonlyProperty);
|
||||
set => this.SetTypedValue<bool>(AddressBarReadonlyProperty, value);
|
||||
}
|
||||
public bool BrowserEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(BrowserEnabledProperty);
|
||||
private set => this.SetTypedValue<bool>(BrowserEnabledProperty, value);
|
||||
}
|
||||
public bool BrowserSupported
|
||||
{
|
||||
get => this.GetTypedValue<bool>(BrowserSupportedProperty);
|
||||
private set => this.SetTypedValue<bool>(BrowserSupportedProperty, value);
|
||||
}
|
||||
public bool ControlsEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(ControlsEnabledProperty);
|
||||
set => this.SetTypedValue<bool>(ControlsEnabledProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool canDownloadBuild;
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool canNavigate;
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool controlsEnabled;
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool browserSupported;
|
||||
[GenerateDependencyProperty]
|
||||
private bool addressBarReadonly;
|
||||
[GenerateDependencyProperty]
|
||||
private bool browserEnabled;
|
||||
[GenerateDependencyProperty]
|
||||
private bool navigating;
|
||||
[GenerateDependencyProperty]
|
||||
private string favoriteAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private string address;
|
||||
|
||||
public ChromiumBrowserWrapper()
|
||||
{
|
||||
this.configurationManager = Launcher.ApplicationServiceManager.GetService<IConfigurationManager>();
|
||||
this.logger = Launcher.ApplicationServiceManager.GetService<ILogger>();
|
||||
this.logger = Launcher.ApplicationServiceManager.GetService<ILogger<ChromiumBrowserWrapper>>();
|
||||
this.buildTemplateManager = Launcher.ApplicationServiceManager.GetService<IBuildTemplateManager>();
|
||||
this.InitializeComponent();
|
||||
this.InitializeEnvironment();
|
||||
@@ -209,7 +174,7 @@ namespace Daybreak.Controls
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
this.logger.LogError(e);
|
||||
this.logger.LogError(e, $"Exception encountered when deserializing {nameof(BrowserPayload)}");
|
||||
}
|
||||
if (payload?.Key == BrowserPayload.PayloadKeys.ContextMenu)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<UserControl x:Class="Daybreak.Controls.LogsGlyph"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Viewbox>
|
||||
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Data="m7,3l0,-1.00209c0,-1.09965 0.89762,-1.99791 2.00488,-1.99791l0.99024,0c1.11098,0 2.00488,0.8945 2.00488,1.99791l0,1.00209l2.00442,0c0.55074,0 0.99558,0.44725 0.99558,0.99896l0,1.00208c0,0.5563 -0.44574,0.99896 -0.99558,0.99896l-9.00884,0c-0.55074,0 -0.99558,-0.44725 -0.99558,-0.99896l0,-1.00208c0,-0.5563 0.44574,-0.99896 0.99558,-0.99896l2.00442,0l0,0zm8.99999,1l1.00259,0c1.10649,0 1.99742,0.89704 1.99742,2.00359l0,20.99282c0,1.11383 -0.89428,2.00359 -1.99742,2.00359l-15.00516,0c-1.10649,0 -1.99742,-0.89704 -1.99742,-2.00359l0,-20.99282c0,-1.11383 0.89428,-2.00359 1.99742,-2.00359l1.00259,0c-0.00001,0.00163 -0.00001,0.00325 -0.00001,0.00488l0,0.99024c0,1.10726 0.89354,2.00488 2.00276,2.00488l8.99448,0c1.10609,0 2.00276,-0.8939 2.00276,-2.00488l0,-0.99024c0,-0.00163 0,-0.00325 -0.00001,-0.00488l0,0l0,0zm-6.49999,-1c0.27614,0 0.5,-0.22386 0.5,-0.5c0,-0.27614 -0.22386,-0.5 -0.5,-0.5c-0.27614,0 -0.5,0.22386 -0.5,0.5c0,0.27614 0.22386,0.5 0.5,0.5l0,0zm-6.5,8l0,1l13,0l0,-1l-13,0l0,0zm0,3l0,1l13,0l0,-1l-13,0l0,0zm0,3l0,1l13,0l0,-1l-13,0l0,0zm0,3l0,1l13,0l0,-1l-13,0l0,0zm0,3l0,1l13,0l0,-1l-13,0l0,0z"></Path>
|
||||
</Viewbox>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LogsGlyph.xaml
|
||||
/// </summary>
|
||||
public partial class LogsGlyph : UserControl
|
||||
{
|
||||
public LogsGlyph()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<UserControl x:Class="Daybreak.Controls.StaticGlyph"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Viewbox>
|
||||
<Polygon Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Points="209.89999389648438,829.800048828125 279.1999816894531,493.9000244140625 0,493.9000244140625 415.89996337890625,0 346.699951171875,335.8999938964844 625.800048828125,335.8999938964844"></Polygon>
|
||||
</Viewbox>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for StaticGlyph.xaml
|
||||
/// </summary>
|
||||
public partial class StaticGlyph : UserControl
|
||||
{
|
||||
public StaticGlyph()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Daybreak.Utils;
|
||||
using System;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
@@ -39,7 +38,7 @@ namespace Daybreak.Controls
|
||||
}
|
||||
|
||||
nextVisible.Source = imageSource;
|
||||
this.Transition(currentVisible, nextVisible);
|
||||
Transition(currentVisible, nextVisible);
|
||||
}
|
||||
|
||||
private Image CurrentVisible()
|
||||
@@ -64,7 +63,7 @@ namespace Daybreak.Controls
|
||||
return this.Image1;
|
||||
}
|
||||
}
|
||||
private void Transition(Image from, Image to)
|
||||
private static void Transition(Image from, Image to)
|
||||
{
|
||||
to.Visibility = Visibility.Visible;
|
||||
to.Opacity = 0;
|
||||
|
||||
@@ -10,37 +10,21 @@ namespace Daybreak.Controls
|
||||
/// <summary>
|
||||
/// Interaction logic for AccountTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class AccountTemplate : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty UsernameProperty = DependencyPropertyExtensions.Register<AccountTemplate, string>(nameof(Username));
|
||||
public static readonly DependencyProperty CharacterNameProperty = DependencyPropertyExtensions.Register<AccountTemplate, string>(nameof(CharacterName));
|
||||
public static readonly DependencyProperty PasswordProperty = DependencyPropertyExtensions.Register<AccountTemplate, string>(nameof(Password));
|
||||
public static readonly DependencyProperty IsDefaultProperty = DependencyPropertyExtensions.Register<AccountTemplate, bool>(nameof(IsDefault));
|
||||
|
||||
|
||||
public event EventHandler RemoveClicked;
|
||||
public event EventHandler DefaultClicked;
|
||||
|
||||
public string Username
|
||||
{
|
||||
get => this.GetTypedValue<string>(UsernameProperty);
|
||||
set => this.SetValue(UsernameProperty, value);
|
||||
}
|
||||
public string Password
|
||||
{
|
||||
get => this.GetTypedValue<string>(PasswordProperty);
|
||||
set => this.SetValue(PasswordProperty, value);
|
||||
}
|
||||
public string CharacterName
|
||||
{
|
||||
get => this.GetTypedValue<string>(CharacterNameProperty);
|
||||
set => this.SetValue(CharacterNameProperty, value);
|
||||
}
|
||||
public bool IsDefault
|
||||
{
|
||||
get => this.GetTypedValue<bool>(IsDefaultProperty);
|
||||
set => this.SetValue(IsDefaultProperty, value);
|
||||
}
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private string username;
|
||||
[GenerateDependencyProperty]
|
||||
private string password;
|
||||
[GenerateDependencyProperty]
|
||||
private string characterName;
|
||||
[GenerateDependencyProperty]
|
||||
private bool isDefault;
|
||||
|
||||
public AccountTemplate()
|
||||
{
|
||||
|
||||
@@ -4,17 +4,22 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="10, 0, 10, 0">
|
||||
<local:HelpButton Width="20" Height="20" Margin="3, 0, 3, 0" Foreground="White"
|
||||
Clicked="HelpButton_Clicked" Cursor="Hand"></local:HelpButton>
|
||||
<TextBlock Text="{Binding Attribute.Name}" FontSize="16" Foreground="White"></TextBlock>
|
||||
</WrapPanel>
|
||||
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="10, 0, 10, 0">
|
||||
<local:MinusButton Foreground="White" Height="20" Clicked="MinusButton_Clicked"></local:MinusButton>
|
||||
<local:MinusButton Foreground="White" Height="20" Clicked="MinusButton_Clicked" Cursor="Hand"
|
||||
IsEnabled="{Binding ElementName=_this, Path=CanSubtract, Mode=OneWay}"></local:MinusButton>
|
||||
<TextBox Background="Transparent" Foreground="White" FontSize="16" IsReadOnly="True" Width="30"
|
||||
Text="{Binding Points}"></TextBox>
|
||||
<local:AddButton Foreground="White" Height="20" Clicked="AddButton_Clicked"></local:AddButton>
|
||||
<local:AddButton Foreground="White" Height="20" Clicked="AddButton_Clicked" Cursor="Hand"
|
||||
IsEnabled="{Binding ElementName=_this, Path=CanAdd, Mode=OneWay}"></local:AddButton>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,17 +1,46 @@
|
||||
using Daybreak.Models.Builds;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
|
||||
namespace Daybreak.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for AttributeTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class AttributeTemplate : UserControl
|
||||
{
|
||||
public event EventHandler<AttributeEntry> HelpClicked;
|
||||
|
||||
[GenerateDependencyProperty(InitialValue = false)]
|
||||
private bool canAdd;
|
||||
[GenerateDependencyProperty(InitialValue = false)]
|
||||
private bool canSubtract;
|
||||
|
||||
public AttributeTemplate()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.InitializeComponent();
|
||||
this.DataContextChanged += AttributeTemplate_DataContextChanged;
|
||||
}
|
||||
|
||||
private void AttributeTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is AttributeEntry attributeEntry)
|
||||
{
|
||||
if (attributeEntry.Points > 0)
|
||||
{
|
||||
this.CanSubtract = true;
|
||||
}
|
||||
|
||||
if (attributeEntry.Points < 12)
|
||||
{
|
||||
this.CanAdd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MinusButton_Clicked(object sender, System.EventArgs e)
|
||||
@@ -19,6 +48,8 @@ namespace Daybreak.Controls
|
||||
if (this.DataContext.As<AttributeEntry>().Points > 0)
|
||||
{
|
||||
this.DataContext.As<AttributeEntry>().Points--;
|
||||
this.CanSubtract = this.DataContext.As<AttributeEntry>().Points > 0;
|
||||
this.CanAdd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +58,16 @@ namespace Daybreak.Controls
|
||||
if (this.DataContext.As<AttributeEntry>().Points < 12)
|
||||
{
|
||||
this.DataContext.As<AttributeEntry>().Points++;
|
||||
this.CanAdd = this.DataContext.As<AttributeEntry>().Points < 12;
|
||||
this.CanSubtract = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void HelpButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (this.DataContext is AttributeEntry attributeEntry)
|
||||
{
|
||||
this.HelpClicked?.Invoke(this, attributeEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +24,16 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Primary Profession: " FontSize="16" Foreground="White"></TextBlock>
|
||||
<ListView FontSize="16" Foreground="White" Grid.Column="1"
|
||||
<TextBlock Text="Primary Profession: " FontSize="16" Foreground="White" Grid.Column="1"></TextBlock>
|
||||
<ListView FontSize="16" Foreground="White" Grid.Column="2" BorderThickness="0"
|
||||
Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Professions, Mode=OneWay}"
|
||||
SelectedItem="{Binding ElementName=_this, Path=PrimaryProfession, Mode=TwoWay}" Height="25"
|
||||
SelectedItem="{Binding ElementName=_this, Path=PrimaryProfession, Mode=TwoWay}" Height="30"
|
||||
SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
PreviewMouseWheel="ListView_DisableMouseWheel">
|
||||
PreviewMouseWheel="ListView_NavigateWithMouseWheel">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name, Mode=OneWay}"></TextBlock>
|
||||
@@ -42,18 +43,21 @@
|
||||
<behaviors:ScrollIntoView></behaviors:ScrollIntoView>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ListView>
|
||||
<local:HelpButton Grid.Column="0" Foreground="White" Width="20" Height="20" Margin="3"
|
||||
Clicked="HelpButtonPrimary_Clicked" Cursor="Hand"></local:HelpButton>
|
||||
</Grid>
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Secondary Profession: " FontSize="16" Foreground="White"></TextBlock>
|
||||
<ListView FontSize="16" Foreground="White" Grid.Column="1"
|
||||
<TextBlock Text="Secondary Profession: " FontSize="16" Foreground="White" Grid.Column="1"></TextBlock>
|
||||
<ListView FontSize="16" Foreground="White" Grid.Column="2" BorderThickness="0"
|
||||
Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Professions, Mode=OneWay}"
|
||||
SelectedItem="{Binding ElementName=_this, Path=SecondaryProfession, Mode=TwoWay}" Height="25"
|
||||
SelectedItem="{Binding ElementName=_this, Path=SecondaryProfession, Mode=TwoWay}" Height="30"
|
||||
SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
PreviewMouseWheel="ListView_DisableMouseWheel">
|
||||
PreviewMouseWheel="ListView_NavigateWithMouseWheel">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name, Mode=OneWay}"></TextBlock>
|
||||
@@ -63,13 +67,15 @@
|
||||
<behaviors:ScrollIntoView></behaviors:ScrollIntoView>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ListView>
|
||||
<local:HelpButton Grid.Column="0" Foreground="White" Width="20" Height="20" Margin="3"
|
||||
Clicked="HelpButtonSecondary_Clicked" Cursor="Hand"></local:HelpButton>
|
||||
</Grid>
|
||||
<Grid Grid.Row="3">
|
||||
<ListBox Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Attributes, Mode=OneWay}"
|
||||
HorizontalContentAlignment="Stretch">
|
||||
HorizontalContentAlignment="Stretch" BorderThickness="0">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:AttributeTemplate></local:AttributeTemplate>
|
||||
<local:AttributeTemplate HelpClicked="AttributeTemplate_HelpClicked"></local:AttributeTemplate>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
@@ -90,42 +96,42 @@
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<local:SkillTemplate Grid.Column="0"
|
||||
Foreground="White"
|
||||
Foreground="White" Cursor="Hand"
|
||||
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill0, Mode=TwoWay}"
|
||||
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
|
||||
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
|
||||
<local:SkillTemplate Grid.Column="1"
|
||||
Foreground="White"
|
||||
Foreground="White" Cursor="Hand"
|
||||
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill1, Mode=TwoWay}"
|
||||
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
|
||||
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
|
||||
<local:SkillTemplate Grid.Column="2"
|
||||
Foreground="White"
|
||||
Foreground="White" Cursor="Hand"
|
||||
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill2, Mode=TwoWay}"
|
||||
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
|
||||
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
|
||||
<local:SkillTemplate Grid.Column="3"
|
||||
Foreground="White"
|
||||
Foreground="White" Cursor="Hand"
|
||||
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill3, Mode=TwoWay}"
|
||||
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
|
||||
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
|
||||
<local:SkillTemplate Grid.Column="4"
|
||||
Foreground="White"
|
||||
Foreground="White" Cursor="Hand"
|
||||
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill4, Mode=TwoWay}"
|
||||
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
|
||||
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
|
||||
<local:SkillTemplate Grid.Column="5"
|
||||
Foreground="White"
|
||||
Foreground="White" Cursor="Hand"
|
||||
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill5, Mode=TwoWay}"
|
||||
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
|
||||
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
|
||||
<local:SkillTemplate Grid.Column="6"
|
||||
Foreground="White"
|
||||
Foreground="White" Cursor="Hand"
|
||||
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill6, Mode=TwoWay}"
|
||||
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
|
||||
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
|
||||
<local:SkillTemplate Grid.Column="7"
|
||||
Foreground="White"
|
||||
Foreground="White" Cursor="Hand"
|
||||
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill7, Mode=TwoWay}"
|
||||
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
|
||||
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
|
||||
|
||||
@@ -1,102 +1,48 @@
|
||||
using Daybreak.Launch;
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Services.IconRetrieve;
|
||||
using Daybreak.Models.Builds;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Extensions;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace Daybreak.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for BuildTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class BuildTemplate : UserControl
|
||||
{
|
||||
private const string SkillNamePlaceholder = "[NAME]";
|
||||
private const string BaseAddress = $"https://wiki.guildwars.com/wiki/{SkillNamePlaceholder}";
|
||||
|
||||
public readonly static DependencyProperty PrimaryProfessionProperty =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Profession>(nameof(PrimaryProfession), new PropertyMetadata(Profession.None));
|
||||
public readonly static DependencyProperty SecondaryProfessionProperty =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Profession>(nameof(SecondaryProfession), new PropertyMetadata(Profession.None));
|
||||
public readonly static DependencyProperty Skill0Property =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Skill>(nameof(Skill0), new PropertyMetadata(Skill.NoSkill));
|
||||
public readonly static DependencyProperty Skill1Property =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Skill>(nameof(Skill1), new PropertyMetadata(Skill.NoSkill));
|
||||
public readonly static DependencyProperty Skill2Property =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Skill>(nameof(Skill2), new PropertyMetadata(Skill.NoSkill));
|
||||
public readonly static DependencyProperty Skill3Property =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Skill>(nameof(Skill3), new PropertyMetadata(Skill.NoSkill));
|
||||
public readonly static DependencyProperty Skill4Property =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Skill>(nameof(Skill4), new PropertyMetadata(Skill.NoSkill));
|
||||
public readonly static DependencyProperty Skill5Property =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Skill>(nameof(Skill5), new PropertyMetadata(Skill.NoSkill));
|
||||
public readonly static DependencyProperty Skill6Property =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Skill>(nameof(Skill6), new PropertyMetadata(Skill.NoSkill));
|
||||
public readonly static DependencyProperty Skill7Property =
|
||||
DependencyPropertyExtensions.Register<BuildTemplate, Skill>(nameof(Skill7), new PropertyMetadata(Skill.NoSkill));
|
||||
private const string InfoNamePlaceholder = "[NAME]";
|
||||
private const string BaseAddress = $"https://wiki.guildwars.com/wiki/{InfoNamePlaceholder}";
|
||||
|
||||
private bool loadedProperties = false;
|
||||
private BuildEntry loadedBuild;
|
||||
private SkillTemplate selectingSkillTemplate;
|
||||
|
||||
public Profession PrimaryProfession
|
||||
{
|
||||
get => this.GetTypedValue<Profession>(PrimaryProfessionProperty);
|
||||
set => this.SetValue(PrimaryProfessionProperty, value);
|
||||
}
|
||||
public Profession SecondaryProfession
|
||||
{
|
||||
get => this.GetTypedValue<Profession>(SecondaryProfessionProperty);
|
||||
set => this.SetValue(SecondaryProfessionProperty, value);
|
||||
}
|
||||
public Skill Skill0
|
||||
{
|
||||
get => this.GetTypedValue<Skill>(Skill0Property);
|
||||
set => this.SetValue(Skill0Property, value);
|
||||
}
|
||||
public Skill Skill1
|
||||
{
|
||||
get => this.GetTypedValue<Skill>(Skill1Property);
|
||||
set => this.SetValue(Skill1Property, value);
|
||||
}
|
||||
public Skill Skill2
|
||||
{
|
||||
get => this.GetTypedValue<Skill>(Skill2Property);
|
||||
set => this.SetValue(Skill2Property, value);
|
||||
}
|
||||
public Skill Skill3
|
||||
{
|
||||
get => this.GetTypedValue<Skill>(Skill3Property);
|
||||
set => this.SetValue(Skill3Property, value);
|
||||
}
|
||||
public Skill Skill4
|
||||
{
|
||||
get => this.GetTypedValue<Skill>(Skill4Property);
|
||||
set => this.SetValue(Skill4Property, value);
|
||||
}
|
||||
public Skill Skill5
|
||||
{
|
||||
get => this.GetTypedValue<Skill>(Skill5Property);
|
||||
set => this.SetValue(Skill5Property, value);
|
||||
}
|
||||
public Skill Skill6
|
||||
{
|
||||
get => this.GetTypedValue<Skill>(Skill6Property);
|
||||
set => this.SetValue(Skill6Property, value);
|
||||
}
|
||||
public Skill Skill7
|
||||
{
|
||||
get => this.GetTypedValue<Skill>(Skill7Property);
|
||||
set => this.SetValue(Skill7Property, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private Profession primaryProfession;
|
||||
[GenerateDependencyProperty]
|
||||
private Profession secondaryProfession;
|
||||
[GenerateDependencyProperty]
|
||||
private Skill skill0;
|
||||
[GenerateDependencyProperty]
|
||||
private Skill skill1;
|
||||
[GenerateDependencyProperty]
|
||||
private Skill skill2;
|
||||
[GenerateDependencyProperty]
|
||||
private Skill skill3;
|
||||
[GenerateDependencyProperty]
|
||||
private Skill skill4;
|
||||
[GenerateDependencyProperty]
|
||||
private Skill skill5;
|
||||
[GenerateDependencyProperty]
|
||||
private Skill skill6;
|
||||
[GenerateDependencyProperty]
|
||||
private Skill skill7;
|
||||
public ObservableCollection<Skill> AvailableSkills { get; } = new ObservableCollection<Skill>();
|
||||
public ObservableCollection<AttributeEntry> Attributes { get; } = new ObservableCollection<AttributeEntry>();
|
||||
public ObservableCollection<Profession> Professions { get; } = new ObservableCollection<Profession>(Profession.Professions);
|
||||
@@ -104,12 +50,18 @@ namespace Daybreak.Controls
|
||||
public BuildTemplate()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.InitializeProperties();
|
||||
this.DataContextChanged += BuildTemplate_DataContextChanged;
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (this.loadedProperties is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Property == PrimaryProfessionProperty || e.Property == SecondaryProfessionProperty)
|
||||
{
|
||||
if (e.Property == PrimaryProfessionProperty)
|
||||
@@ -125,6 +77,21 @@ namespace Daybreak.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeProperties()
|
||||
{
|
||||
this.PrimaryProfession = Profession.None;
|
||||
this.SecondaryProfession = Profession.None;
|
||||
this.Skill0 = Skill.NoSkill;
|
||||
this.Skill1 = Skill.NoSkill;
|
||||
this.Skill2 = Skill.NoSkill;
|
||||
this.Skill3 = Skill.NoSkill;
|
||||
this.Skill4 = Skill.NoSkill;
|
||||
this.Skill5 = Skill.NoSkill;
|
||||
this.Skill6 = Skill.NoSkill;
|
||||
this.Skill7 = Skill.NoSkill;
|
||||
this.loadedProperties = true;
|
||||
}
|
||||
|
||||
private void BuildTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if(e.NewValue is BuildEntry)
|
||||
@@ -137,8 +104,8 @@ namespace Daybreak.Controls
|
||||
|
||||
private void Grid_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
this.SkillBrowser.Width = 0;
|
||||
this.SkillsListView.Width = 0;
|
||||
this.HideSkillListView();
|
||||
this.HideInfoBrowser();
|
||||
}
|
||||
|
||||
private void LoadAttributes()
|
||||
@@ -244,21 +211,79 @@ namespace Daybreak.Controls
|
||||
this.Skill7 = build.Build.Skills[7];
|
||||
}
|
||||
|
||||
private void BrowseToInfo(string infoName)
|
||||
{
|
||||
var address = BaseAddress.Replace(InfoNamePlaceholder, infoName.Replace(" ", "_"));
|
||||
this.SkillBrowser.Address = address;
|
||||
this.ShowInfoBrowser();
|
||||
}
|
||||
|
||||
private void ShowInfoBrowser()
|
||||
{
|
||||
this.SkillBrowser.Width = 400;
|
||||
this.SkillsListView.Width = 0;
|
||||
}
|
||||
|
||||
private void HideInfoBrowser()
|
||||
{
|
||||
this.SkillBrowser.Width = 0;
|
||||
}
|
||||
|
||||
private void ShowSkillListView()
|
||||
{
|
||||
this.SkillBrowser.Width = 0;
|
||||
this.SkillsListView.Width = 400;
|
||||
}
|
||||
|
||||
private void HideSkillListView()
|
||||
{
|
||||
this.SkillsListView.Width = 0;
|
||||
}
|
||||
|
||||
private void HelpButtonPrimary_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (this.PrimaryProfession == Profession.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.BrowseToInfo(PrimaryProfession.Name);
|
||||
if (e is RoutedEventArgs routedEventArgs)
|
||||
{
|
||||
routedEventArgs.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void HelpButtonSecondary_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (this.SecondaryProfession == Profession.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.BrowseToInfo(this.SecondaryProfession.Name);
|
||||
if (e is RoutedEventArgs routedEventArgs)
|
||||
{
|
||||
routedEventArgs.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void AttributeTemplate_HelpClicked(object sender, AttributeEntry e)
|
||||
{
|
||||
this.BrowseToInfo(e.Attribute.Name);
|
||||
}
|
||||
|
||||
private void SkillTemplate_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var skill = sender.As<SkillTemplate>().DataContext.As<Skill>();
|
||||
if (skill == Skill.NoSkill)
|
||||
{
|
||||
this.SkillsListView.Width = 400;
|
||||
this.SkillBrowser.Width = 0;
|
||||
this.ShowSkillListView();
|
||||
this.selectingSkillTemplate = sender.As<SkillTemplate>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var address = BaseAddress.Replace(SkillNamePlaceholder, skill.Name.Replace(" ", "_"));
|
||||
this.SkillBrowser.Address = address;
|
||||
this.SkillBrowser.Width = 400;
|
||||
this.SkillsListView.Width = 0;
|
||||
this.BrowseToInfo(skill.Name);
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
@@ -272,12 +297,12 @@ namespace Daybreak.Controls
|
||||
{
|
||||
if (this.selectingSkillTemplate is null)
|
||||
{
|
||||
this.SkillsListView.Width = 0;
|
||||
this.HideSkillListView();
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectingSkillTemplate.DataContext = sender.As<ListView>().SelectedItem;
|
||||
this.SkillsListView.Width = 0;
|
||||
this.HideSkillListView();
|
||||
this.loadedBuild.Build.Skills[0] = Skill0;
|
||||
this.loadedBuild.Build.Skills[1] = Skill1;
|
||||
this.loadedBuild.Build.Skills[2] = Skill2;
|
||||
@@ -288,9 +313,21 @@ namespace Daybreak.Controls
|
||||
this.loadedBuild.Build.Skills[7] = Skill7;
|
||||
}
|
||||
|
||||
private void ListView_DisableMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
|
||||
private void ListView_NavigateWithMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
if (e.Delta > 0)
|
||||
{
|
||||
sender.As<ListView>().SelectedIndex = sender.As<ListView>().SelectedIndex > 0 ?
|
||||
sender.As<ListView>().SelectedIndex - 1 :
|
||||
0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sender.As<ListView>().SelectedIndex = sender.As<ListView>().SelectedIndex < sender.As<ListView>().Items.Count - 1 ?
|
||||
sender.As<ListView>().SelectedIndex + 1 :
|
||||
sender.As<ListView>().Items.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,24 +11,17 @@ namespace Daybreak.Controls
|
||||
/// <summary>
|
||||
/// Interaction logic for GuildwarsPathTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class GuildwarsPathTemplate : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty PathProperty = DependencyPropertyExtensions.Register<GuildwarsPathTemplate, string>(nameof(Path));
|
||||
public static readonly DependencyProperty IsDefaultProperty = DependencyPropertyExtensions.Register<GuildwarsPathTemplate, bool>(nameof(IsDefault));
|
||||
|
||||
public event EventHandler RemoveClicked;
|
||||
public event EventHandler DefaultClicked;
|
||||
|
||||
public string Path
|
||||
{
|
||||
get => this.GetTypedValue<string>(PathProperty);
|
||||
set => this.SetValue(PathProperty, value);
|
||||
}
|
||||
public bool IsDefault
|
||||
{
|
||||
get => this.GetTypedValue<bool>(IsDefaultProperty);
|
||||
set => this.SetValue(IsDefaultProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private string path;
|
||||
[GenerateDependencyProperty]
|
||||
private bool isDefault;
|
||||
|
||||
public GuildwarsPathTemplate()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<UserControl x:Class="Daybreak.Controls.Templates.ScreenTemplate"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Controls.Templates"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<Rectangle Stroke="{Binding ElementName=_this, Path=Highlight, Mode=OneWay}" StrokeThickness="15"></Rectangle>
|
||||
<TextBlock FontSize="168" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Text="{Binding ElementName=_this, Path=ScreenId, Mode=OneWay}"
|
||||
Foreground="{Binding ElementName=_this, Path=Highlight, Mode=OneWay}"></TextBlock>
|
||||
<Rectangle Fill="Transparent" MouseEnter="Rectangle_MouseEnter" MouseLeave="Rectangle_MouseLeave" MouseLeftButtonDown="Rectangle_MouseLeftButtonDown"></Rectangle>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,63 @@
|
||||
using Daybreak.Models;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Daybreak.Controls.Templates
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ScreenTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class ScreenTemplate : UserControl
|
||||
{
|
||||
public event EventHandler<Screen> Clicked;
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private string screenId;
|
||||
[GenerateDependencyProperty]
|
||||
private Brush highlight;
|
||||
|
||||
public ScreenTemplate()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.DataContextChanged += ScreenTemplate_DataContextChanged;
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (e.Property == ForegroundProperty)
|
||||
{
|
||||
this.Highlight = e.NewValue.As<Brush>();
|
||||
}
|
||||
}
|
||||
|
||||
private void ScreenTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is Screen screen)
|
||||
{
|
||||
this.ScreenId = screen.Id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void Rectangle_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
this.Highlight = Brushes.LightSteelBlue;
|
||||
}
|
||||
|
||||
private void Rectangle_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
this.Highlight = this.Foreground;
|
||||
}
|
||||
|
||||
private void Rectangle_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
this.Clicked?.Invoke(this, this.DataContext.As<Screen>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,11 @@
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Image VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
|
||||
Source="{Binding ElementName=_this, Path=ImageSource, Mode=OneWay}"></Image>
|
||||
<Image VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
Width="{Binding ElementName=_this, Path=ActualWidth, Mode=OneWay}"
|
||||
Height="{Binding ElementName=_this, Path=ActualWidth, Mode=OneWay}"
|
||||
Source="{Binding ElementName=_this, Path=ImageSource, Mode=OneWay}"
|
||||
Stretch="UniformToFill"></Image>
|
||||
<Border BorderThickness="5" BorderBrush="Black"></Border>
|
||||
<Border BorderThickness="5" BorderBrush="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Opacity="{Binding ElementName=_this, Path=BorderOpacity, Mode=OneWay}"></Border>
|
||||
|
||||
@@ -16,28 +16,19 @@ namespace Daybreak.Controls
|
||||
/// <summary>
|
||||
/// Interaction logic for SkillTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class SkillTemplate : UserControl
|
||||
{
|
||||
public readonly static DependencyProperty ImageSourceProperty =
|
||||
DependencyPropertyExtensions.Register<SkillTemplate, ImageSource>(nameof(ImageSource));
|
||||
public readonly static DependencyProperty BorderOpacityProperty =
|
||||
DependencyPropertyExtensions.Register<SkillTemplate, double>(nameof(BorderOpacity), new PropertyMetadata(0d));
|
||||
|
||||
public event EventHandler<RoutedEventArgs> Clicked;
|
||||
public event EventHandler RemoveClicked;
|
||||
|
||||
private readonly IIconRetriever iconRetriever;
|
||||
|
||||
public ImageSource ImageSource
|
||||
{
|
||||
get => this.GetTypedValue<ImageSource>(ImageSourceProperty);
|
||||
set => this.SetValue(ImageSourceProperty, value);
|
||||
}
|
||||
public double BorderOpacity
|
||||
{
|
||||
get => this.GetTypedValue<double>(BorderOpacityProperty);
|
||||
set => this.SetValue(BorderOpacityProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private ImageSource imageSource;
|
||||
[GenerateDependencyProperty]
|
||||
private double borderOpacity;
|
||||
|
||||
public SkillTemplate()
|
||||
{
|
||||
@@ -48,15 +39,22 @@ namespace Daybreak.Controls
|
||||
|
||||
private void SkillTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is Skill)
|
||||
if (e.NewValue is Skill skill)
|
||||
{
|
||||
Task.Run(() => GetImageStream(e.NewValue.As<Skill>())).ContinueWith((previousTask) =>
|
||||
if (skill != Skill.NoSkill)
|
||||
{
|
||||
this.Dispatcher.Invoke(() =>
|
||||
Task.Run(() => GetImageStream(skill)).ContinueWith((previousTask) =>
|
||||
{
|
||||
this.ImageSource = GetImageSource(previousTask.Result);
|
||||
this.Dispatcher.Invoke(() =>
|
||||
{
|
||||
this.ImageSource = GetImageSource(previousTask.Result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (this.ImageSource is not null)
|
||||
{
|
||||
this.ImageSource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
@@ -9,22 +10,32 @@
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
|
||||
<Version>0.7.2</Version>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
|
||||
<Version>0.8.9.3</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.33" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.32" />
|
||||
<PackageReference Include="LiteDB" Version="5.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.818.41" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Slim" Version="1.2.1" />
|
||||
<PackageReference Include="NReco.Logging.File" Version="1.1.1" />
|
||||
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageReference Include="Slim" Version="1.3.3" />
|
||||
<PackageReference Include="System.Windows.Interactivity.WPF" Version="2.0.20525" />
|
||||
<PackageReference Include="SystemExtensions.NetStandard" Version="1.1.4" />
|
||||
<PackageReference Include="SystemExtensions.NetStandard" Version="1.2.0" />
|
||||
<PackageReference Include="WCL" Version="1.0.2" />
|
||||
<PackageReference Include="WpfExtended" Version="0.2.0" />
|
||||
<PackageReference Include="WpfExtended" Version="0.3.3" />
|
||||
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.1" />
|
||||
<PackageReference Include="WpfScreenHelper" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Controls\Buttons\HelpButton.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Controls\Buttons\MinusButton.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@@ -37,6 +48,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\Buttons\HelpButton.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="Controls\Buttons\MinusButton.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<SubType>Designer</SubType>
|
||||
@@ -55,4 +70,8 @@
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'">
|
||||
<Exec Command="echo.>$(Version).version" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using Daybreak.Configuration;
|
||||
using Daybreak.Exceptions;
|
||||
using Daybreak.Services.ApplicationLifetime;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Microsoft.Web.WebView2.Wpf;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Slim;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Extensions;
|
||||
|
||||
@@ -26,6 +25,10 @@ namespace Daybreak.Launch
|
||||
return LaunchMainWindow();
|
||||
}
|
||||
|
||||
protected override void SetupServiceManager(IServiceManager serviceManager)
|
||||
{
|
||||
ProjectConfiguration.RegisterResolvers(serviceManager);
|
||||
}
|
||||
protected override void RegisterServices(IServiceProducer serviceProducer)
|
||||
{
|
||||
ProjectConfiguration.RegisterServices(this.ServiceManager);
|
||||
@@ -39,12 +42,33 @@ namespace Daybreak.Launch
|
||||
return false;
|
||||
}
|
||||
|
||||
this.ServiceManager.GetService<ILogger>().LogCritical(e);
|
||||
this.ServiceManager.GetService<ILogger>().LogCritical(e, $"Unhandled exception");
|
||||
if (e is FatalException fatalException)
|
||||
{
|
||||
this.ServiceManager.GetService<ILogger>().LogCritical(e, $"{nameof(FatalException)} encountered. Closing application.");
|
||||
MessageBox.Show(fatalException.ToString());
|
||||
File.WriteAllText("crash.log", e.ToString());
|
||||
return false;
|
||||
}
|
||||
else if (e is TargetInvocationException targetInvocationException && e.InnerException is FatalException innerFatalException)
|
||||
{
|
||||
this.ServiceManager.GetService<ILogger>().LogCritical(e, $"{nameof(FatalException)} encountered. Closing application.");
|
||||
MessageBox.Show(innerFatalException.ToString());
|
||||
File.WriteAllText("crash.log", e.ToString());
|
||||
return false;
|
||||
}
|
||||
else if (e is AggregateException aggregateException)
|
||||
{
|
||||
if (aggregateException.InnerExceptions.FirstOrDefault() is COMException comException &&
|
||||
comException.Message.Contains("Invalid window handle"))
|
||||
{
|
||||
/*
|
||||
* Ignore exception caused by browser failing to initialize due to missing window.
|
||||
* Likely caused by switching windows before browser was initialized.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show(e.ToString());
|
||||
return true;
|
||||
|
||||
@@ -16,43 +16,46 @@
|
||||
Title="Daybreak"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Window.Resources>
|
||||
<Style x:Key="Window_SettingsButton" TargetType="ToggleButton">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Grid>
|
||||
<Rectangle x:Name="OverlayRect" Fill="{TemplateBinding Background}" Opacity="0"></Rectangle>
|
||||
<Viewbox Margin="5" Stretch="Uniform">
|
||||
<Grid>
|
||||
<Path Stroke="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
<ResourceDictionary>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
|
||||
<Style x:Key="Window_SettingsButton" TargetType="ToggleButton">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Grid>
|
||||
<Rectangle x:Name="OverlayRect" Fill="{TemplateBinding Background}" Opacity="0"></Rectangle>
|
||||
<Viewbox Margin="5" Stretch="Uniform">
|
||||
<Grid>
|
||||
<Path Stroke="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Data="m76.89,45.018c0,-1.587 -0.151,-3.138 -0.383,-4.66l11.227,-6.48l-11.639,-20.136l-11.616,6.47c-2.421,-1.931 -5.479,-3.506 -8.479,-4.664l0,-11.548l-22,0l0,11.548c-4,1.112 -5.649,2.617 -8.005,4.448l-11.251,-6.604l-11.914,20.006l11.12,6.604c-0.262,1.643 -0.45,3.308 -0.45,5.016c0,1.587 0.153,3.136 0.376,4.658l-11.228,6.484l11.634,20.135l11.424,-6.472c2.431,1.933 4.294,3.506 8.294,4.665l0,12.512l22,0l0,-12.512c3,-1.115 5.839,-2.618 8.19,-4.449l11.348,6.604l11.96,-20.006l-11.095,-6.603c0.258,-1.64 0.487,-3.304 0.487,-5.016zm-31.704,14.137c-7.799,0 -14.138,-6.332 -14.138,-14.138c0,-7.808 6.339,-14.136 14.138,-14.136c7.81,0 14.14,6.328 14.14,14.136c-0.001,7.806 -6.331,14.138 -14.14,14.138z"></Path>
|
||||
<Path Fill="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
<Path Fill="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Data="m56.5,89l-22,0c-1.381,0 -2.5,-1.119 -2.5,-2.5l0,-10.199c-2.612,-0.955 -4.464,-2.126 -6.274,-3.49l-10.229,5.671c-1.192,0.661 -2.694,0.244 -3.377,-0.936l-11.633,-20.135c-0.331,-0.574 -0.422,-1.257 -0.25,-1.897s0.591,-1.188 1.165,-1.519l9.794,-5.654c-0.121,-1.164 -0.181,-2.262 -0.181,-3.323c0,-1.317 0.112,-2.592 0.254,-3.721l-9.656,-5.75c-0.57,-0.34 -0.982,-0.893 -1.145,-1.537c-0.163,-0.644 -0.063,-1.326 0.279,-1.896l11.977,-20.006c0.702,-1.173 2.218,-1.564 3.399,-0.878l9.927,5.762c1.618,-1.213 3.362,-2.346 5.949,-3.273l0,-10.219c0.001,-1.381 1.12,-2.5 2.501,-2.5l22,0c1.381,0 2.5,1.119 2.5,2.5l0,10.377c2.124,0.938 4.104,2.059 5.919,3.35l9.939,-5.657c1.19,-0.677 2.713,-0.269 3.4,0.922l11.639,20.135c0.332,0.574 0.423,1.257 0.251,1.897s-0.591,1.188 -1.165,1.519l-9.781,5.646c0.126,1.165 0.188,2.266 0.188,3.329c0,1.327 -0.108,2.597 -0.246,3.717l9.692,5.752c0.571,0.339 0.983,0.891 1.147,1.533c0.163,0.644 0.065,1.325 -0.274,1.896l-11.897,20.005c-0.703,1.183 -2.234,1.573 -3.416,0.876l-9.798,-5.765c-1.782,1.25 -3.634,2.29 -5.598,3.144l0,10.324c0,1.381 -1.119,2.5 -2.5,2.5zm-19.5,-5l17,0l0,-9.512c0,-1.045 0.649,-1.979 1.629,-2.344c2.624,-0.975 5.004,-2.309 7.275,-4.078c0.809,-0.629 1.921,-0.702 2.804,-0.182l9.078,5.341l9.347,-15.716l-8.974,-5.325c-0.88,-0.522 -1.354,-1.528 -1.194,-2.538c0.198,-1.259 0.425,-2.923 0.425,-4.629c0,-1.314 -0.116,-2.716 -0.354,-4.284c-0.154,-1.018 0.33,-2.026 1.222,-2.541l9.061,-5.23l-9.145,-15.818l-9.208,5.241c-0.888,0.505 -1.995,0.42 -2.795,-0.219c-2.199,-1.754 -4.747,-3.196 -7.57,-4.286c-0.964,-0.372 -1.6,-1.299 -1.6,-2.332l0,-9.548l-17.001,0l0,9.548c0,1.123 -0.748,2.107 -1.83,2.408c-3.47,0.965 -5.073,2.212 -7.293,3.938l-0.098,0.076c-0.804,0.625 -1.909,0.7 -2.789,0.188l-9.243,-5.365l-9.423,15.739l8.937,5.321c0.879,0.524 1.351,1.531 1.189,2.542c-0.202,1.27 -0.435,2.943 -0.435,4.622c0,1.318 0.112,2.724 0.342,4.297c0.148,1.013 -0.337,2.015 -1.224,2.526l-9.065,5.234l9.151,15.842l9.525,-5.28c0.883,-0.49 1.976,-0.4 2.768,0.229c2.384,1.896 4.207,3.214 7.684,4.221c1.069,0.31 1.804,1.29 1.804,2.402l0,9.512zm8.186,-22.345c-9.174,0 -16.638,-7.464 -16.638,-16.638c0,-9.173 7.464,-16.636 16.638,-16.636c9.175,0 16.64,7.463 16.64,16.636c-0.001,9.174 -7.466,16.638 -16.64,16.638zm0,-28.273c-6.417,0 -11.638,5.22 -11.638,11.636c0,6.417 5.221,11.638 11.638,11.638c6.418,0 11.64,-5.221 11.64,-11.638c-0.001,-6.416 -5.222,-11.636 -11.64,-11.636z"></Path>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="Opacity" TargetName="OverlayRect" Value="0.5"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="False"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="Opacity" TargetName="OverlayRect" Value="0"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="Opacity" TargetName="OverlayRect" Value="0.5"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="False"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="Opacity" TargetName="OverlayRect" Value="0"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid Background="Black">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -82,9 +85,20 @@
|
||||
Text="{Binding ElementName=_this, Path=CreditText, Mode=OneWay}" VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right" Margin="0, 0, 20, 30" FontSize="22" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Clicked="CreditTextBox_MouseLeftButtonDown" Cursor="Hand"></controls:OpaqueButton>
|
||||
<TextBox Grid.Row="1" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Opacity="0.6"
|
||||
Text="{Binding ElementName=_this, Path=CurrentVersionText, Mode=OneWay}" IsReadOnly="True" VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right" Margin="0, 0, 20, 10" FontSize="10" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></TextBox>
|
||||
<WrapPanel Grid.Row="1" Margin="0, 0, 20, 10" VerticalAlignment="Bottom" HorizontalAlignment="Right">
|
||||
<controls:OpaqueButton Grid.Row="1" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Opacity="0.6"
|
||||
Text="{Binding ElementName=_this, Path=CurrentVersionText, Mode=OneWay}" FontSize="10"
|
||||
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Clicked="VersionText_Clicked"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Grid.Row="1" Background="Transparent" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Opacity="0.6" Text=" - " FontSize="10" BorderBrush="Transparent" BorderThickness="0"
|
||||
Visibility="{Binding ElementName=_this, Path=IsRunningAsAdmin, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Clicked="VersionText_Clicked"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Grid.Row="1" Background="Transparent" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Opacity="0.6" Text="[Admin]" FontSize="10" BorderBrush="Transparent" BorderThickness="0"
|
||||
Visibility="{Binding ElementName=_this, Path=IsRunningAsAdmin, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Clicked="VersionText_Clicked"></controls:OpaqueButton>
|
||||
</WrapPanel>
|
||||
<Grid x:Name="Container" Grid.Row="1">
|
||||
</Grid>
|
||||
<wcl:Border OnResize="Border_OnResize" Grid.RowSpan="2" Active="True"></wcl:Border>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Daybreak.Services.Bloogum;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Privilege;
|
||||
using Daybreak.Services.Runtime;
|
||||
using Daybreak.Services.Screenshots;
|
||||
using Daybreak.Services.Updater;
|
||||
@@ -21,41 +23,52 @@ namespace Daybreak.Launch
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public static readonly DependencyProperty CreditTextProperty = DependencyPropertyExtensions.Register<MainWindow, string>(nameof(CreditText));
|
||||
public static readonly DependencyProperty CurrentVersionTextProperty = DependencyPropertyExtensions.Register<MainWindow, string>(nameof(CurrentVersionText));
|
||||
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IScreenshotProvider screenshotProvider;
|
||||
private readonly IBloogumClient bloogumClient;
|
||||
private readonly IApplicationUpdater applicationUpdater;
|
||||
private readonly IPrivilegeManager privilegeManager;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly CancellationTokenSource cancellationToken = new();
|
||||
|
||||
public string CreditText
|
||||
{
|
||||
get => this.GetTypedValue<string>(CreditTextProperty);
|
||||
set => this.SetValue(CreditTextProperty, value);
|
||||
}
|
||||
private bool canCheckUpdate = false;
|
||||
|
||||
public string CurrentVersionText
|
||||
{
|
||||
get => this.GetTypedValue<string>(CurrentVersionTextProperty);
|
||||
set => this.SetValue(CurrentVersionTextProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private string creditText;
|
||||
[GenerateDependencyProperty]
|
||||
private string currentVersionText;
|
||||
[GenerateDependencyProperty]
|
||||
private bool isRunningAsAdmin;
|
||||
|
||||
public MainWindow(
|
||||
IViewManager viewManager,
|
||||
IScreenshotProvider screenshotProvider,
|
||||
IBloogumClient bloogumClient,
|
||||
IApplicationUpdater applicationUpdater)
|
||||
IApplicationUpdater applicationUpdater,
|
||||
IPrivilegeManager privilegeManager,
|
||||
IConfigurationManager configurationManager)
|
||||
{
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.screenshotProvider = screenshotProvider.ThrowIfNull(nameof(screenshotProvider));
|
||||
this.bloogumClient = bloogumClient.ThrowIfNull(nameof(bloogumClient));
|
||||
this.applicationUpdater = applicationUpdater.ThrowIfNull(nameof(applicationUpdater));
|
||||
this.privilegeManager = privilegeManager.ThrowIfNull(nameof(privilegeManager));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.InitializeComponent();
|
||||
this.CurrentVersionText = this.applicationUpdater.CurrentVersion;
|
||||
this.LoadConfiguration();
|
||||
this.configurationManager.ConfigurationChanged += (s, e) => this.LoadConfiguration();
|
||||
this.CurrentVersionText = this.applicationUpdater.CurrentVersion.ToString();
|
||||
this.IsRunningAsAdmin = this.privilegeManager.AdminPrivileges;
|
||||
}
|
||||
|
||||
private void LoadConfiguration()
|
||||
{
|
||||
var configuration = this.configurationManager.GetConfiguration();
|
||||
this.canCheckUpdate = configuration.AutoCheckUpdate;
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
@@ -140,6 +153,11 @@ namespace Daybreak.Launch
|
||||
}
|
||||
}
|
||||
|
||||
private void VersionText_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<VersionManagementView>();
|
||||
}
|
||||
|
||||
private void SetImage(ImageSource imageSource)
|
||||
{
|
||||
this.ImageViewer.ShowImage(imageSource);
|
||||
@@ -160,7 +178,7 @@ namespace Daybreak.Launch
|
||||
|
||||
private async void CheckForUpdates()
|
||||
{
|
||||
var updateAvailable = await this.applicationUpdater.UpdateAvailable().ConfigureAwait(true);
|
||||
var updateAvailable = this.canCheckUpdate is true && await this.applicationUpdater.UpdateAvailable().ConfigureAwait(true);
|
||||
if (updateAvailable)
|
||||
{
|
||||
this.viewManager.ShowView<AskUpdateView>();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public sealed class ElevationRequest
|
||||
{
|
||||
public object DataContext { get; set; }
|
||||
public Type View { get; set; }
|
||||
public string MessageToUser { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Daybreak.Models.Github
|
||||
{
|
||||
public sealed class GithubRefTag
|
||||
{
|
||||
[JsonProperty("ref")]
|
||||
public string Ref;
|
||||
[JsonProperty("node_id")]
|
||||
public string NodeId;
|
||||
[JsonProperty("url")]
|
||||
public string Url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public sealed class KeyMacro
|
||||
{
|
||||
public List<Keys> Keys { get; set; }
|
||||
public Keys TargetKey { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public sealed class KeyboardHookEventArgs
|
||||
{
|
||||
public bool Handled { get; set; }
|
||||
public KeyboardState KeyboardState { get; }
|
||||
public KeyboardInput KeyboardInput { get; }
|
||||
|
||||
public KeyboardHookEventArgs(
|
||||
KeyboardState keyboardState,
|
||||
KeyboardInput keyboardInput)
|
||||
{
|
||||
this.KeyboardState = keyboardState;
|
||||
this.KeyboardInput = keyboardInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KeyboardInput
|
||||
{
|
||||
/// <summary>
|
||||
/// A virtual-key code. The code must be a value in the range 1 to 254.
|
||||
/// </summary>
|
||||
public int VirtualCode;
|
||||
|
||||
// EDT: added a conversion from VirtualCode to Keys.
|
||||
/// <summary>
|
||||
/// The VirtualCode converted to typeof(Keys) for higher usability.
|
||||
/// </summary>
|
||||
public Keys Key { get { return (Keys)VirtualCode; } }
|
||||
|
||||
/// <summary>
|
||||
/// A hardware scan code for the key.
|
||||
/// </summary>
|
||||
public int HardwareScanCode;
|
||||
|
||||
/// <summary>
|
||||
/// The extended-key flag, event-injected Flags, context code, and transition-state flag. This member is specified as follows. An application can use the following values to test the keystroke Flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected from a process running at lower integrity level.
|
||||
/// </summary>
|
||||
public int Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The time stamp stamp for this message, equivalent to what GetMessageTime would return for this message.
|
||||
/// </summary>
|
||||
public int TimeStamp;
|
||||
|
||||
/// <summary>
|
||||
/// Additional information associated with the message.
|
||||
/// </summary>
|
||||
public IntPtr AdditionalInformation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public enum KeyboardState
|
||||
{
|
||||
KeyDown = 0x0100,
|
||||
KeyUp = 0x0101,
|
||||
SysKeyDown = 0x0104,
|
||||
SysKeyUp = 0x0105
|
||||
}
|
||||
}
|
||||
+12
-4
@@ -1,12 +1,20 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public class Log
|
||||
public sealed class Log
|
||||
{
|
||||
[JsonProperty("DateTime")]
|
||||
public DateTime LogTime { get; set; }
|
||||
[JsonProperty("Category")]
|
||||
public string Category { get; set; }
|
||||
[JsonProperty("EventId")]
|
||||
public string EventId { get; set; }
|
||||
[JsonProperty("LogLevel")]
|
||||
public LogLevel LogLevel { get; set; }
|
||||
[JsonProperty("Message")]
|
||||
public string Message { get; set; }
|
||||
public string StackTrace { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public enum LogLevel
|
||||
{
|
||||
Information,
|
||||
Warning,
|
||||
Error,
|
||||
Critical
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public sealed class Screen
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Rect Size { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ namespace Daybreak.Models
|
||||
public sealed class UpdateStatus : INotifyPropertyChanged
|
||||
{
|
||||
public static readonly UpdateStep StartingStep = new("Starting");
|
||||
public static readonly UpdateStep InitializingDownload = new("Initializing download");
|
||||
public static readonly UpdateStep CheckingLatestVersion = new("Checking latest version");
|
||||
public static UpdateStep Downloading(double progress) => new DownloadUpdateStep("Downloading", progress);
|
||||
public static readonly UpdateStep DownloadFinished = new("Download finished. Application will restart in order to apply the update.");
|
||||
public static readonly UpdateStep DownloadFinished = new("Download finished. Application will restart in order to apply the update");
|
||||
public static readonly UpdateStep FailedDownload = new("Update failed. Please check logs for details");
|
||||
|
||||
private UpdateStep currentStep = StartingStep;
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
|
||||
namespace Daybreak.Models.Versioning
|
||||
{
|
||||
public sealed class Version
|
||||
{
|
||||
private List<VersionToken> parts = new();
|
||||
|
||||
public bool HasPrefix { get; private set; }
|
||||
public IEnumerable<VersionToken> VersionTokens { get => this.parts; }
|
||||
public string VersionString { get => this.ToString(); }
|
||||
|
||||
private Version()
|
||||
{
|
||||
}
|
||||
|
||||
public Version(string version)
|
||||
{
|
||||
if (TryParseParts(version, out var parts, out var hasPrefix) is false)
|
||||
{
|
||||
throw new ArgumentException($"Provided argument is not valid: {version}");
|
||||
}
|
||||
|
||||
this.parts = parts;
|
||||
this.HasPrefix = hasPrefix;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var version = string.Join('.', parts.OfType<VersionNumberToken>());
|
||||
if (parts.Last() is VersionStringToken)
|
||||
{
|
||||
version += "-" + parts.Last();
|
||||
}
|
||||
|
||||
if (this.HasPrefix)
|
||||
{
|
||||
version = 'v' + version;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
public static Version Parse(string version)
|
||||
{
|
||||
if (TryParse(version, out var parsedVersion) is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to parse version: {version}");
|
||||
}
|
||||
|
||||
return parsedVersion;
|
||||
}
|
||||
|
||||
public static bool TryParse(string version, out Version parsedVersion)
|
||||
{
|
||||
if (TryParseParts(version, out var parts, out var hasPrefix) is false)
|
||||
{
|
||||
parsedVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = parts.Count - 1; i >= 1; i--)
|
||||
{
|
||||
if (parts[i] is VersionNumberToken && parts[i].ToString() == "0")
|
||||
{
|
||||
parts.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
parsedVersion = new Version
|
||||
{
|
||||
parts = parts,
|
||||
HasPrefix = hasPrefix
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseParts(string version, out List<VersionToken> parts, out bool hasPrefix)
|
||||
{
|
||||
hasPrefix = false;
|
||||
parts = null;
|
||||
if (version.IsNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version.StartsWith('.') || version.EndsWith('.'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var tokens = version.Trim().Split('.');
|
||||
if (tokens.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tokens[0].StartsWith('v'))
|
||||
{
|
||||
hasPrefix = true;
|
||||
tokens[0] = tokens[0][1..];
|
||||
}
|
||||
|
||||
parts = new List<VersionToken>();
|
||||
for(int i = 0; i < tokens.Length - 1; i++)
|
||||
{
|
||||
var token = tokens[i];
|
||||
if (token.IsNullOrWhiteSpace() || token.All(c => char.IsDigit(c)) is false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add(VersionToken.Parse(token));
|
||||
}
|
||||
|
||||
var lastToken = tokens[^1];
|
||||
if (lastToken.Contains('-'))
|
||||
{
|
||||
var lastTokenParts = lastToken.Split('-');
|
||||
if (lastTokenParts.Length > 2 || lastTokenParts.Length < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lastTokenParts[0].IsNullOrWhiteSpace() || lastTokenParts[0].All(c => char.IsDigit(c)) is false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add(VersionToken.Parse(lastTokenParts[0]));
|
||||
if (lastTokenParts[1].IsNullOrWhiteSpace() || lastTokenParts[1].All(c => char.IsLetter(c)) is false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add(VersionToken.Parse(lastTokenParts[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lastToken.IsNullOrWhiteSpace() || lastToken.Any(c => char.IsDigit(c) is false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add(VersionToken.Parse(lastToken));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Daybreak.Models.Versioning
|
||||
{
|
||||
public sealed class VersionNumberToken : VersionToken
|
||||
{
|
||||
public int Number { get; }
|
||||
internal VersionNumberToken(int number)
|
||||
{
|
||||
this.Number = number;
|
||||
}
|
||||
|
||||
protected override string Stringify()
|
||||
{
|
||||
return this.Number.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Daybreak.Models.Versioning
|
||||
{
|
||||
public sealed class VersionStringToken : VersionToken
|
||||
{
|
||||
public string Token { get; }
|
||||
internal VersionStringToken(string token)
|
||||
{
|
||||
this.Token = token;
|
||||
}
|
||||
|
||||
protected override string Stringify()
|
||||
{
|
||||
return this.Token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Daybreak.Models.Versioning
|
||||
{
|
||||
public abstract class VersionToken
|
||||
{
|
||||
public static VersionToken Parse(string token)
|
||||
{
|
||||
if (token.All(c => char.IsDigit(c)))
|
||||
{
|
||||
var number = int.Parse(token);
|
||||
return new VersionNumberToken(number);
|
||||
}
|
||||
|
||||
if (token.All(c => char.IsLetter(c)))
|
||||
{
|
||||
return new VersionStringToken(token);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Failed to parse version token {token}");
|
||||
}
|
||||
|
||||
protected abstract string Stringify();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Stringify();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using Daybreak.Exceptions;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Credentials;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.Mutex;
|
||||
using Daybreak.Utils;
|
||||
using Daybreak.Services.Privilege;
|
||||
using Daybreak.Views;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Win32;
|
||||
using Pepa.Wpf.Utilities;
|
||||
using System;
|
||||
@@ -15,11 +16,13 @@ using System.Linq;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace Daybreak.Services.ApplicationLauncher
|
||||
{
|
||||
public class ApplicationLauncher : IApplicationLauncher
|
||||
{
|
||||
private const int MaxRetries = 10;
|
||||
private const string TexModProcessName = "TexMod";
|
||||
private const string UModProcessName = "uMod";
|
||||
private const string ToolboxProcessName = "GWToolbox";
|
||||
@@ -29,7 +32,8 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly ICredentialManager credentialManager;
|
||||
private readonly IMutexHandler mutexHandler;
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<ApplicationLauncher> logger;
|
||||
private readonly IPrivilegeManager privilegeManager;
|
||||
|
||||
public bool IsTexmodRunning => TexModProcessDetected();
|
||||
public bool IsGuildwarsRunning => this.GuildwarsProcessDetected();
|
||||
@@ -39,32 +43,41 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
IConfigurationManager configurationManager,
|
||||
ICredentialManager credentialManager,
|
||||
IMutexHandler mutexHandler,
|
||||
ILogger logger)
|
||||
ILogger<ApplicationLauncher> logger,
|
||||
IPrivilegeManager privilegeManager)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.mutexHandler = mutexHandler.ThrowIfNull(nameof(mutexHandler));
|
||||
this.credentialManager = credentialManager.ThrowIfNull(nameof(credentialManager));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.privilegeManager = privilegeManager.ThrowIfNull(nameof(privilegeManager));
|
||||
}
|
||||
|
||||
public async Task LaunchGuildwars()
|
||||
public async Task<bool> LaunchGuildwars()
|
||||
{
|
||||
var configuration = this.configurationManager.GetConfiguration();
|
||||
var auth = await this.credentialManager.GetDefaultCredentials().ConfigureAwait(false);
|
||||
auth.Do(
|
||||
onSome: (credentials) =>
|
||||
return await auth.Switch(
|
||||
onSome: async (credentials) =>
|
||||
{
|
||||
if (configuration.ExperimentalFeatures.MultiLaunchSupport is true)
|
||||
{
|
||||
if (this.privilegeManager.AdminPrivileges is false)
|
||||
{
|
||||
this.privilegeManager.RequestAdminPrivileges<MainView>("You need administrator rights in order to start using multi-launch");
|
||||
return false;
|
||||
}
|
||||
|
||||
ClearGwLocks();
|
||||
}
|
||||
|
||||
LaunchGuildwarsProcess(credentials.Username, credentials.Password, credentials.CharacterName);
|
||||
return await LaunchGuildwarsProcess(credentials.Username, credentials.Password, credentials.CharacterName);
|
||||
},
|
||||
onNone: () =>
|
||||
{
|
||||
throw new CredentialsNotFoundException($"No credentials available");
|
||||
});
|
||||
})
|
||||
.ExtractValue();
|
||||
}
|
||||
|
||||
public Task LaunchGuildwarsToolbox()
|
||||
@@ -103,7 +116,34 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
});
|
||||
}
|
||||
|
||||
private void LaunchGuildwarsProcess(string email, Models.SecureString password, string character)
|
||||
public void RestartDaybreakAsAdmin()
|
||||
{
|
||||
this.logger.LogInformation("Restarting daybreak with admin rights");
|
||||
var processName = Process.GetCurrentProcess()?.MainModule?.FileName;
|
||||
if (processName.IsNullOrWhiteSpace() || File.Exists(processName) is false)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to find executable. Aborting restart");
|
||||
}
|
||||
|
||||
var process = new Process()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
Verb = "runas",
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
UseShellExecute = true,
|
||||
FileName = processName
|
||||
}
|
||||
};
|
||||
if (process.Start() is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to start {processName} as admin");
|
||||
}
|
||||
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
private async Task<bool> LaunchGuildwarsProcess(string email, Models.SecureString password, string character)
|
||||
{
|
||||
var executable = this.configurationManager.GetConfiguration().GuildwarsPaths.Where(path => path.Default).FirstOrDefault();
|
||||
if (executable is null)
|
||||
@@ -129,10 +169,54 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
args.Add(character);
|
||||
}
|
||||
|
||||
if (Process.Start(executable.Path, args) is null)
|
||||
var identity = this.configurationManager.GetConfiguration().ExperimentalFeatures.LaunchGuildwarsAsCurrentUser ?
|
||||
System.Security.Principal.WindowsIdentity.GetCurrent().Name :
|
||||
System.Security.Principal.WindowsIdentity.GetAnonymous().Name;
|
||||
this.logger.LogInformation($"Launching guildwars as [{identity}] identity");
|
||||
var process = new Process()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
Arguments = string.Join(" ", args),
|
||||
FileName = executable.Path
|
||||
}
|
||||
};
|
||||
if (process.Start() is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to launch {executable}");
|
||||
}
|
||||
|
||||
var retries = 0;
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
retries++;
|
||||
var gwProcess = Process.GetProcessesByName("gw").FirstOrDefault();
|
||||
if (gwProcess is null && retries < MaxRetries)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (gwProcess is null && retries >= MaxRetries)
|
||||
{
|
||||
throw new InvalidOperationException("Newly launched gw process not detected");
|
||||
}
|
||||
|
||||
if (gwProcess.MainWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int titleLength = NativeMethods.GetWindowTextLength(gwProcess.MainWindowHandle);
|
||||
var titleBuffer = new StringBuilder(titleLength);
|
||||
var readCount = NativeMethods.GetWindowText(gwProcess.MainWindowHandle, titleBuffer, titleLength + 1);
|
||||
var title = titleBuffer.ToString();
|
||||
if (title != "Guild Wars")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool GuildwarsProcessDetected()
|
||||
|
||||
@@ -7,8 +7,9 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
bool IsGuildwarsRunning { get; }
|
||||
bool IsToolboxRunning { get; }
|
||||
bool IsTexmodRunning { get; }
|
||||
Task LaunchGuildwars();
|
||||
Task<bool> LaunchGuildwars();
|
||||
Task LaunchGuildwarsToolbox();
|
||||
Task LaunchTexmod();
|
||||
void RestartDaybreakAsAdmin();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Daybreak.Services.Bloogum.Models;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Http;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.Bloogum
|
||||
@@ -12,15 +11,16 @@ namespace Daybreak.Services.Bloogum
|
||||
public sealed class BloogumClient : IBloogumClient
|
||||
{
|
||||
private const string BaseAddress = "http://bloogum.net/guildwars";
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly IHttpClient<BloogumClient> httpClient;
|
||||
private readonly ILogger logger;
|
||||
private readonly Random random = new();
|
||||
|
||||
public BloogumClient(ILogger logger)
|
||||
public BloogumClient(
|
||||
ILogger<BloogumClient> logger,
|
||||
IHttpClient<BloogumClient> httpClient)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
|
||||
this.httpClient = new HttpClient();
|
||||
this.httpClient = httpClient.ThrowIfNull(nameof(httpClient));
|
||||
}
|
||||
|
||||
public async Task<Optional<Stream>> GetRandomScreenShot()
|
||||
@@ -53,7 +53,7 @@ namespace Daybreak.Services.Bloogum
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
this.logger.LogError(e);
|
||||
this.logger.LogError(e.ToString());
|
||||
return Optional.None<Stream>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,13 +81,13 @@ namespace Daybreak.Services.Bloogum.Models
|
||||
new Category("stingraystrand", 15),
|
||||
new Category("fishermenshaven", 4),
|
||||
new Category("riversideprovince", 31),
|
||||
new Category("sanctumcay", 21)
|
||||
new Category("sanctumcay", 21),
|
||||
new Category("majestysrest", 14)
|
||||
});
|
||||
public static readonly Location MaguumaJungle = new(
|
||||
"maguuma",
|
||||
new List<Category>
|
||||
{
|
||||
new Category("majestysrest", 14),
|
||||
new Category("druidsoverlook", 1),
|
||||
new Category("sagelands", 27),
|
||||
new Category("thewilds", 19),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Services.BuildTemplates.Models;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
@@ -16,10 +15,10 @@ namespace Daybreak.Services.BuildTemplates
|
||||
private const string DecodingLookupTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
private readonly static string BuildsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Guild Wars\\Templates\\Skills";
|
||||
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<BuildTemplateManager> logger;
|
||||
|
||||
public BuildTemplateManager(
|
||||
ILogger logger)
|
||||
ILogger<BuildTemplateManager> logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
}
|
||||
@@ -68,13 +67,12 @@ namespace Daybreak.Services.BuildTemplates
|
||||
public void SaveBuild(BuildEntry buildEntry)
|
||||
{
|
||||
var encodedBuild = this.EncodeTemplate(buildEntry.Build);
|
||||
File.WriteAllText($"{BuildsPath}\\{buildEntry.Name}.txt", encodedBuild);
|
||||
if (string.IsNullOrWhiteSpace(buildEntry.PreviousName))
|
||||
{
|
||||
return;
|
||||
File.Delete($"{BuildsPath}\\{buildEntry.PreviousName}.txt");
|
||||
}
|
||||
|
||||
File.Delete($"{BuildsPath}\\{buildEntry.PreviousName}.txt");
|
||||
File.WriteAllText($"{BuildsPath}\\{buildEntry.Name}.txt", encodedBuild);
|
||||
}
|
||||
|
||||
public void RemoveBuild(BuildEntry buildEntry)
|
||||
@@ -230,8 +228,10 @@ namespace Daybreak.Services.BuildTemplates
|
||||
{
|
||||
var curedTemplate = template.Trim();
|
||||
|
||||
var buildMetadata = new BuildMetadata();
|
||||
buildMetadata.Base64Decoded = template.Select(c => DecodingLookupTable.IndexOf(c)).ToList();
|
||||
var buildMetadata = new BuildMetadata
|
||||
{
|
||||
Base64Decoded = template.Select(c => DecodingLookupTable.IndexOf(c)).ToList(),
|
||||
};
|
||||
buildMetadata.BinaryDecoded = buildMetadata.Base64Decoded.Select(b => ToBitString(b)).ToList();
|
||||
|
||||
var stream = new DecodeCharStream(buildMetadata.BinaryDecoded.ToArray());
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Daybreak.Configuration;
|
||||
using Daybreak.Exceptions;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.IO;
|
||||
@@ -13,9 +12,11 @@ namespace Daybreak.Services.Configuration
|
||||
private const string ConfigName = "Daybreak.config.json";
|
||||
|
||||
private ApplicationConfiguration applicationConfiguration;
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<ConfigurationManager> logger;
|
||||
|
||||
public ConfigurationManager(ILogger logger)
|
||||
public event EventHandler ConfigurationChanged;
|
||||
|
||||
public ConfigurationManager(ILogger<ConfigurationManager> logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
try
|
||||
@@ -39,6 +40,7 @@ namespace Daybreak.Services.Configuration
|
||||
{
|
||||
File.WriteAllText(ConfigName, applicationConfiguration.Serialize());
|
||||
this.applicationConfiguration = applicationConfiguration;
|
||||
this.ConfigurationChanged?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Daybreak.Configuration;
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Services.Configuration
|
||||
{
|
||||
public interface IConfigurationManager
|
||||
{
|
||||
event EventHandler ConfigurationChanged;
|
||||
ApplicationConfiguration GetConfiguration();
|
||||
void SaveConfiguration(ApplicationConfiguration applicationConfiguration);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
@@ -15,11 +14,11 @@ namespace Daybreak.Services.Credentials
|
||||
public sealed class CredentialManager : ICredentialManager
|
||||
{
|
||||
private static readonly byte[] Entropy = Convert.FromBase64String("R3VpbGR3YXJz");
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<CredentialManager> logger;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
|
||||
public CredentialManager(
|
||||
ILogger logger,
|
||||
ILogger<CredentialManager> logger,
|
||||
IConfigurationManager configurationManager)
|
||||
{
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using HtmlAgilityPack;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Http;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.IconRetrieve
|
||||
@@ -17,13 +16,15 @@ namespace Daybreak.Services.IconRetrieve
|
||||
private const string BaseUrl = "https://wiki.guildwars.com";
|
||||
private const string QueryUrl = $"wiki/File:{NamePlaceholder}.jpg";
|
||||
|
||||
private readonly HttpClient httpClient = new();
|
||||
private readonly ILogger logger;
|
||||
private readonly IHttpClient<IconRetriever> httpClient;
|
||||
private readonly ILogger<IconRetriever> logger;
|
||||
|
||||
public IconRetriever(
|
||||
ILogger logger)
|
||||
ILogger<IconRetriever> logger,
|
||||
IHttpClient<IconRetriever> httpClient)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.httpClient = httpClient.ThrowIfNull(nameof(httpClient));
|
||||
this.httpClient.BaseAddress = new Uri(BaseUrl);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.ApplicationLifetime;
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Services.KeyboardHook
|
||||
{
|
||||
public interface IKeyboardHookService : IApplicationLifetimeService
|
||||
{
|
||||
event EventHandler<KeyboardHookEventArgs> KeyboardPressed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using Daybreak.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Pepa.Wpf.Utilities;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Daybreak.Services.KeyboardHook
|
||||
{
|
||||
// Based on https://gist.github.com/Stasonix
|
||||
// https://stackoverflow.com/questions/604410/global-keyboard-capture-in-c-sharp-application
|
||||
public sealed class KeyboardHookService : IKeyboardHookService, IDisposable
|
||||
{
|
||||
private readonly ILogger<KeyboardHookService> logger;
|
||||
private IntPtr windowsHookHandle;
|
||||
private IntPtr user32LibraryHandle;
|
||||
private NativeMethods.HookProc hookProc;
|
||||
|
||||
public event EventHandler<KeyboardHookEventArgs> KeyboardPressed;
|
||||
|
||||
public KeyboardHookService(
|
||||
ILogger<KeyboardHookService> logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.Setup();
|
||||
}
|
||||
|
||||
public void OnStartup()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnClosing()
|
||||
{
|
||||
this.Dispose();
|
||||
}
|
||||
|
||||
private void Setup()
|
||||
{
|
||||
this.windowsHookHandle = IntPtr.Zero;
|
||||
this.hookProc = this.LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.
|
||||
|
||||
this.user32LibraryHandle = NativeMethods.LoadLibrary("User32");
|
||||
if (this.user32LibraryHandle == IntPtr.Zero)
|
||||
{
|
||||
int errorCode = Marshal.GetLastWin32Error();
|
||||
this.logger.LogError($"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
|
||||
}
|
||||
|
||||
this.windowsHookHandle = NativeMethods.SetWindowsHookEx(NativeMethods.WH_KEYBOARD_LL, this.hookProc, this.user32LibraryHandle, 0);
|
||||
if (this.windowsHookHandle == IntPtr.Zero)
|
||||
{
|
||||
int errorCode = Marshal.GetLastWin32Error();
|
||||
this.logger.LogError($"Failed to adjust keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
var handled = false;
|
||||
var wparamTyped = wParam.ToInt32();
|
||||
if (Enum.IsDefined(typeof(KeyboardState), wparamTyped))
|
||||
{
|
||||
var p = Marshal.PtrToStructure(lParam, typeof(KeyboardInput)).Cast<KeyboardInput>();
|
||||
var eventArguments = new KeyboardHookEventArgs(wparamTyped.Cast<KeyboardState>(), p);
|
||||
this.KeyboardPressed?.Invoke(this, eventArguments);
|
||||
handled = eventArguments.Handled;
|
||||
}
|
||||
|
||||
return handled ? (IntPtr)1 : NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// because we can unhook only in the same thread, not in garbage collector thread
|
||||
if (this.windowsHookHandle != IntPtr.Zero)
|
||||
{
|
||||
if (NativeMethods.UnhookWindowsHookEx(this.windowsHookHandle) is false)
|
||||
{
|
||||
int errorCode = Marshal.GetLastWin32Error();
|
||||
this.logger.LogCritical($"Failed to remove keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
|
||||
}
|
||||
|
||||
this.windowsHookHandle = IntPtr.Zero;
|
||||
this.hookProc -= LowLevelKeyboardProc;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.user32LibraryHandle != IntPtr.Zero)
|
||||
{
|
||||
if (NativeMethods.FreeLibrary(this.user32LibraryHandle) is false) // reduces reference to library by 1.
|
||||
{
|
||||
int errorCode = Marshal.GetLastWin32Error();
|
||||
this.logger.LogCritical($"Failed to unload library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
|
||||
}
|
||||
|
||||
this.user32LibraryHandle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
~KeyboardHookService()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Daybreak.Services.ApplicationLifetime;
|
||||
|
||||
namespace Daybreak.Services.KeyboardMacros
|
||||
{
|
||||
public interface IMacroService : IApplicationLifetimeService
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.KeyboardHook;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Pepa.Wpf.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Daybreak.Services.KeyboardMacros
|
||||
{
|
||||
public sealed class MacroService : IMacroService
|
||||
{
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new();
|
||||
private readonly IKeyboardHookService keyboardHookService;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly HashSet<Keys> KeysDown = new();
|
||||
|
||||
private IEnumerable<KeyMacro> loadedMacros;
|
||||
private bool gameActive, hookEnabled;
|
||||
private IntPtr gwWindowHwnd;
|
||||
|
||||
public MacroService(
|
||||
IKeyboardHookService keyboardHookService,
|
||||
IConfigurationManager configurationManager)
|
||||
{
|
||||
this.keyboardHookService = keyboardHookService.ThrowIfNull(nameof(keyboardHookService));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
|
||||
this.configurationManager.ConfigurationChanged += (s, e) => this.LoadConfiguration();
|
||||
this.LoadConfiguration();
|
||||
this.keyboardHookService.KeyboardPressed += this.KeyboardHookService_KeyboardPressed;
|
||||
this.SetupGameActiveChecker();
|
||||
}
|
||||
|
||||
private void LoadConfiguration()
|
||||
{
|
||||
this.hookEnabled = this.configurationManager.GetConfiguration().ExperimentalFeatures.CanInterceptKeys;
|
||||
this.loadedMacros = this.configurationManager.GetConfiguration().ExperimentalFeatures.Macros ?? new List<KeyMacro>();
|
||||
}
|
||||
|
||||
private void SetupGameActiveChecker()
|
||||
{
|
||||
TaskExtensions.RunPeriodicAsync(() =>
|
||||
{
|
||||
var windowHandle = NativeMethods.GetForegroundWindow();
|
||||
var windowNameLength = NativeMethods.GetWindowTextLength(windowHandle);
|
||||
var sb = new StringBuilder(windowNameLength);
|
||||
_ = NativeMethods.GetWindowText(windowHandle, sb, windowNameLength + 1);
|
||||
if (sb.ToString() != "Guild Wars")
|
||||
{
|
||||
this.gameActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.gwWindowHwnd = windowHandle;
|
||||
this.gameActive = true;
|
||||
},
|
||||
TimeSpan.Zero,
|
||||
TimeSpan.FromMilliseconds(33),
|
||||
cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
private void KeyboardHookService_KeyboardPressed(object sender, KeyboardHookEventArgs e)
|
||||
{
|
||||
if (this.gameActive && this.hookEnabled)
|
||||
{
|
||||
if (e.KeyboardState == KeyboardState.KeyDown)
|
||||
{
|
||||
this.KeysDown.Add(e.KeyboardInput.Key);
|
||||
}
|
||||
else if (e.KeyboardState == KeyboardState.KeyUp)
|
||||
{
|
||||
this.KeysDown.Remove(e.KeyboardInput.Key);
|
||||
return;
|
||||
}
|
||||
|
||||
e.Handled = this.loadedMacros
|
||||
.Where(keyMacro => MacroContainsKey(keyMacro, e.KeyboardInput.Key))
|
||||
.Where(this.MacroHit)
|
||||
.Do(this.HandleMacro)
|
||||
.Any();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnClosing()
|
||||
{
|
||||
this.cancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
public void OnStartup()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
private bool MacroHit(KeyMacro keyMacro)
|
||||
{
|
||||
return this.KeysDown.Intersect(keyMacro.Keys).OrderBy(key => key).SequenceEqual(keyMacro.Keys.OrderBy(key => key));
|
||||
}
|
||||
|
||||
private void HandleMacro(KeyMacro keyMacro)
|
||||
{
|
||||
// TODO: Propagate key to guildwars executable.
|
||||
}
|
||||
|
||||
private static bool MacroContainsKey(KeyMacro keyMacro, Keys lastKey)
|
||||
{
|
||||
return keyMacro.Keys.Contains(lastKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
using Daybreak.Exceptions;
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.Logging
|
||||
{
|
||||
public class FlatLoggingDatabase : ILoggingDatabase
|
||||
{
|
||||
private const string Path = "logs.db";
|
||||
private const int bufferSize = 10;
|
||||
private const char separator = '§';
|
||||
private readonly string filePath = Path;
|
||||
private readonly List<string> buffer = new List<string>();
|
||||
|
||||
public FlatLoggingDatabase()
|
||||
{
|
||||
}
|
||||
|
||||
public Task<IEnumerable<Log>> GetLogs()
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
lock (this.buffer)
|
||||
{
|
||||
if (this.buffer.Count > 0)
|
||||
{
|
||||
this.WriteBufferToFile();
|
||||
}
|
||||
}
|
||||
|
||||
return this.GetSerializedLogs().Select(s => s.Deserialize<Log>());
|
||||
});
|
||||
}
|
||||
|
||||
public Task<IEnumerable<Log>> GetLogsByDate(DateTime startTime, DateTime endTime)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
lock (this.buffer)
|
||||
{
|
||||
if (this.buffer.Count > 0)
|
||||
{
|
||||
this.WriteBufferToFile();
|
||||
}
|
||||
}
|
||||
|
||||
return this.GetSerializedLogs()
|
||||
.Select(s => s.Deserialize<Log>())
|
||||
.Where(l => l.Timestamp > startTime && l.Timestamp < endTime);
|
||||
});
|
||||
}
|
||||
|
||||
public Task<bool> InsertLog(Log log)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
lock (this.buffer)
|
||||
{
|
||||
this.buffer.Add(log.Serialize());
|
||||
|
||||
if (this.buffer.Count > bufferSize)
|
||||
{
|
||||
this.WriteBufferToFile();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public Task<bool> ClearDatabase()
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
lock (this.buffer)
|
||||
{
|
||||
this.buffer.Clear();
|
||||
}
|
||||
lock (this.filePath)
|
||||
{
|
||||
File.WriteAllText(this.filePath, string.Empty);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void OnClosing()
|
||||
{
|
||||
this.WriteBufferToFile();
|
||||
}
|
||||
|
||||
public void OnStartup()
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var fs = File.Create(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new FatalException("Could not initialize logging. See inner exception for details.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteBufferToFile()
|
||||
{
|
||||
lock (this.filePath)
|
||||
{
|
||||
File.AppendAllLines(this.filePath, this.buffer.Select(s => s + separator), Encoding.UTF8);
|
||||
}
|
||||
this.buffer.Clear();
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetSerializedLogs()
|
||||
{
|
||||
lock (this.filePath)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
using var fileStream = File.OpenRead(this.filePath);
|
||||
using var streamReader = new StreamReader(fileStream, Encoding.UTF8);
|
||||
string serializedLog = null;
|
||||
while ((serializedLog = streamReader.ReadUntil(separator.ToString())) != null
|
||||
&& serializedLog != string.Empty
|
||||
&& serializedLog != "\r"
|
||||
&& serializedLog != "\n"
|
||||
&& serializedLog != "\r\n")
|
||||
{
|
||||
yield return serializedLog;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using Daybreak.Models;
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Services.Logging
|
||||
{
|
||||
public interface ILogger
|
||||
{
|
||||
void Log(LogLevel logLevel, Exception exception);
|
||||
void Log(LogLevel logLevel, string message);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.ApplicationLifetime;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.Logging
|
||||
{
|
||||
public interface ILoggingDatabase : IApplicationLifetimeService
|
||||
{
|
||||
Task<bool> ClearDatabase();
|
||||
Task<IEnumerable<Log>> GetLogsByDate(DateTime startTime, DateTime endTime);
|
||||
Task<IEnumerable<Log>> GetLogs();
|
||||
Task<bool> InsertLog(Log log);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Daybreak.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Services.Logging
|
||||
{
|
||||
public interface ILogsManager
|
||||
{
|
||||
void WriteLog(Log log);
|
||||
IEnumerable<Log> GetLogs();
|
||||
int DeleteLogs();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Daybreak.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
|
||||
namespace Daybreak.Services.Logging
|
||||
{
|
||||
public sealed class JsonLogger : ILogger
|
||||
{
|
||||
private readonly string category;
|
||||
private readonly JsonLoggerProvider jsonLoggerProvider;
|
||||
|
||||
public JsonLogger(string category, JsonLoggerProvider jsonLoggerProvider)
|
||||
{
|
||||
this.category = category;
|
||||
this.jsonLoggerProvider = jsonLoggerProvider.ThrowIfNull(nameof(jsonLoggerProvider));
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
var message = formatter(state, exception);
|
||||
|
||||
var log = new Log
|
||||
{
|
||||
LogLevel = logLevel,
|
||||
EventId = eventId.Name,
|
||||
Message = message,
|
||||
Category = category,
|
||||
LogTime = DateTime.Now
|
||||
};
|
||||
|
||||
this.jsonLoggerProvider.LogEntry(log);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Daybreak.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Extensions;
|
||||
|
||||
namespace Daybreak.Services.Logging
|
||||
{
|
||||
public sealed class JsonLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private readonly ILogsManager logsManager;
|
||||
|
||||
public JsonLoggerProvider(ILogsManager logsManager)
|
||||
{
|
||||
this.logsManager = logsManager.ThrowIfNull(nameof(logsManager));
|
||||
}
|
||||
|
||||
public void LogEntry(Log log)
|
||||
{
|
||||
this.logsManager.WriteLog(log);
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new JsonLogger(categoryName, this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Daybreak.Models;
|
||||
using LiteDB;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
|
||||
namespace Daybreak.Services.Logging
|
||||
{
|
||||
public sealed class JsonLogsManager : ILogsManager
|
||||
{
|
||||
private readonly ILiteDatabase liteDatabase;
|
||||
|
||||
public JsonLogsManager(ILiteDatabase liteDatabase)
|
||||
{
|
||||
this.liteDatabase = liteDatabase.ThrowIfNull(nameof(liteDatabase));
|
||||
}
|
||||
|
||||
public IEnumerable<Log> GetLogs()
|
||||
{
|
||||
return this.liteDatabase.GetCollection<Log>().FindAll();
|
||||
}
|
||||
public void WriteLog(Log log)
|
||||
{
|
||||
this.liteDatabase.GetCollection<Log>().Insert(log);
|
||||
}
|
||||
public int DeleteLogs()
|
||||
{
|
||||
return this.liteDatabase.GetCollection<Log>().DeleteAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using Daybreak.Models;
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Services.Logging
|
||||
{
|
||||
public class Logger : ILogger
|
||||
{
|
||||
private readonly ILoggingDatabase loggingDatabase;
|
||||
public Logger(ILoggingDatabase loggingDatabase)
|
||||
{
|
||||
this.loggingDatabase = loggingDatabase;
|
||||
}
|
||||
|
||||
public async void Log(LogLevel logLevel, Exception exception)
|
||||
{
|
||||
if (exception is null) throw new ArgumentNullException(nameof(exception));
|
||||
|
||||
await this.loggingDatabase.InsertLog(
|
||||
new Log
|
||||
{
|
||||
LogLevel = logLevel,
|
||||
Message = exception.Message,
|
||||
StackTrace = exception.StackTrace,
|
||||
Timestamp = DateTime.Now
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async void Log(LogLevel logLevel, string message)
|
||||
{
|
||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||
|
||||
await this.loggingDatabase.InsertLog(
|
||||
new Log
|
||||
{
|
||||
LogLevel = logLevel,
|
||||
Message = message,
|
||||
StackTrace = Environment.StackTrace,
|
||||
Timestamp = DateTime.Now
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Windows.Controls;
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Services.ViewManagement
|
||||
{
|
||||
@@ -11,5 +12,9 @@ namespace Daybreak.Services.ViewManagement
|
||||
|
||||
void ShowView<T>(object dataContext)
|
||||
where T : UserControl;
|
||||
|
||||
void ShowView(Type type);
|
||||
|
||||
void ShowView(Type type, object dataContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Slim;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Services.ViewManagement
|
||||
@@ -32,17 +33,33 @@ namespace Daybreak.Services.ViewManagement
|
||||
|
||||
public void ShowView<T>() where T : UserControl
|
||||
{
|
||||
var view = this.serviceManager.GetService<T>();
|
||||
this.container.Children.Clear();
|
||||
this.container.Children.Add(view);
|
||||
this.ShowViewInner(typeof(T), null);
|
||||
}
|
||||
|
||||
public void ShowView<T>(object dataContext) where T : UserControl
|
||||
{
|
||||
var view = this.serviceManager.GetService<T>();
|
||||
this.container.Children.Clear();
|
||||
this.container.Children.Add(view);
|
||||
view.DataContext = dataContext;
|
||||
this.ShowViewInner(typeof(T), dataContext);
|
||||
}
|
||||
|
||||
public void ShowView(Type type)
|
||||
{
|
||||
this.ShowViewInner(type, null);
|
||||
}
|
||||
|
||||
public void ShowView(Type type, object dataContext)
|
||||
{
|
||||
this.ShowViewInner(type, dataContext);
|
||||
}
|
||||
|
||||
private void ShowViewInner(Type viewType, object dataContext)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var view = this.serviceManager.GetService(viewType).As<UserControl>();
|
||||
this.container.Children.Clear();
|
||||
this.container.Children.Add(view);
|
||||
view.DataContext = dataContext;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Services.Privilege
|
||||
{
|
||||
public interface IPrivilegeManager
|
||||
{
|
||||
bool AdminPrivileges { get; }
|
||||
|
||||
void RequestAdminPrivileges<TCancelView>(string messageToUser, object dataContextOfCancelView = null)
|
||||
where TCancelView : UserControl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Views;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Extensions;
|
||||
using System.Security.Principal;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Services.Privilege
|
||||
{
|
||||
public sealed class PrivilegeManager : IPrivilegeManager
|
||||
{
|
||||
public bool AdminPrivileges => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly ILogger<PrivilegeManager> logger;
|
||||
|
||||
public PrivilegeManager(
|
||||
IViewManager viewManager,
|
||||
ILogger<PrivilegeManager> logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
}
|
||||
|
||||
public void RequestAdminPrivileges<TCancelView>(string messageToUser, object dataContextOfCancelView = null)
|
||||
where TCancelView : UserControl
|
||||
{
|
||||
this.logger.LogInformation("Requesting admin privileges");
|
||||
this.viewManager.ShowView<RequestElevationView>(new ElevationRequest { View = typeof(TCancelView), DataContext = dataContextOfCancelView, MessageToUser = messageToUser });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Daybreak.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Services.Screens
|
||||
{
|
||||
public interface IScreenManager
|
||||
{
|
||||
IEnumerable<Screen> Screens { get; }
|
||||
void MoveGuildwarsToScreen(Screen screen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Daybreak.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Pepa.Wpf.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
|
||||
namespace Daybreak.Services.Screens
|
||||
{
|
||||
public sealed class ScreenManager : IScreenManager
|
||||
{
|
||||
private readonly ILogger<ScreenManager> logger;
|
||||
|
||||
public IEnumerable<Screen> Screens { get; } = WpfScreenHelper.Screen.AllScreens
|
||||
.Select((screen, index) => new Screen { Id = index, Size = screen.Bounds });
|
||||
|
||||
public ScreenManager(
|
||||
ILogger<ScreenManager> logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
}
|
||||
|
||||
public void MoveGuildwarsToScreen(Screen screen)
|
||||
{
|
||||
this.logger.LogInformation($"Attempting to move guildwars to screen {screen.Id}");
|
||||
var hwnd = GetMainWindowHandle();
|
||||
NativeMethods.SetWindowPos(hwnd, NativeMethods.HWND_TOP, screen.Size.Left.ToInt(), screen.Size.Top.ToInt(), screen.Size.Width.ToInt(), screen.Size.Height.ToInt(), NativeMethods.SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
private static IntPtr GetMainWindowHandle()
|
||||
{
|
||||
var process = Process.GetProcessesByName("gw").FirstOrDefault();
|
||||
return process is not null ? process.MainWindowHandle : throw new InvalidOperationException("Could not find guildwars process");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
@@ -14,11 +13,11 @@ namespace Daybreak.Services.Screenshots
|
||||
{
|
||||
private const string ScreenshotsFolder = "Screenshots";
|
||||
|
||||
private readonly List<string> Screenshots = new List<string>();
|
||||
private readonly ILogger logger;
|
||||
private readonly List<string> Screenshots = new();
|
||||
private readonly ILogger<ScreenshotProvider> logger;
|
||||
private int innerCount = 0;
|
||||
|
||||
public ScreenshotProvider(ILogger logger)
|
||||
public ScreenshotProvider(ILogger<ScreenshotProvider> logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
if (Directory.Exists(ScreenshotsFolder) is false)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using Daybreak.Services.ApplicationLifetime;
|
||||
|
||||
namespace Daybreak.Services.Shortcuts
|
||||
{
|
||||
public interface IShortcutManager : IApplicationLifetimeService
|
||||
{
|
||||
bool ShortcutEnabled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using Daybreak.Services.Configuration;
|
||||
using ShellLink;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.IO;
|
||||
|
||||
namespace Daybreak.Services.Shortcuts
|
||||
{
|
||||
public sealed class ShortcutManager : IShortcutManager
|
||||
{
|
||||
private const string ShortcutName = "Daybreak.lnk";
|
||||
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
|
||||
public bool ShortcutEnabled {
|
||||
get => this.ShortcutExists();
|
||||
set
|
||||
{
|
||||
if (value is true)
|
||||
{
|
||||
this.CreateShortcut();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.RemoveShortcut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ShortcutManager(IConfigurationManager configurationManager)
|
||||
{
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.configurationManager.ConfigurationChanged += (_, _) => this.LoadConfiguration();
|
||||
this.LoadConfiguration();
|
||||
}
|
||||
|
||||
private void LoadConfiguration()
|
||||
{
|
||||
var shortcutEnabled = this.configurationManager.GetConfiguration().PlaceShortcut;
|
||||
if (shortcutEnabled && this.ShortcutEnabled is false)
|
||||
{
|
||||
this.ShortcutEnabled = true;
|
||||
}
|
||||
else if (shortcutEnabled is false && this.ShortcutEnabled is true)
|
||||
{
|
||||
this.ShortcutEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShortcutExists()
|
||||
{
|
||||
var shortcutFolder = this.configurationManager.GetConfiguration().ShortcutLocation;
|
||||
var shortcutPath = $"{shortcutFolder}\\{ShortcutName}";
|
||||
if (File.Exists(shortcutPath))
|
||||
{
|
||||
var shortcut = Shortcut.ReadFromFile(shortcutPath);
|
||||
var currentExecutable = Process.GetCurrentProcess()?.MainModule?.FileName;
|
||||
if (shortcut.ExtraData?.EnvironmentVariableDataBlock?.TargetAnsi?.Equals(currentExecutable) is true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CreateShortcut()
|
||||
{
|
||||
if (this.ShortcutExists())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var shortcutFolder = this.configurationManager.GetConfiguration().ShortcutLocation;
|
||||
var shortcutPath = $"{shortcutFolder}\\{ShortcutName}";
|
||||
var currentExecutable = Process.GetCurrentProcess()?.MainModule?.FileName;
|
||||
var shortcut = Shortcut.CreateShortcut(currentExecutable);
|
||||
shortcut.StringData = new ShellLink.Structures.StringData
|
||||
{
|
||||
WorkingDir = Path.GetDirectoryName(currentExecutable),
|
||||
RelativePath = "Daybreak.exe"
|
||||
};
|
||||
shortcut.WriteToFile(shortcutPath);
|
||||
}
|
||||
|
||||
private void RemoveShortcut()
|
||||
{
|
||||
if (this.ShortcutExists() is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var shortcutFolder = this.configurationManager.GetConfiguration().ShortcutLocation;
|
||||
var shortcutPath = $"{shortcutFolder}\\{ShortcutName}";
|
||||
File.Delete(shortcutPath);
|
||||
}
|
||||
|
||||
public void OnStartup()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnClosing()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Exceptions;
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Models.Github;
|
||||
using Daybreak.Services.Runtime;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Utils;
|
||||
using Daybreak.Views;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.Http;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
@@ -16,11 +19,13 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Version = Daybreak.Models.Versioning.Version;
|
||||
|
||||
namespace Daybreak.Services.Updater
|
||||
{
|
||||
public sealed class ApplicationUpdater : IApplicationUpdater
|
||||
{
|
||||
private const string LaunchActionName = "Launch_Daybreak";
|
||||
private const string UpdateDesiredKey = "UpdateDesired";
|
||||
private const string ExecutionPolicyKey = "ExecutionPolicy";
|
||||
private const string UpdatedKey = "Updating";
|
||||
@@ -32,57 +37,75 @@ namespace Daybreak.Services.Updater
|
||||
private const string OutputPathTag = "{OUTPUTPATH}";
|
||||
private const string ExecutionPolicyTag = "{EXECUTIONPOLICY}";
|
||||
private const string ProcessIdTag = "{PROCESSID}";
|
||||
private const string ExecutableNameTag = "{EXECUTABLE}";
|
||||
private const string WorkingDirectoryTag = "{WORKINGDIRECTORY}";
|
||||
private const string RefTagPrefix = "/refs/tags";
|
||||
private const string VersionListUrl = "https://api.github.com/repos/AlexMacocian/Daybreak/git/refs/tags";
|
||||
private const string Url = "https://github.com/AlexMacocian/Daybreak/releases/latest";
|
||||
private const string DownloadUrl = $"https://github.com/AlexMacocian/Daybreak/releases/download/v{VersionTag}/Daybreakv{VersionTag}.zip";
|
||||
private const string DownloadUrl = $"https://github.com/AlexMacocian/Daybreak/releases/download/{VersionTag}/Daybreak{VersionTag}.zip";
|
||||
private const string GetExecutionPolicyCommand = "Get-ExecutionPolicy -Scope CurrentUser";
|
||||
private const string SetExecutionPolicyCommand = $"Set-ExecutionPolicy {ExecutionPolicyTag} -Scope CurrentUser";
|
||||
private const string WaitCommand = $"Wait-Process -Id {ProcessIdTag}";
|
||||
private const string ExtractCommandTemplate = $"Expand-Archive -Path '{InputFileTag}' -DestinationPath '{OutputPathTag}' -Force";
|
||||
private const string RunClientCommand = @".\Daybreak.exe";
|
||||
private const string PrepareScheduledAction = $"$action = New-ScheduledTaskAction -Execute {ExecutableNameTag} -WorkingDirectory {WorkingDirectoryTag}";
|
||||
private const string PrepareTriggerForAction = $"$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date)";
|
||||
private const string RegisterScheduledAction = $"Register-ScheduledTask -Action $action -Trigger $trigger -TaskName {LaunchActionName} | Out-Null";
|
||||
private const string LaunchScheduledAction = $"Start-ScheduledTask -TaskName {LaunchActionName}";
|
||||
private const string SleepOneSecond = $"Start-Sleep -s 1";
|
||||
private const string UnregisterScheduledAction = $"Unregister-ScheduledTask -TaskName {LaunchActionName} -Confirm:$false";
|
||||
private const string RemoveTempFile = $"Remove-item {TempFile}";
|
||||
private const string RemovePs1 = $"Remove-item {ExtractAndRunPs1}";
|
||||
|
||||
private readonly CancellationTokenSource updateCancellationTokenSource = new();
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<ApplicationUpdater> logger;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IRuntimeStore runtimeStore;
|
||||
private readonly HttpClient httpClient = new();
|
||||
private readonly IHttpClient<ApplicationUpdater> httpClient;
|
||||
|
||||
public string CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
public Version CurrentVersion { get; }
|
||||
|
||||
public ApplicationUpdater(
|
||||
ILogger logger,
|
||||
ILogger<ApplicationUpdater> logger,
|
||||
IRuntimeStore runtimeStore,
|
||||
IViewManager viewManager)
|
||||
IViewManager viewManager,
|
||||
IHttpClient<ApplicationUpdater> httpClient)
|
||||
{
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.runtimeStore = runtimeStore.ThrowIfNull(nameof(runtimeStore));
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.httpClient = httpClient.ThrowIfNull(nameof(httpClient));
|
||||
this.httpClient.DefaultRequestHeaders.Add("user-agent", "Daybreak Client");
|
||||
if (Version.TryParse(Assembly.GetExecutingAssembly().GetName().Version.ToString(), out var currentVersion))
|
||||
{
|
||||
if (currentVersion.HasPrefix is false)
|
||||
{
|
||||
currentVersion = Version.Parse("v" + currentVersion);
|
||||
}
|
||||
|
||||
this.CurrentVersion = currentVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FatalException($"Application version is invalid: {Assembly.GetExecutingAssembly().GetName().Version}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadUpdate(UpdateStatus updateStatus)
|
||||
public async Task<bool> DownloadUpdate(Version version, UpdateStatus updateStatus)
|
||||
{
|
||||
updateStatus.CurrentStep = UpdateStatus.CheckingLatestVersion;
|
||||
var latestVersion = (await this.GetLatestVersion()).ExtractValue();
|
||||
if (latestVersion is null)
|
||||
updateStatus.CurrentStep = UpdateStatus.InitializingDownload;
|
||||
var uri = DownloadUrl.Replace(VersionTag, version.ToString());
|
||||
using var response = await this.httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
|
||||
if (response.IsSuccessStatusCode is false)
|
||||
{
|
||||
this.logger.LogWarning("Failed to retrieve latest version. Aborting update");
|
||||
return false;
|
||||
}
|
||||
|
||||
using var downloadLatestResponse = await this.httpClient.GetAsync(
|
||||
DownloadUrl.Replace(VersionTag, latestVersion));
|
||||
|
||||
if (downloadLatestResponse.IsSuccessStatusCode is false)
|
||||
{
|
||||
this.logger.LogWarning("Failed to download latest version. Aborting udpate");
|
||||
updateStatus.CurrentStep = UpdateStatus.FailedDownload;
|
||||
this.logger.LogError($"Failed to download update. Details: {await response.Content.ReadAsStringAsync()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
using var downloadStream = await this.httpClient.GetStreamAsync(uri);
|
||||
this.logger.LogInformation("Beginning update download");
|
||||
var downloadStream = await downloadLatestResponse.Content.ReadAsStreamAsync();
|
||||
var fileStream = File.OpenWrite(TempFile);
|
||||
var downloadSize = (double)downloadStream.Length;
|
||||
var downloadSize = (double)response.Content.Headers.ContentLength;
|
||||
var buffer = new byte[1024];
|
||||
var length = 0;
|
||||
double downloaded = 0;
|
||||
@@ -105,12 +128,29 @@ namespace Daybreak.Services.Updater
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadLatestUpdate(UpdateStatus updateStatus)
|
||||
{
|
||||
updateStatus.CurrentStep = UpdateStatus.CheckingLatestVersion;
|
||||
var latestVersion = (await this.GetLatestVersion()).ExtractValue();
|
||||
if (latestVersion is null)
|
||||
{
|
||||
this.logger.LogWarning("Failed to retrieve latest version. Aborting update");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Version.TryParse(latestVersion, out var parsedVersion) is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not parse retrieved version: {latestVersion}");
|
||||
}
|
||||
|
||||
return await this.DownloadUpdate(parsedVersion, updateStatus);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAvailable()
|
||||
{
|
||||
var version = string.Join('.', this.CurrentVersion.Split('.'));
|
||||
var maybeLatestVersion = await this.GetLatestVersion();
|
||||
return maybeLatestVersion.Switch(
|
||||
onSome: latestVersion => string.Compare(version, latestVersion, true) < 0,
|
||||
onSome: latestVersion => string.Compare(this.CurrentVersion.ToString().Trim('v'), latestVersion, true) < 0,
|
||||
onNone: () =>
|
||||
{
|
||||
this.logger.LogWarning("Failed to retrieve latest version");
|
||||
@@ -118,6 +158,20 @@ namespace Daybreak.Services.Updater
|
||||
}).ExtractValue();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Version>> GetVersions()
|
||||
{
|
||||
this.logger.LogInformation($"Retrieving version list from {VersionListUrl}");
|
||||
var response = await this.httpClient.GetAsync(VersionListUrl);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var serializedList = await response.Content.ReadAsStringAsync();
|
||||
var versionList = serializedList.Deserialize<GithubRefTag[]>();
|
||||
return versionList.Select(v => v.Ref.Remove(0, RefTagPrefix.Length)).Select(v => new Version(v));
|
||||
}
|
||||
|
||||
return new List<Version>();
|
||||
}
|
||||
|
||||
public void PeriodicallyCheckForUpdates()
|
||||
{
|
||||
System.Extensions.TaskExtensions.RunPeriodicAsync(async () =>
|
||||
@@ -251,8 +305,15 @@ namespace Daybreak.Services.Updater
|
||||
.Replace(InputFileTag, Path.GetFullPath(TempFile))
|
||||
.Replace(OutputPathTag, Directory.GetCurrentDirectory()),
|
||||
RemoveTempFile,
|
||||
PrepareScheduledAction
|
||||
.Replace(ExecutableNameTag, Process.GetCurrentProcess()?.MainModule?.FileName)
|
||||
.Replace(WorkingDirectoryTag, Directory.GetCurrentDirectory()),
|
||||
PrepareTriggerForAction,
|
||||
RegisterScheduledAction,
|
||||
LaunchScheduledAction,
|
||||
SleepOneSecond,
|
||||
UnregisterScheduledAction,
|
||||
RemovePs1,
|
||||
RunClientCommand
|
||||
});
|
||||
var process = new Process()
|
||||
{
|
||||
@@ -260,12 +321,10 @@ namespace Daybreak.Services.Updater
|
||||
{
|
||||
FileName = "powershell.exe",
|
||||
Arguments = $@"{Directory.GetCurrentDirectory()}\{ExtractAndRunPs1}",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = true,
|
||||
WindowStyle = ProcessWindowStyle.Maximized,
|
||||
WorkingDirectory = Directory.GetCurrentDirectory()
|
||||
WorkingDirectory = Directory.GetCurrentDirectory(),
|
||||
Verb = "runas"
|
||||
},
|
||||
};
|
||||
this.logger.LogInformation("Created extractor script. Attempting to launch powershell");
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Models.Versioning;
|
||||
using Daybreak.Services.ApplicationLifetime;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.Updater
|
||||
{
|
||||
public interface IApplicationUpdater : IApplicationLifetimeService
|
||||
{
|
||||
string CurrentVersion { get; }
|
||||
Version CurrentVersion { get; }
|
||||
void FinalizeUpdate();
|
||||
void PeriodicallyCheckForUpdates();
|
||||
Task<IEnumerable<Version>> GetVersions();
|
||||
Task<bool> UpdateAvailable();
|
||||
Task<bool> DownloadUpdate(UpdateStatus updateStatus);
|
||||
Task<bool> DownloadUpdate(Version version, UpdateStatus updateStatus);
|
||||
Task<bool> DownloadLatestUpdate(UpdateStatus updateStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Logging;
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Utils
|
||||
{
|
||||
public static class LoggingExtensions
|
||||
{
|
||||
public static void LogInformation(this ILogger logger, string message)
|
||||
{
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
logger.Log(LogLevel.Information, message);
|
||||
}
|
||||
|
||||
public static void LogError(this ILogger logger, string message)
|
||||
{
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
logger.Log(LogLevel.Error, message);
|
||||
}
|
||||
|
||||
public static void LogCritical(this ILogger logger, string message)
|
||||
{
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
logger.Log(LogLevel.Critical, message);
|
||||
}
|
||||
|
||||
public static void LogWarning(this ILogger logger, string message)
|
||||
{
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
logger.Log(LogLevel.Warning, message);
|
||||
}
|
||||
|
||||
public static void LogInformation(this ILogger logger, Exception e)
|
||||
{
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
logger.Log(LogLevel.Information, e);
|
||||
}
|
||||
|
||||
public static void LogWarning(this ILogger logger, Exception e)
|
||||
{
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
logger.Log(LogLevel.Warning, e);
|
||||
}
|
||||
|
||||
public static void LogError(this ILogger logger, Exception e)
|
||||
{
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
logger.Log(LogLevel.Error, e);
|
||||
}
|
||||
|
||||
public static void LogCritical(this ILogger logger, Exception e)
|
||||
{
|
||||
if (logger is null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
logger.Log(LogLevel.Critical, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,14 @@ namespace Pepa.Wpf.Utilities
|
||||
{
|
||||
static class NativeMethods
|
||||
{
|
||||
public static uint WM_KEYDOWN = 0x0100;
|
||||
public static uint SWP_SHOWWINDOW = 0x0040;
|
||||
public static IntPtr HWND_TOPMOST = new(-1);
|
||||
public static IntPtr HWND_TOP = IntPtr.Zero;
|
||||
public const int WH_KEYBOARD_LL = 13;
|
||||
|
||||
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct SystemHandleInformation
|
||||
{
|
||||
@@ -89,7 +97,7 @@ namespace Pepa.Wpf.Utilities
|
||||
|
||||
public const int WM_SYSCOMMAND = 0x112;
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
@@ -103,7 +111,27 @@ namespace Pepa.Wpf.Utilities
|
||||
public static extern NtStatus NtQueryObject(IntPtr ObjectHandle, ObjectInformationClass ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength, out int ReturnLength);
|
||||
[DllImport("ntdll.dll")]
|
||||
public static extern NtStatus NtQuerySystemInformation(SystemInformationClass SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength, out int ReturnLength);
|
||||
[DllImport("kernel32.dll")]
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, StringBuilder lpExeName, ref uint lpdwSize);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool SetWindowPos(IntPtr hwnd, IntPtr insertAfter, int x, int y, int cx, int cy, uint flags);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool ShowWindow(IntPtr hwnd, int cmd);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern int GetWindowTextLength(IntPtr hWnd);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr LoadLibrary(string lpFileName);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool UnhookWindowsHookEx(IntPtr hHook);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr CallNextHookEx(IntPtr hHook, int code, IntPtr wParam, IntPtr lParam);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetForegroundWindow();
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,10 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace Daybreak.Utils
|
||||
{
|
||||
public static class SerializationExtensions
|
||||
{
|
||||
public static byte[] SerializeBytes(this object obj)
|
||||
{
|
||||
byte[] returnArray;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
var bf = new BinaryFormatter();
|
||||
bf.Serialize(memoryStream, obj);
|
||||
returnArray = memoryStream.ToArray();
|
||||
}
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
public static T DeserializeBytes<T>(this byte[] serializedObject)
|
||||
{
|
||||
T obj;
|
||||
using (var memoryStream = new MemoryStream(serializedObject))
|
||||
{
|
||||
var bf = new BinaryFormatter();
|
||||
obj = (T)bf.Deserialize(memoryStream);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static string Serialize<T>(this T obj)
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj);
|
||||
|
||||
@@ -9,13 +9,19 @@
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Width="400" Height="200"
|
||||
Background="White">
|
||||
<TextBlock Text="An update has been detected. Do you wish to update?" FontSize="20" TextWrapping="Wrap"
|
||||
Foreground="Black"></TextBlock>
|
||||
<controls:OpaqueButton Text="No" Foreground="Black" TransparentBackground="Gray" BackgroundOpacity="0.2" Width="50" Height="30"
|
||||
FontSize="16" HorizontalAlignment="Center" Margin="0, 0, 80, 0"
|
||||
Clicked="NoButton_Clicked"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="Yes" Foreground="Black" TransparentBackground="Gray" BackgroundOpacity="0.2" Width="50" Height="30"
|
||||
FontSize="16" HorizontalAlignment="Center" Margin="80, 0, 0, 0"
|
||||
Clicked="YesButton_Clicked"></controls:OpaqueButton>
|
||||
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
|
||||
<TextBlock Text="An update has been detected. Do you want to download the update?" HorizontalAlignment="Center"
|
||||
FontSize="16" Foreground="Black" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<controls:OpaqueButton Text="Yes" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="YesButton_Clicked" Foreground="Black" FontSize="16"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="No" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="NoButton_Clicked" Foreground="Black" Grid.Column="1" FontSize="16"></controls:OpaqueButton>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.Privilege;
|
||||
using Daybreak.Services.Runtime;
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Views
|
||||
@@ -15,21 +16,38 @@ namespace Daybreak.Views
|
||||
{
|
||||
private const string UpdateDesiredKey = "UpdateDesired";
|
||||
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<AskUpdateView> logger;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IRuntimeStore runtimeStore;
|
||||
private readonly IPrivilegeManager privilegeManager;
|
||||
private readonly IApplicationUpdater applicationUpdater;
|
||||
|
||||
public AskUpdateView(
|
||||
ILogger logger,
|
||||
ILogger<AskUpdateView> logger,
|
||||
IViewManager viewManager,
|
||||
IRuntimeStore runtimeStore)
|
||||
IRuntimeStore runtimeStore,
|
||||
IPrivilegeManager privilegeManager,
|
||||
IApplicationUpdater applicationUpdater)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.runtimeStore = runtimeStore.ThrowIfNull(nameof(runtimeStore));
|
||||
this.privilegeManager = privilegeManager.ThrowIfNull(nameof(privilegeManager));
|
||||
this.applicationUpdater = applicationUpdater.ThrowIfNull(nameof(applicationUpdater));
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private bool CheckIfAdmin()
|
||||
{
|
||||
if (this.privilegeManager.AdminPrivileges is false)
|
||||
{
|
||||
this.privilegeManager.RequestAdminPrivileges<MainView>("Application needs to be in administrator mode in order to update.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void NoButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.logger.LogInformation("User declined update");
|
||||
@@ -37,11 +55,17 @@ namespace Daybreak.Views
|
||||
this.viewManager.ShowView<MainView>();
|
||||
}
|
||||
|
||||
private void YesButton_Clicked(object sender, System.EventArgs e)
|
||||
private async void YesButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.logger.LogInformation("User accepted update");
|
||||
this.runtimeStore.StoreValue(UpdateDesiredKey, true);
|
||||
this.viewManager.ShowView<UpdateView>();
|
||||
if (this.CheckIfAdmin() is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var latestVersion = (await this.applicationUpdater.GetVersions()).Last();
|
||||
this.viewManager.ShowView<UpdateView>(latestVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,10 +87,11 @@
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Multi-launch support" Foreground="White" FontSize="22" VerticalAlignment="Center" Margin="0, 0, 15, 0" Height="30"></TextBlock>
|
||||
<TextBlock Text="GWToolbox startup delay (in ms)" Foreground="White" FontSize="22" VerticalAlignment="Center" Margin="0, 0, 15, 0" Height="30"></TextBlock>
|
||||
<TextBlock Text="Detect build templates (in browser)" Foreground="White" FontSize="22" VerticalAlignment="Center" Margin="0, 0, 15, 0" Height="30"></TextBlock>
|
||||
<StackPanel Margin="0, 0, 15, 0">
|
||||
<TextBlock Text="Multi-launch support" Foreground="White" FontSize="22" Height="30"></TextBlock>
|
||||
<TextBlock Text="GWToolbox startup delay (in ms)" Foreground="White" FontSize="22" Height="30"></TextBlock>
|
||||
<TextBlock Text="Detect build templates (in browser)" Foreground="White" FontSize="22" Height="30"></TextBlock>
|
||||
<TextBlock Text="Launch gw as current user" Foreground="White" FontSize="22" Height="30"></TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<ToggleButton IsChecked="{Binding ElementName=_this, Path=MultiLaunch, Mode=TwoWay}" Style="{StaticResource AnimatedSwitch}"
|
||||
@@ -100,6 +101,8 @@
|
||||
CommandManager.PreviewCanExecute="TextBox_DisallowPaste"></TextBox>
|
||||
<ToggleButton IsChecked="{Binding ElementName=_this, Path=DynamicBuildLoading, Mode=TwoWay}" Style="{StaticResource AnimatedSwitch}"
|
||||
Height="30" Width="60"></ToggleButton>
|
||||
<ToggleButton IsChecked="{Binding ElementName=_this, Path=LaunchAsCurrentUser, Mode=TwoWay}" Style="{StaticResource AnimatedSwitch}"
|
||||
Height="30" Width="60"></ToggleButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -21,10 +21,19 @@ namespace Daybreak.Views
|
||||
DependencyPropertyExtensions.Register<ExperimentalSettingsView, string>(nameof(GWToolboxLaunchDelay));
|
||||
public static readonly DependencyProperty DynamicBuildLoadingProperty =
|
||||
DependencyPropertyExtensions.Register<ExperimentalSettingsView, bool>(nameof(DynamicBuildLoading));
|
||||
public static readonly DependencyProperty LaunchAsCurrentUserProperty =
|
||||
DependencyPropertyExtensions.Register<ExperimentalSettingsView, bool>(nameof(LaunchAsCurrentUser));
|
||||
public static readonly DependencyProperty MacrosEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<ExperimentalSettingsView, bool>(nameof(MacrosEnabled));
|
||||
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
|
||||
public bool LaunchAsCurrentUser
|
||||
{
|
||||
get => this.GetTypedValue<bool>(LaunchAsCurrentUserProperty);
|
||||
set => this.SetValue(LaunchAsCurrentUserProperty, value);
|
||||
}
|
||||
public bool MultiLaunch
|
||||
{
|
||||
get => this.GetTypedValue<bool>(MultiLaunchProperty);
|
||||
@@ -40,6 +49,11 @@ namespace Daybreak.Views
|
||||
get => this.GetTypedValue<bool>(DynamicBuildLoadingProperty);
|
||||
set => this.SetValue(DynamicBuildLoadingProperty, value);
|
||||
}
|
||||
public bool MacrosEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(MacrosEnabledProperty);
|
||||
set => this.SetValue(MacrosEnabledProperty, value);
|
||||
}
|
||||
|
||||
public ExperimentalSettingsView(
|
||||
IViewManager viewManager,
|
||||
@@ -57,6 +71,8 @@ namespace Daybreak.Views
|
||||
this.MultiLaunch = config.ExperimentalFeatures.MultiLaunchSupport;
|
||||
this.GWToolboxLaunchDelay = config.ExperimentalFeatures.ToolboxAutoLaunchDelay.ToString();
|
||||
this.DynamicBuildLoading = config.ExperimentalFeatures.DynamicBuildLoading;
|
||||
this.LaunchAsCurrentUser = config.ExperimentalFeatures.LaunchGuildwarsAsCurrentUser;
|
||||
this.MacrosEnabled = config.ExperimentalFeatures.CanInterceptKeys;
|
||||
}
|
||||
|
||||
private void SaveExperimentalSettings()
|
||||
@@ -64,6 +80,8 @@ namespace Daybreak.Views
|
||||
var config = this.configurationManager.GetConfiguration();
|
||||
config.ExperimentalFeatures.MultiLaunchSupport = this.MultiLaunch;
|
||||
config.ExperimentalFeatures.DynamicBuildLoading = this.DynamicBuildLoading;
|
||||
config.ExperimentalFeatures.LaunchGuildwarsAsCurrentUser = this.LaunchAsCurrentUser;
|
||||
config.ExperimentalFeatures.CanInterceptKeys = this.MacrosEnabled;
|
||||
if (int.TryParse(this.GWToolboxLaunchDelay, out var gwToolboxLaunchDelay))
|
||||
{
|
||||
config.ExperimentalFeatures.ToolboxAutoLaunchDelay = gwToolboxLaunchDelay;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<UserControl x:Class="Daybreak.Views.LogsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<Style TargetType="{x:Type TextBlock}" x:Key="WrapText">
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<controls:BinButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="5"
|
||||
Clicked="BinButton_Clicked"></controls:BinButton>
|
||||
<controls:RefreshGlyph Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="0, 5, 45, 5"
|
||||
Clicked="RefreshGlyph_Clicked"></controls:RefreshGlyph>
|
||||
<DataGrid IsReadOnly="True" Background="Transparent" Foreground="White" Grid.Row="1"
|
||||
ItemsSource="{Binding ElementName=_this, Path=Logs, Mode=OneWay}" HorizontalScrollBarVisibility="Disabled"
|
||||
AutoGenerateColumns="False" HeadersVisibility="Column" EnableColumnVirtualization="True"
|
||||
EnableRowVirtualization="True">
|
||||
<DataGrid.ColumnHeaderStyle>
|
||||
<Style TargetType="{x:Type DataGridColumnHeader}">
|
||||
<Setter Property="Foreground" Value="White"></Setter>
|
||||
<Setter Property="Background" Value="Transparent"></Setter>
|
||||
<Setter Property="BorderBrush" Value="#80808080"></Setter>
|
||||
<Setter Property="BorderThickness" Value="1"></Setter>
|
||||
</Style>
|
||||
</DataGrid.ColumnHeaderStyle>
|
||||
<DataGrid.CellStyle>
|
||||
<Style TargetType="{x:Type DataGridCell}">
|
||||
<Setter Property="BorderBrush" Value="#80808080"></Setter>
|
||||
<Setter Property="BorderThickness" Value="1"></Setter>
|
||||
</Style>
|
||||
</DataGrid.CellStyle>
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="{x:Type DataGridRow}">
|
||||
<Setter Property="Background" Value="Transparent"></Setter>
|
||||
<Setter Property="Foreground" Value="White"></Setter>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn IsReadOnly="True" Header="DateTime" Binding="{Binding LogTime}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
|
||||
<DataGridTextColumn IsReadOnly="True" Header="Category" Binding="{Binding Category}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
|
||||
<DataGridTextColumn IsReadOnly="True" Header="LogLevel" Binding="{Binding LogLevel}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
|
||||
<DataGridTextColumn IsReadOnly="True" Header="EventId" Binding="{Binding EventId}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
|
||||
<DataGridTextColumn IsReadOnly="True" Header="Message" Binding="{Binding Message}" ElementStyle="{StaticResource WrapText}" Width="*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,49 @@
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Extensions;
|
||||
using System.Windows.Controls;
|
||||
using Daybreak.Models;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LogsView.xaml
|
||||
/// </summary>
|
||||
public partial class LogsView : UserControl
|
||||
{
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly ILogsManager logManager;
|
||||
|
||||
public ObservableCollection<Log> Logs { get; } = new ObservableCollection<Log>();
|
||||
|
||||
public LogsView(
|
||||
IViewManager viewManager,
|
||||
ILogsManager logManager)
|
||||
{
|
||||
this.logManager = logManager.ThrowIfNull(nameof(logManager));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.InitializeComponent();
|
||||
this.UpdateLogs();
|
||||
}
|
||||
|
||||
private void UpdateLogs()
|
||||
{
|
||||
this.Logs.ClearAnd().AddRange(this.logManager.GetLogs());
|
||||
}
|
||||
private void BackButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<SettingsCategoryView>();
|
||||
}
|
||||
private void BinButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.logManager.DeleteLogs();
|
||||
this.UpdateLogs();
|
||||
}
|
||||
private void RefreshGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.UpdateLogs();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Services.ApplicationLauncher;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Screens;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
@@ -16,86 +18,45 @@ namespace Daybreak.Views
|
||||
/// <summary>
|
||||
/// Interaction logic for StartupView.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class MainView : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty ButtonsVisibleProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(ButtonsVisible), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty LaunchButtonEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(LaunchButtonEnabled));
|
||||
public static readonly DependencyProperty LaunchToolboxButtonEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(LaunchToolboxButtonEnabled));
|
||||
public static readonly DependencyProperty LaunchTexmodButtonEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(LaunchTexmodButtonEnabled));
|
||||
public static readonly DependencyProperty BrowsersEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(BrowsersEnabled), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty RightBrowserAddressProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, string>(nameof(RightBrowserAddress));
|
||||
public static readonly DependencyProperty LeftBrowserAddressProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, string>(nameof(LeftBrowserAddress));
|
||||
public static readonly DependencyProperty RightBrowserFavoriteAddressProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, string>(nameof(RightBrowserFavoriteAddress));
|
||||
public static readonly DependencyProperty LeftBrowserFavoriteAddressProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, string>(nameof(LeftBrowserFavoriteAddress));
|
||||
|
||||
private readonly IApplicationLauncher applicationDetector;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly IScreenManager screenManager;
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new();
|
||||
|
||||
private bool leftBrowserMaximized = false;
|
||||
private bool rightBrowserMaximized = false;
|
||||
|
||||
public bool ButtonsVisible
|
||||
{
|
||||
get => this.GetTypedValue<bool>(ButtonsVisibleProperty);
|
||||
set => this.SetTypedValue(ButtonsVisibleProperty, value);
|
||||
}
|
||||
public string RightBrowserFavoriteAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(RightBrowserFavoriteAddressProperty);
|
||||
set => this.SetTypedValue(RightBrowserFavoriteAddressProperty, value);
|
||||
}
|
||||
public string LeftBrowserFavoriteAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(LeftBrowserFavoriteAddressProperty);
|
||||
set => this.SetTypedValue(LeftBrowserFavoriteAddressProperty, value);
|
||||
}
|
||||
public string RightBrowserAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(RightBrowserAddressProperty);
|
||||
set => this.SetTypedValue(RightBrowserAddressProperty, value);
|
||||
}
|
||||
public string LeftBrowserAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(LeftBrowserAddressProperty);
|
||||
set => this.SetTypedValue(LeftBrowserAddressProperty, value);
|
||||
}
|
||||
public bool LaunchButtonEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(LaunchButtonEnabledProperty);
|
||||
set => this.SetTypedValue(LaunchButtonEnabledProperty, value);
|
||||
}
|
||||
public bool LaunchToolboxButtonEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(LaunchToolboxButtonEnabledProperty);
|
||||
set => this.SetTypedValue(LaunchToolboxButtonEnabledProperty, value);
|
||||
}
|
||||
public bool LaunchTexmodButtonEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(LaunchTexmodButtonEnabledProperty);
|
||||
set => this.SetTypedValue(LaunchTexmodButtonEnabledProperty, value);
|
||||
}
|
||||
public bool BrowsersEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(BrowsersEnabledProperty);
|
||||
set => this.SetTypedValue(BrowsersEnabledProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool buttonsVisible;
|
||||
[GenerateDependencyProperty]
|
||||
private string rightBrowserFavoriteAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private string leftBrowserFavoriteAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private string rightBrowserAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private string leftBrowserAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private bool launchButtonEnabled;
|
||||
[GenerateDependencyProperty]
|
||||
private bool launchToolboxButtonEnabled;
|
||||
[GenerateDependencyProperty]
|
||||
private bool launchTexmodButtonEnabled;
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool browsersEnabled;
|
||||
|
||||
public MainView(
|
||||
IApplicationLauncher applicationDetector,
|
||||
IViewManager viewManager,
|
||||
IConfigurationManager configurationManager)
|
||||
IConfigurationManager configurationManager,
|
||||
IScreenManager screenManager)
|
||||
{
|
||||
this.screenManager = screenManager.ThrowIfNull(nameof(screenManager));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.applicationDetector = applicationDetector.ThrowIfNull(nameof(applicationDetector));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
@@ -145,7 +106,23 @@ namespace Daybreak.Views
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.applicationDetector.LaunchGuildwars();
|
||||
if (await this.applicationDetector.LaunchGuildwars() is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.configurationManager.GetConfiguration().SetGuildwarsWindowSizeOnLaunch)
|
||||
{
|
||||
var id = this.configurationManager.GetConfiguration().DesiredGuildwarsScreen;
|
||||
var desiredScreen = this.screenManager.Screens.Skip(id).FirstOrDefault();
|
||||
if (desiredScreen is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to set guildwars on desired screen. No screen with id {id}");
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
this.screenManager.MoveGuildwarsToScreen(desiredScreen);
|
||||
}
|
||||
|
||||
if (this.configurationManager.GetConfiguration().ToolboxAutoLaunch is true)
|
||||
{
|
||||
var delay = this.configurationManager.GetConfiguration().ExperimentalFeatures.ToolboxAutoLaunchDelay;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<UserControl x:Class="Daybreak.Views.RequestElevationView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
mc:Ignorable="d"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
|
||||
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding MessageToUser}" Margin="10, 0, 10, 0" Foreground="Black"
|
||||
TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="16"></TextBlock>
|
||||
<TextBlock Text="Do you want to restart the application with administrator rights?" HorizontalAlignment="Center"
|
||||
FontSize="16" Foreground="Black" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<controls:OpaqueButton Text="Yes" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="YesButton_Clicked" Foreground="Black" FontSize="16"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="No" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="NoButton_Clicked" Foreground="Black" Grid.Column="1" FontSize="16"></controls:OpaqueButton>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,49 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.ApplicationLauncher;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Extensions;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for RequestElevationView.xaml
|
||||
/// </summary>
|
||||
public partial class RequestElevationView : UserControl
|
||||
{
|
||||
private readonly IApplicationLauncher applicationLauncher;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly ILogger<RequestElevationView> logger;
|
||||
|
||||
public RequestElevationView(
|
||||
IApplicationLauncher applicationLauncher,
|
||||
IViewManager viewManager,
|
||||
ILogger<RequestElevationView> logger)
|
||||
{
|
||||
this.applicationLauncher = applicationLauncher.ThrowIfNull(nameof(applicationLauncher));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void YesButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.applicationLauncher.RestartDaybreakAsAdmin();
|
||||
}
|
||||
|
||||
private void NoButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (this.DataContext is ElevationRequest elevationRequest)
|
||||
{
|
||||
this.logger.LogWarning($"Elevation request denied. Showing {elevationRequest.View} with {elevationRequest.DataContext}");
|
||||
this.viewManager.ShowView(elevationRequest.View, elevationRequest.DataContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logger.LogWarning("Elevation request context is not set. Returning to home page");
|
||||
this.viewManager.ShowView<MainView>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<UserControl x:Class="Daybreak.Views.ScreenChoiceView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<TextBlock Text="Choose screen" FontSize="18" Foreground="White" HorizontalAlignment="Center"></TextBlock>
|
||||
<controls:OpaqueButton Text="Test" Foreground="White" HighlightOpacity="0.3" Highlight="White" FontSize="18" Width="80"
|
||||
HorizontalAlignment="Right" Margin="0, 0, 50, 0" Clicked="OpaqueButton_Clicked"
|
||||
IsEnabled="{Binding ElementName=_this, Path=CanTest, Mode=OneWay}"></controls:OpaqueButton>
|
||||
<controls:SaveButton Foreground="White" Height="30" Width="30" Grid.Column="1" HorizontalAlignment="Right" Margin="5"
|
||||
Clicked="SaveButton_Clicked"></controls:SaveButton>
|
||||
<Viewbox Grid.Row="1">
|
||||
<Grid x:Name="ScreenContainer"></Grid>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,104 @@
|
||||
using Daybreak.Controls.Templates;
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.ApplicationLauncher;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Screens;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ScreenChoiceView.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class ScreenChoiceView : UserControl
|
||||
{
|
||||
private readonly IScreenManager screenManager;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly IApplicationLauncher applicationLauncher;
|
||||
private int selectedId;
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private bool canTest;
|
||||
|
||||
public ScreenChoiceView(
|
||||
IViewManager viewManager,
|
||||
IScreenManager screenManager,
|
||||
IConfigurationManager configurationManager,
|
||||
IApplicationLauncher applicationLauncher)
|
||||
{
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.screenManager = screenManager.ThrowIfNull(nameof(screenManager));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.applicationLauncher = applicationLauncher.ThrowIfNull(nameof(applicationLauncher));
|
||||
this.InitializeComponent();
|
||||
this.selectedId = configurationManager.GetConfiguration().DesiredGuildwarsScreen;
|
||||
this.CanTest = applicationLauncher.IsGuildwarsRunning;
|
||||
this.SetupView();
|
||||
}
|
||||
|
||||
private void SetupView()
|
||||
{
|
||||
foreach(var screen in this.screenManager.Screens)
|
||||
{
|
||||
var screenTemplate = new ScreenTemplate
|
||||
{
|
||||
DataContext = screen,
|
||||
Margin = new System.Windows.Thickness(screen.Size.Left, screen.Size.Top, 0, 0),
|
||||
Width = screen.Size.Width,
|
||||
Height = screen.Size.Height,
|
||||
VerticalAlignment = System.Windows.VerticalAlignment.Top,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
|
||||
Foreground = screen.Id == this.selectedId ? Brushes.LightGreen : Brushes.White
|
||||
};
|
||||
screenTemplate.Clicked += ScreenTemplate_Clicked;
|
||||
this.ScreenContainer.Children.Add(screenTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScreenTemplate_Clicked(object sender, Screen e)
|
||||
{
|
||||
this.SelectScreen(e);
|
||||
}
|
||||
|
||||
private void SelectScreen(Screen screen)
|
||||
{
|
||||
this.selectedId = screen.Id;
|
||||
foreach(var template in this.ScreenContainer.Children.OfType<ScreenTemplate>())
|
||||
{
|
||||
template.Foreground = template.DataContext.As<Screen>().Id == this.selectedId ? Brushes.LightGreen : Brushes.White;
|
||||
}
|
||||
}
|
||||
|
||||
private void BackButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<SettingsView>();
|
||||
}
|
||||
|
||||
private void SaveButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.configurationManager.GetConfiguration().DesiredGuildwarsScreen = this.selectedId;
|
||||
this.viewManager.ShowView<SettingsCategoryView>();
|
||||
}
|
||||
|
||||
private void OpaqueButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
var screen = this.screenManager.Screens.Skip(this.selectedId).FirstOrDefault();
|
||||
if (screen is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to test placement. No screen with id {this.selectedId}");
|
||||
}
|
||||
|
||||
this.screenManager.MoveGuildwarsToScreen(screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,18 @@
|
||||
<controls:FireballGlyph></controls:FireballGlyph>
|
||||
</controls:TileButton.InnerContent>
|
||||
</controls:TileButton>
|
||||
<controls:TileButton Title="Version management" Foreground="White" BorderBrush="White" BorderThickness="2"
|
||||
HighlightColor="White" Clicked="VersionButton_Clicked" Height="150" Width="150">
|
||||
<controls:TileButton.InnerContent>
|
||||
<controls:StaticGlyph></controls:StaticGlyph>
|
||||
</controls:TileButton.InnerContent>
|
||||
</controls:TileButton>
|
||||
<controls:TileButton Title="Logs" Foreground="White" BorderBrush="White" BorderThickness="2"
|
||||
HighlightColor="White" Clicked="LogsButton_Clicked" Height="150" Width="150">
|
||||
<controls:TileButton.InnerContent>
|
||||
<controls:LogsGlyph></controls:LogsGlyph>
|
||||
</controls:TileButton.InnerContent>
|
||||
</controls:TileButton>
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -38,6 +38,16 @@ namespace Daybreak.Views
|
||||
this.viewManager.ShowView<BuildsListView>();
|
||||
}
|
||||
|
||||
private void VersionButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<VersionManagementView>(this);
|
||||
}
|
||||
|
||||
private void LogsButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<LogsView>();
|
||||
}
|
||||
|
||||
private void FileButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<ExecutablesView>();
|
||||
|
||||
@@ -66,60 +66,89 @@
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Launcher settings" FontSize="22" Foreground="White" HorizontalAlignment="Center" Grid.ColumnSpan="2"></TextBlock>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<controls:SaveButton Foreground="White" Height="30" Width="30" Grid.Column="1" HorizontalAlignment="Right" Margin="5"
|
||||
Clicked="SaveButton_Clicked"></controls:SaveButton>
|
||||
<StackPanel Orientation="Vertical" Grid.Row="1">
|
||||
<TextBlock Text="Texmod path" FontSize="22" Foreground="White"></TextBlock>
|
||||
<TextBlock Text="GWToolbox path: " FontSize="22" Foreground="White"></TextBlock>
|
||||
<TextBlock Text="GWToolbox auto-launch: " FontSize="22" Foreground="White"></TextBlock>
|
||||
<TextBlock Text="Browsers enabled: " FontSize="22" Foreground="White"/>
|
||||
<TextBlock Text="Browsers address bar readonly: " FontSize="22" Foreground="White"/>
|
||||
<TextBlock Text="Left browser homepage: " FontSize="22" Foreground="White"></TextBlock>
|
||||
<TextBlock Text="Right browser homepage: " FontSize="22" Foreground="White"></TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="1">
|
||||
<Grid>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Launcher settings" FontSize="22" Foreground="White" HorizontalAlignment="Center" Grid.ColumnSpan="2"></TextBlock>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<controls:SaveButton Foreground="White" Height="30" Width="30" Grid.Column="1" HorizontalAlignment="Right" Margin="5"
|
||||
Clicked="SaveButton_Clicked"></controls:SaveButton>
|
||||
<StackPanel Orientation="Vertical" Grid.Row="1">
|
||||
<TextBlock Text="Auto check for updates: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Texmod path: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="GWToolbox auto-launch: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="GWToolbox path: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Browsers enabled: " FontSize="22" Foreground="White" Height="30"/>
|
||||
<TextBlock Text="Browsers address bar readonly: " FontSize="22" Foreground="White" Height="30"/>
|
||||
<TextBlock Text="Left browser homepage: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Right browser homepage: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Auto-place on desired screen: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Desired screen id: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Place Shortcut: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Shortcut folder: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="1">
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=AutoCheckUpdate, Mode=TwoWay}"></ToggleButton>
|
||||
<Grid Height="30">
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=TexmodPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="TexmodFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=ToolboxAutoLaunch, Mode=TwoWay}"></ToggleButton>
|
||||
<Grid Height="30">
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=ToolboxPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="ToolboxFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=TwoWay}"></ToggleButton>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=AddressBarReadonly, Mode=TwoWay}"></ToggleButton>
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=TexmodPath, Mode=TwoWay}"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="TexmodFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<Grid>
|
||||
FontSize="22" Background="Transparent" Foreground="White" Height="30"
|
||||
Text="{Binding ElementName=_this, Path=LeftBrowserUrl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=ToolboxPath, Mode=TwoWay}"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="ToolboxFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=ToolboxAutoLaunch, Mode=TwoWay}"></ToggleButton>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=TwoWay}"></ToggleButton>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=AddressBarReadonly, Mode=TwoWay}"></ToggleButton>
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=LeftBrowserUrl, Mode=TwoWay}"
|
||||
TextChanged="LeftBrowserUrl_TextChanged"></TextBox>
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=RightBrowserUrl, Mode=TwoWay}"
|
||||
TextChanged="RightBrowserUrl_TextChanged"></TextBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
FontSize="22" Background="Transparent" Foreground="White" Height="30"
|
||||
Text="{Binding ElementName=_this, Path=RightBrowserUrl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=AutoPlaceOnScreen, Mode=TwoWay}"></ToggleButton>
|
||||
<Grid Height="30">
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=DesiredScreen, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
PreviewTextInput="TextBox_AllowOnlyNumbers" Margin="0, 0, 30, 0"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="ScreenPickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=ShortcutPlaced, Mode=TwoWay}"></ToggleButton>
|
||||
<Grid Height="30">
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=ShortcutFolder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="0, 0, 30, 0"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="ShortcutFolderPickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Shortcuts;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
@@ -6,67 +7,44 @@ using System.Extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for SettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class SettingsView : UserControl
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class SettingsView : System.Windows.Controls.UserControl
|
||||
{
|
||||
public static readonly DependencyProperty TexmodPathProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(TexmodPath));
|
||||
public static readonly DependencyProperty ToolboxPathProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(ToolboxPath));
|
||||
public static readonly DependencyProperty AddressBarReadonlyProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, bool>(nameof(AddressBarReadonly));
|
||||
public static readonly DependencyProperty BrowsersEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, bool>(nameof(BrowsersEnabled));
|
||||
public static readonly DependencyProperty LeftBrowserUrlProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(LeftBrowserUrl));
|
||||
public static readonly DependencyProperty RightBrowserUrlProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(RightBrowserUrl));
|
||||
public static readonly DependencyProperty ToolboxAutoLaunchProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, bool>(nameof(ToolboxAutoLaunch));
|
||||
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly IViewManager viewManager;
|
||||
|
||||
public string TexmodPath
|
||||
{
|
||||
get => this.GetTypedValue<string>(TexmodPathProperty);
|
||||
set => this.SetValue(TexmodPathProperty, value);
|
||||
}
|
||||
public bool ToolboxAutoLaunch
|
||||
{
|
||||
get => this.GetTypedValue<bool>(ToolboxAutoLaunchProperty);
|
||||
set => this.SetValue(ToolboxAutoLaunchProperty, value);
|
||||
}
|
||||
public string ToolboxPath
|
||||
{
|
||||
get => this.GetTypedValue<string>(ToolboxPathProperty);
|
||||
set => this.SetValue(ToolboxPathProperty, value);
|
||||
}
|
||||
public bool AddressBarReadonly
|
||||
{
|
||||
get => this.GetTypedValue<bool>(AddressBarReadonlyProperty);
|
||||
set => this.SetValue(AddressBarReadonlyProperty, value);
|
||||
}
|
||||
public string LeftBrowserUrl
|
||||
{
|
||||
get => this.GetTypedValue<string>(LeftBrowserUrlProperty);
|
||||
set => this.SetValue(LeftBrowserUrlProperty, value);
|
||||
}
|
||||
public string RightBrowserUrl
|
||||
{
|
||||
get => this.GetTypedValue<string>(RightBrowserUrlProperty);
|
||||
set => this.SetValue(RightBrowserUrlProperty, value);
|
||||
}
|
||||
public bool BrowsersEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(BrowsersEnabledProperty);
|
||||
set => this.SetValue(BrowsersEnabledProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private string texmodPath;
|
||||
[GenerateDependencyProperty]
|
||||
private bool toolboxAutoLaunch;
|
||||
[GenerateDependencyProperty]
|
||||
private string toolboxPath;
|
||||
[GenerateDependencyProperty]
|
||||
private bool addressBarReadonly;
|
||||
[GenerateDependencyProperty]
|
||||
private string leftBrowserUrl;
|
||||
[GenerateDependencyProperty]
|
||||
private string rightBrowserUrl;
|
||||
[GenerateDependencyProperty]
|
||||
private bool browsersEnabled;
|
||||
[GenerateDependencyProperty]
|
||||
private bool autoPlaceOnScreen;
|
||||
[GenerateDependencyProperty]
|
||||
private string desiredScreen;
|
||||
[GenerateDependencyProperty]
|
||||
private string shortcutFolder;
|
||||
[GenerateDependencyProperty]
|
||||
private bool shortcutPlaced;
|
||||
[GenerateDependencyProperty]
|
||||
private bool autoCheckUpdate;
|
||||
|
||||
public SettingsView(
|
||||
IConfigurationManager configurationManager,
|
||||
@@ -88,6 +66,11 @@ namespace Daybreak.Views
|
||||
this.ToolboxAutoLaunch = config.ToolboxAutoLaunch;
|
||||
this.TexmodPath = config.TexmodPath;
|
||||
this.BrowsersEnabled = config.BrowsersEnabled;
|
||||
this.AutoPlaceOnScreen = config.SetGuildwarsWindowSizeOnLaunch;
|
||||
this.DesiredScreen = config.DesiredGuildwarsScreen.ToString();
|
||||
this.ShortcutFolder = config.ShortcutLocation;
|
||||
this.ShortcutPlaced = config.PlaceShortcut;
|
||||
this.AutoCheckUpdate = config.AutoCheckUpdate;
|
||||
}
|
||||
|
||||
private void SaveButton_Clicked(object sender, EventArgs e)
|
||||
@@ -100,13 +83,18 @@ namespace Daybreak.Views
|
||||
currentConfig.ToolboxAutoLaunch = this.ToolboxAutoLaunch;
|
||||
currentConfig.TexmodPath = this.TexmodPath;
|
||||
currentConfig.BrowsersEnabled = this.BrowsersEnabled;
|
||||
currentConfig.SetGuildwarsWindowSizeOnLaunch = this.AutoPlaceOnScreen;
|
||||
currentConfig.DesiredGuildwarsScreen = int.Parse(this.DesiredScreen);
|
||||
currentConfig.ShortcutLocation = this.ShortcutFolder;
|
||||
currentConfig.PlaceShortcut = this.ShortcutPlaced;
|
||||
currentConfig.AutoCheckUpdate = this.AutoCheckUpdate;
|
||||
this.configurationManager.SaveConfiguration(currentConfig);
|
||||
this.viewManager.ShowView<SettingsCategoryView>();
|
||||
}
|
||||
|
||||
private void ToolboxFilePickerGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
var filePicker = new OpenFileDialog()
|
||||
var filePicker = new Microsoft.Win32.OpenFileDialog()
|
||||
{
|
||||
CheckFileExists = true,
|
||||
CheckPathExists = true,
|
||||
@@ -121,7 +109,7 @@ namespace Daybreak.Views
|
||||
|
||||
private void TexmodFilePickerGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
var filePicker = new OpenFileDialog()
|
||||
var filePicker = new Microsoft.Win32.OpenFileDialog()
|
||||
{
|
||||
CheckFileExists = true,
|
||||
CheckPathExists = true,
|
||||
@@ -134,19 +122,37 @@ namespace Daybreak.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void ShortcutFolderPickerGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
var folderPicker = new FolderBrowserDialog()
|
||||
{
|
||||
Description = "Select shortcut folder",
|
||||
UseDescriptionForTitle = true,
|
||||
SelectedPath = this.ShortcutFolder,
|
||||
ShowNewFolderButton = true
|
||||
};
|
||||
if (folderPicker.ShowDialog() is DialogResult.OK)
|
||||
{
|
||||
this.ShortcutFolder = folderPicker.SelectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void ScreenPickerGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<ScreenChoiceView>();
|
||||
}
|
||||
|
||||
private void BackButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<SettingsCategoryView>();
|
||||
}
|
||||
|
||||
private void LeftBrowserUrl_TextChanged(object sender, TextChangedEventArgs e)
|
||||
private void TextBox_AllowOnlyNumbers(object sender, System.Windows.Input.TextCompositionEventArgs e)
|
||||
{
|
||||
this.LeftBrowserUrl = sender.As<TextBox>().Text;
|
||||
}
|
||||
|
||||
private void RightBrowserUrl_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
this.RightBrowserUrl = sender.As<TextBox>().Text;
|
||||
if (int.TryParse(e.Text, out _) is false)
|
||||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
Loaded="UpdateView_Loaded"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
|
||||
</UserControl.Resources>
|
||||
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 10" Foreground="Black"
|
||||
TextWrapping="Wrap"></TextBlock>
|
||||
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 0" Foreground="Black"
|
||||
TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="16"></TextBlock>
|
||||
<ProgressBar Minimum="0" Maximum="100" Value="{Binding ElementName=_this, Path=ProgressValue, Mode=OneWay}" Width="300" Height="20"></ProgressBar>
|
||||
<controls:OpaqueButton Text="Ok" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="30" Height="20"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ContinueButtonEnabled, Mode=OneWay}"
|
||||
Clicked="OpaqueButton_Clicked" Foreground="Black"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="Ok" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="OpaqueButton_Clicked" Foreground="Black" FontSize="16"
|
||||
Visibility="{Binding ElementName=_this, Path=ContinueButtonEnabled, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"></controls:OpaqueButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,52 +1,38 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using Version = Daybreak.Models.Versioning.Version;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for UpdateView.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class UpdateView : UserControl
|
||||
{
|
||||
public readonly static DependencyProperty DescriptionProperty =
|
||||
DependencyPropertyExtensions.Register<UpdateView, string>(nameof(Description));
|
||||
public readonly static DependencyProperty ProgressValueProperty =
|
||||
DependencyPropertyExtensions.Register<UpdateView, double>(nameof(ProgressValue));
|
||||
public readonly static DependencyProperty ContinueButtonEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<UpdateView, bool>(nameof(ContinueButtonEnabled));
|
||||
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<UpdateView> logger;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IApplicationUpdater applicationUpdater;
|
||||
private readonly UpdateStatus updateStatus = new();
|
||||
|
||||
private bool success = false;
|
||||
|
||||
public string Description
|
||||
{
|
||||
get => this.GetTypedValue<string>(DescriptionProperty);
|
||||
set => this.SetValue(DescriptionProperty, value);
|
||||
}
|
||||
public double ProgressValue
|
||||
{
|
||||
get => this.GetTypedValue<double>(ProgressValueProperty);
|
||||
set => this.SetValue(ProgressValueProperty, value);
|
||||
}
|
||||
public bool ContinueButtonEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(ContinueButtonEnabledProperty);
|
||||
set => this.SetValue(ContinueButtonEnabledProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private string description;
|
||||
[GenerateDependencyProperty]
|
||||
private double progressValue;
|
||||
[GenerateDependencyProperty]
|
||||
private bool continueButtonEnabled;
|
||||
|
||||
public UpdateView(
|
||||
IApplicationUpdater applicationUpdater,
|
||||
ILogger logger,
|
||||
ILogger<UpdateView> logger,
|
||||
IViewManager viewManager)
|
||||
{
|
||||
this.applicationUpdater = applicationUpdater;
|
||||
@@ -71,8 +57,14 @@ namespace Daybreak.Views
|
||||
|
||||
private async void UpdateView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (this.DataContext is not Version version)
|
||||
{
|
||||
this.viewManager.ShowView<MainView>();
|
||||
throw new InvalidOperationException("No version specified for download");
|
||||
}
|
||||
|
||||
this.logger.LogInformation("Starting update procedure");
|
||||
var success = await applicationUpdater.DownloadUpdate(updateStatus).ConfigureAwait(true);
|
||||
var success = await applicationUpdater.DownloadUpdate(version, updateStatus).ConfigureAwait(true);
|
||||
if (success is false)
|
||||
{
|
||||
this.logger.LogError("Update procedure failed");
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<UserControl x:Class="Daybreak.Views.VersionManagementView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<TextBlock Text="Manage application version" FontSize="18" Foreground="White" HorizontalAlignment="Center"></TextBlock>
|
||||
<WrapPanel Grid.Row="1" HorizontalAlignment="Center" Margin="10">
|
||||
<TextBlock Text="Current version: " Foreground="White" FontSize="16"></TextBlock>
|
||||
<controls:OpaqueButton Text="{Binding ElementName=_this, Path=CurrentVersion.VersionString, Mode=OneWay}" Foreground="White" FontSize="16"
|
||||
Cursor="Hand" BorderBrush="Transparent" BorderThickness="0" Clicked="CurrentVersion_Clicked"></controls:OpaqueButton>
|
||||
</WrapPanel>
|
||||
<ListView Grid.Row="2"
|
||||
Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Versions, Mode=OneWay}"
|
||||
HorizontalContentAlignment="Stretch" Width="230" ScrollViewer.CanContentScroll="True"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="5">
|
||||
<controls:OpaqueButton Text="{Binding VersionString}" Clicked="DesiredVersion_Clicked"
|
||||
Foreground="White" FontSize="16" BorderThickness="0"
|
||||
HorizontalAlignment="Left"></controls:OpaqueButton>
|
||||
<controls:BackButton Foreground="White" Width="30" Height="30" HorizontalAlignment="Right"
|
||||
Clicked="DesiredVersion_DownloadButton_Clicked">
|
||||
<controls:BackButton.RenderTransform>
|
||||
<RotateTransform Angle="270" CenterX="15" CenterY="15"></RotateTransform>
|
||||
</controls:BackButton.RenderTransform>
|
||||
</controls:BackButton>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,92 @@
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using Version = Daybreak.Models.Versioning.Version;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for VersionManagementView.xaml
|
||||
/// </summary>
|
||||
public partial class VersionManagementView : UserControl
|
||||
{
|
||||
private const string VersionPlaceholder = "{VERSION}";
|
||||
private const string ReleaseURL = $"https://github.com/AlexMacocian/Daybreak/releases/tag/{VersionPlaceholder}";
|
||||
|
||||
private readonly IApplicationUpdater applicationUpdater;
|
||||
private readonly IViewManager viewManager;
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private Version currentVersion;
|
||||
|
||||
public ObservableCollection<Version> Versions { get; } = new();
|
||||
|
||||
public VersionManagementView(
|
||||
IApplicationUpdater applicationUpdater,
|
||||
IViewManager viewManager)
|
||||
{
|
||||
this.applicationUpdater = applicationUpdater.ThrowIfNull(nameof(applicationUpdater));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.InitializeComponent();
|
||||
this.CurrentVersion = this.applicationUpdater.CurrentVersion;
|
||||
this.LoadVersionList();
|
||||
}
|
||||
|
||||
private async void LoadVersionList()
|
||||
{
|
||||
this.Versions.ClearAnd().AddRange(await this.applicationUpdater.GetVersions());
|
||||
}
|
||||
|
||||
private void CurrentVersion_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("explorer.exe", ReleaseURL.Replace(VersionPlaceholder, this.currentVersion.ToString()));
|
||||
}
|
||||
|
||||
private void DesiredVersion_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is not UserControl userControl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (userControl.DataContext is not Version desiredVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Process.Start("explorer.exe", ReleaseURL.Replace(VersionPlaceholder, desiredVersion.ToString()));
|
||||
}
|
||||
|
||||
private void DesiredVersion_DownloadButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is not UserControl userControl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (userControl.DataContext is not Version desiredVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewManager.ShowView<UpdateView>(desiredVersion);
|
||||
}
|
||||
|
||||
private void BackButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (this.DataContext is UserControl userControl)
|
||||
{
|
||||
this.viewManager.ShowView(userControl.GetType());
|
||||
}
|
||||
else
|
||||
{
|
||||
this.viewManager.ShowView<MainView>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user