Compare commits

...
30 Commits
Author SHA1 Message Date
amacocianandGitHub f47cc319b5 Build template code field 2021-06-03 19:57:29 +02:00
amacocianandGitHub 26da84c4e7 Update to new version of wpfextended
Fix a bug where version parsing would ignore all 0s before the first digit
Remove LoggerFactory and LoggerProviders duplicated in WpfExtended
2021-06-03 14:48:35 +02:00
a39e8dc5c6 Separate CV from message (#12)
Co-authored-by: Alexandru Macocian <almacoci@microsoft.com>
2021-06-01 21:57:27 +02:00
1bb4669135 Introduce correlated logs (#11)
Co-authored-by: Alexandru Macocian <almacoci@microsoft.com>
2021-06-01 20:43:55 +02:00
amacocianandGitHub 0203eef006 Rework logging (#10)
* Rework log storing into a litedb.
LogView implementation to view logs.
Create crash.log on crash.

* Rename Logs namespace to Logging.
2021-05-18 19:49:51 +02:00
amacocianandGitHub 4ab50ee191 Use Microsoft.Logging.Abstractions
Typed ILogger.
2021-05-14 14:31:33 +02:00
amacocianandGitHub 06c1ec1ac2 Add scoped http client. (#8)
* Introduce scoped http client.
* Uts for scoped httpclient
2021-05-10 12:46:01 +02:00
amacocianandGitHub ef4befdc38 Version management view.
Enable/disable version autocheck.
Version management view.
2021-05-06 18:23:07 +02:00
amacocianandGitHub 33f49ddffa Cleanup cd pipeline (#6)
* Cleanup cd pipeline

* Remove cd pipeline on PR

* Cleanup CI pipeline comments.
2021-05-06 12:33:58 +02:00
amacocianandGitHub 49aa249b5b Generate changelog from last release tag.
Generate changelog from last release tag.
2021-05-05 23:01:57 +02:00
amacocianandGitHub c387d44890 Generate changelog from latest release.
Auto-generate changelog.
2021-05-05 21:31:45 +02:00
d4d09e7267 Update version (#3)
Co-authored-by: Alexandru Macocian <almacoci@microsoft.com>
2021-05-05 20:52:43 +02:00
amacocianandGitHub dd7bf02a97 Add CI & CD pipelines
Add CD&CI pipelines
2021-05-05 20:46:36 +02:00
Alexandru Macocian 6d0eeb478f Use source generator to generate dependency properties. 2021-05-05 17:23:37 +02:00
d4e9de48b1 Add ci pipeline (#1)
* Add ci pipeline

* Remove unused env variables.

* Change dotnet version to 5.0.5

* Change to dotnet 5.0.202

Co-authored-by: Alexandru Macocian <almacoci@microsoft.com>
2021-05-03 17:11:59 +02:00
Alexandru Macocian 6bd9877925 Remove dependency on InputSimulator. 2021-05-03 16:23:30 +02:00
Alexandru Macocian d38df6596f Remove keyboard macro functionality for now.
Unify input prompts.
Modify screenshot manager functionality to apply functionality of settings changed.
Update to 0.8.6.
2021-05-03 16:16:00 +02:00
Alexandru Macocian b4e31e859c Merge branch 'master' of https://github.com/AlexMacocian/Daybreak 2021-05-02 18:49:31 +02:00
Alexandru Macocian 1acd07c1be Change the way shortcut manager manages shortcuts.
Base for keyboard macros.
2021-05-02 18:49:25 +02:00
amacocianandGitHub b9b25bb098 Update README.md 2021-04-27 17:28:25 +02:00
amacocianandGitHub 5ea2981dc7 Update README.md 2021-04-27 17:25:03 +02:00
Alexandru Macocian 4fb96ca3a4 Ability to place screenshots in desired location. 2021-04-27 17:06:28 +02:00
Alexandru Macocian 6de52f5585 Ability to test placement while guildwars is running. 2021-04-26 11:26:43 +02:00
Alexandru Macocian 6622e41f64 Wait for guildwars window to be initialized before moving to desired window. 2021-04-26 11:02:23 +02:00
Alexandru Macocian 8405e4d1e5 Return to settings category after saving desired window. 2021-04-25 18:26:29 +02:00
Alexandru Macocian 0edc3f1560 Throw exception when screen id is invalid. 2021-04-25 18:24:51 +02:00
Alexandru Macocian c109fe7561 Ability to select on which window to place guildwars. 2021-04-25 18:20:45 +02:00
Alexandru Macocian b5413dca5c Include [Admin] tag when running as administrator. 2021-04-24 01:35:46 +02:00
Alexandru Macocian 6c2d4a1e70 No longer require administrator rights for normal usage.
Request elevation only in necesarry parts of the code.
Request elevation on update.
After update, launch application without elevation.
2021-04-24 01:20:36 +02:00
Alexandru Macocian be03f1a32d Run updates as admin 2021-04-23 23:37:01 +02:00
103 changed files with 2860 additions and 1017 deletions
+96
View File
@@ -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 }}
+53
View File
@@ -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 }}
+1
View File
@@ -9,6 +9,7 @@
*.user
*.userosscache
*.sln.docstates
*.version
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
+1
View File
@@ -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>
+136
View File
@@ -0,0 +1,136 @@
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.0.1", "v0.1.0.0.1")]
[DataRow("v0.1.0.0.1.0.0.0.0", "v0.1.0.0.1")]
[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,61 @@
using Daybreak.Services.Logging;
using FluentAssertions;
using LiteDB;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using WpfExtended.Logging;
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 CVLoggerProvider(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);
}
}
}
+5 -5
View File
@@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.31005.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daybreak", "Daybreak\Daybreak.csproj", "{AA45C2B1-8BD0-466C-9271-699F168905AF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Daybreak.Tests", "Daybreak.Tests\Daybreak.Tests.csproj", "{C911C1C2-6566-419C-89C7-F802EB3FE709}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daybreak.Tests", "Daybreak.Tests\Daybreak.Tests.csproj", "{C911C1C2-6566-419C-89C7-F802EB3FE709}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -17,12 +17,12 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.ActiveCfg = Debug|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.Build.0 = Debug|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.ActiveCfg = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.Build.0 = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|Any CPU.Build.0 = Release|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.ActiveCfg = Release|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.Build.0 = Release|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.ActiveCfg = Release|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.Build.0 = Release|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -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
{
@@ -12,5 +14,9 @@ namespace Daybreak.Configuration
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();
}
}
+42 -11
View File
@@ -7,44 +7,71 @@ 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 LiteDB;
using System.Windows.Extensions;
namespace Daybreak.Configuration
{
public static class ProjectConfiguration
{
public static void RegisterResolvers(IServiceManager serviceManager)
{
serviceManager.ThrowIfNull(nameof(serviceManager));
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>();
serviceProducer.RegisterSingleton<IScreenshotProvider, ScreenshotProvider>();
serviceProducer.RegisterSingleton<IConfigurationManager, ConfigurationManager>();
serviceProducer.RegisterSingleton<IBloogumClient, BloogumClient>();
serviceProducer.RegisterSingleton<IApplicationUpdater, ApplicationUpdater>();
serviceProducer.RegisterSingleton<IMutexHandler, MutexHandler>();
serviceProducer.RegisterSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
serviceProducer.RegisterSingleton<IRuntimeStore, RuntimeStore>();
serviceProducer.RegisterSingleton<IBuildTemplateManager, BuildTemplateManager>();
serviceProducer.RegisterSingleton<IIconRetriever, IconRetriever>();
serviceProducer.RegisterSingleton<IMutexHandler, MutexHandler>();
serviceProducer.RegisterSingleton<IShortcutManager, ShortcutManager>();
serviceProducer.RegisterScoped<ICredentialManager, CredentialManager>();
serviceProducer.RegisterScoped<IApplicationLauncher, ApplicationLauncher>();
serviceProducer.RegisterScoped<IScreenshotProvider, ScreenshotProvider>();
serviceProducer.RegisterScoped<IBloogumClient, BloogumClient>();
serviceProducer.RegisterScoped<IApplicationUpdater, ApplicationUpdater>();
serviceProducer.RegisterScoped<IBuildTemplateManager, BuildTemplateManager>();
serviceProducer.RegisterScoped<IIconRetriever, IconRetriever>();
serviceProducer.RegisterScoped<IPrivilegeManager, PrivilegeManager>();
serviceProducer.RegisterScoped<IScreenManager, ScreenManager>();
serviceProducer.RegisterLogWriter<ILogsManager, JsonLogsManager>();
}
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 +87,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>();
}
}
}
@@ -1,10 +1,9 @@
using Daybreak.Launch;
using Daybreak.Models.Browser;
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,83 +18,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 IBuildTemplateManager buildTemplateManager;
private IConfigurationManager configurationManager;
private ILogger<ChromiumBrowserWrapper> logger;
private 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]
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.buildTemplateManager = Launcher.ApplicationServiceManager.GetService<IBuildTemplateManager>();
this.InitializeComponent();
this.InitializeEnvironment();
this.InitializeBrowser();
this.WebBrowser.IsEnabled = false;
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
@@ -107,6 +67,18 @@ namespace Daybreak.Controls
}
}
public async void InitializeBrowser(
IConfigurationManager configurationManager,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
{
this.configurationManager = configurationManager;
this.buildTemplateManager = buildTemplateManager;
this.logger = logger;
this.InitializeEnvironment();
await this.InitializeBrowser();
}
public async void ReinitializeBrowser()
{
await this.InitializeBrowser();
@@ -136,6 +108,7 @@ namespace Daybreak.Controls
{
if (this.BrowserSupported is true)
{
this.WebBrowser.IsEnabled = true;
await this.WebBrowser.EnsureCoreWebView2Async(this.coreWebView2Environment);
this.AddressBarReadonly = this.configurationManager.GetConfiguration().AddressBarReadonly;
this.CanDownloadBuild = this.configurationManager.GetConfiguration().ExperimentalFeatures.DynamicBuildLoading;
@@ -209,7 +182,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)
{
+14
View File
@@ -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();
}
}
}
+14
View File
@@ -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();
}
}
}
+3 -4
View File
@@ -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()
{
@@ -10,26 +10,16 @@ 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 readonly static DependencyProperty CanAddProperty =
DependencyPropertyExtensions.Register<AttributeTemplate, bool>(nameof(CanAdd), new PropertyMetadata(false));
public readonly static DependencyProperty CanSubtractProperty =
DependencyPropertyExtensions.Register<AttributeTemplate, bool>(nameof(CanSubtract), new PropertyMetadata(false));
public event EventHandler<AttributeEntry> HelpClicked;
public bool CanAdd
{
get => this.GetTypedValue<bool>(CanAddProperty);
private set => this.SetValue(CanAddProperty, value);
}
public bool CanSubtract
{
get => this.GetTypedValue<bool>(CanSubtractProperty);
private set => this.SetValue(CanSubtractProperty, value);
}
[GenerateDependencyProperty(InitialValue = false)]
private bool canAdd;
[GenerateDependencyProperty(InitialValue = false)]
private bool canSubtract;
public AttributeTemplate()
{
@@ -95,42 +95,42 @@
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<local:SkillTemplate Grid.Column="0"
<local:SkillTemplate Grid.Column="0" x:Name="SkillTemplate0"
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"
<local:SkillTemplate Grid.Column="1" x:Name="SkillTemplate1"
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"
<local:SkillTemplate Grid.Column="2" x:Name="SkillTemplate2"
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"
<local:SkillTemplate Grid.Column="3" x:Name="SkillTemplate3"
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"
<local:SkillTemplate Grid.Column="4" x:Name="SkillTemplate4"
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"
<local:SkillTemplate Grid.Column="5" x:Name="SkillTemplate5"
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"
<local:SkillTemplate Grid.Column="6" x:Name="SkillTemplate6"
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"
<local:SkillTemplate Grid.Column="7" x:Name="SkillTemplate7"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill7, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
@@ -1,103 +1,52 @@
using Daybreak.Launch;
using Daybreak.Models.Builds;
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.IconRetrieve;
using System.Collections;
using Microsoft.Extensions.Logging;
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 InfoNamePlaceholder = "[NAME]";
private const string BaseAddress = $"https://wiki.guildwars.com/wiki/{InfoNamePlaceholder}";
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 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);
@@ -105,12 +54,35 @@ namespace Daybreak.Controls
public BuildTemplate()
{
this.InitializeComponent();
this.InitializeProperties();
this.DataContextChanged += BuildTemplate_DataContextChanged;
}
public void InitializeTemplate(
IIconRetriever iconRetriever,
IConfigurationManager configurationManager,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
{
this.SkillBrowser.InitializeBrowser(configurationManager, buildTemplateManager, logger);
this.SkillTemplate0.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate1.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate2.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate3.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate4.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate5.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate6.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate7.InitializeSkillTemplate(iconRetriever);
}
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)
@@ -126,6 +98,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)
@@ -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>());
}
}
}
@@ -16,47 +16,42 @@ 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;
private 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()
{
this.iconRetriever = Launcher.ApplicationServiceManager.GetService<IIconRetriever>();
this.InitializeComponent();
this.DataContextChanged += SkillTemplate_DataContextChanged;
}
public void InitializeSkillTemplate(IIconRetriever iconRetriever)
{
this.iconRetriever = iconRetriever;
}
private void SkillTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is Skill skill)
{
if (skill != Skill.NoSkill)
{
Task.Run(() => GetImageStream(skill)).ContinueWith((previousTask) =>
Task.Run(() => this.GetImageStream(skill)).ContinueWith((previousTask) =>
{
this.Dispatcher.Invoke(() =>
{
this.ImageSource = GetImageSource(previousTask.Result);
this.ImageSource = this.GetImageSource(previousTask.Result);
});
});
}
@@ -97,6 +92,11 @@ namespace Daybreak.Controls
}
private async Task<Stream> GetImageStream(Skill skill)
{
if (this.iconRetriever is null)
{
return null;
}
var maybeStream = await this.iconRetriever.GetIcon(skill);
return maybeStream.ExtractValue();
}
+18 -6
View File
@@ -9,19 +9,27 @@
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<Version>0.7.8</Version>
<ApplicationManifest>app.manifest</ApplicationManifest>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.1</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.32" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.818.41" />
<PackageReference Include="LiteDB" Version="5.0.10" />
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
<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.864.35" />
<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.4.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.3.0" />
<PackageReference Include="WCL" Version="1.0.2" />
<PackageReference Include="WpfExtended" Version="0.2.0" />
<PackageReference Include="WpfExtended" Version="0.4.1" />
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.1" />
<PackageReference Include="WpfScreenHelper" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
@@ -62,4 +70,8 @@
</Page>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'">
<Exec Command="echo.&gt;$(Version).version" />
</Target>
</Project>
+18 -7
View File
@@ -1,17 +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.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Extensions;
@@ -28,9 +25,15 @@ namespace Daybreak.Launch
return LaunchMainWindow();
}
protected override void SetupServiceManager(IServiceManager serviceManager)
{
ApplicationServiceManager = this.ServiceManager;
ProjectConfiguration.RegisterResolvers(serviceManager);
}
protected override void RegisterServices(IServiceProducer serviceProducer)
{
ProjectConfiguration.RegisterServices(this.ServiceManager);
ServiceManager.BuildSingletons();
ProjectConfiguration.RegisterLifetimeServices(this.ServiceManager.GetService<IApplicationLifetimeManager>());
ProjectConfiguration.RegisterViews(this.ServiceManager.GetService<IViewManager>());
}
@@ -41,10 +44,19 @@ 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)
@@ -65,7 +77,6 @@ namespace Daybreak.Launch
}
protected override void ApplicationStarting()
{
ApplicationServiceManager = this.ServiceManager;
this.ServiceManager.GetService<IApplicationLifetimeManager>().OnStartup();
this.RegisterViewContainer();
}
+52 -38
View File
@@ -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>
+34 -16
View File
@@ -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>();
+11
View File
@@ -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; }
}
}
+14
View File
@@ -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;
}
}
+11
View File
@@ -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; }
}
}
+17
View File
@@ -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;
}
}
}
+41
View File
@@ -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;
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Daybreak.Models
{
public enum KeyboardState
{
KeyDown = 0x0100,
KeyUp = 0x0101,
SysKeyDown = 0x0104,
SysKeyUp = 0x0105
}
}
-12
View File
@@ -1,12 +0,0 @@
using System;
namespace Daybreak.Models
{
public class Log
{
public LogLevel LogLevel { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public DateTime Timestamp { get; set; }
}
}
-10
View File
@@ -1,10 +0,0 @@
namespace Daybreak.Models
{
public enum LogLevel
{
Information,
Warning,
Error,
Critical
}
}
+14
View File
@@ -0,0 +1,14 @@
using Microsoft.CorrelationVector;
namespace Daybreak.Models
{
public class ScopeMetadata
{
public CorrelationVector CorrelationVector { get; set; }
public ScopeMetadata(CorrelationVector correlationVector)
{
this.CorrelationVector = correlationVector;
}
}
}
+10
View File
@@ -0,0 +1,10 @@
using System.Windows;
namespace Daybreak.Models
{
public sealed class Screen
{
public int Id { get; set; }
public Rect Size { get; set; }
}
}
+3 -1
View File
@@ -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;
+158
View File
@@ -0,0 +1,158 @@
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);
}
else
{
break;
}
}
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)
@@ -138,13 +178,45 @@ namespace Daybreak.Services.ApplicationLauncher
StartInfo = new ProcessStartInfo
{
Arguments = string.Join(" ", args),
UserName = identity
FileName = executable.Path
}
};
if (Process.Start(executable.Path, args) is null)
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();
}
}
+8 -8
View File
@@ -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>();
}
}
+2 -2
View File
@@ -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));
}
@@ -229,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;
}
}
}
}
}
-11
View File
@@ -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);
}
}
+12
View File
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using WpfExtended.Logging;
using WpfExtended.Models;
namespace Daybreak.Services.Logging
{
public interface ILogsManager : ILogsWriter
{
IEnumerable<Log> GetLogs();
int DeleteLogs();
}
}
@@ -0,0 +1,30 @@
using LiteDB;
using System.Collections.Generic;
using System.Extensions;
using WpfExtended.Models;
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();
}
}
}
-42
View File
@@ -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);
}
}
}
+6 -1
View File
@@ -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);
}
}
+29 -8
View File
@@ -1,6 +1,9 @@
using Slim;
using Daybreak.Models;
using Microsoft.CorrelationVector;
using Slim;
using System;
using System.Extensions;
using System.Windows;
using System.Windows.Controls;
namespace Daybreak.Services.ViewManagement
@@ -32,17 +35,35 @@ 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)
{
var scopedManager = this.serviceManager.CreateScope();
scopedManager.As<IServiceManager>().RegisterScoped<ScopeMetadata, ScopeMetadata>((sp) => new ScopeMetadata(new CorrelationVector()));
Application.Current.Dispatcher.Invoke(() =>
{
var view = scopedManager.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()
{
}
}
}
+92 -33
View File
@@ -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);
}
}
-65
View File
@@ -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);
}
}
}
+30 -2
View File
@@ -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();
}
}
-25
View File
@@ -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);
+14 -8
View File
@@ -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>
+31 -7
View File
@@ -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);
}
}
}
+12 -1
View File
@@ -11,6 +11,7 @@
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
@@ -35,7 +36,17 @@
<controls:SaveButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5"
Clicked="SaveButton_Clicked" Grid.Column="2" IsEnabled="{Binding ElementName=_this, Path=SaveButtonEnabled, Mode=OneWay}"></controls:SaveButton>
</Grid>
<controls:BuildTemplate Grid.Row="1" DataContext="{Binding ElementName=_this, Path=CurrentBuild, Mode=OneWay}">
<Grid Grid.Row="1" Margin="10, 0, 10, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Code: " Foreground="White" Background="Transparent" FontSize="16"></TextBlock>
<TextBox Grid.Column="1" Foreground="White" Background="Transparent" FontSize="16"
Text="{Binding ElementName=_this, Path=CurrentBuildCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
KeyDown="BuildCodeTextBox_KeyDown"></TextBox>
</Grid>
<controls:BuildTemplate x:Name="BuildTemplate" Grid.Row="2" DataContext="{Binding ElementName=_this, Path=CurrentBuild, Mode=OneWay}">
</controls:BuildTemplate>
</Grid>
</UserControl>
+44 -20
View File
@@ -1,12 +1,17 @@
using Daybreak.Models.Builds;
using Daybreak.Controls;
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.IconRetrieve;
using Daybreak.Services.ViewManagement;
using Microsoft.Extensions.Logging;
using System;
using System.Extensions;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Input;
namespace Daybreak.Views
{
@@ -17,47 +22,67 @@ namespace Daybreak.Views
{
private const string DisallowedChars = "\r\n\\/.";
public readonly static DependencyProperty CurrentBuildProperty =
DependencyPropertyExtensions.Register<BuildTemplateView, BuildEntry>(nameof(CurrentBuild));
public readonly static DependencyProperty SaveButtonEnabledProperty =
DependencyPropertyExtensions.Register<BuildTemplateView, bool>(nameof(SaveButtonEnabled), new PropertyMetadata(false));
private readonly IViewManager viewManager;
private readonly IBuildTemplateManager buildTemplateManager;
private readonly ILogger<BuildTemplateView> logger;
public bool SaveButtonEnabled
{
get => this.GetTypedValue<bool>(SaveButtonEnabledProperty);
set => this.SetValue(SaveButtonEnabledProperty, value);
}
public BuildEntry CurrentBuild
{
get => this.GetTypedValue<BuildEntry>(CurrentBuildProperty);
set => this.SetValue(CurrentBuildProperty, value);
}
[GenerateDependencyProperty(InitialValue = false)]
private bool saveButtonEnabled;
[GenerateDependencyProperty]
private BuildEntry currentBuild;
[GenerateDependencyProperty]
private string currentBuildCode;
public BuildTemplateView(
IViewManager viewManager,
IBuildTemplateManager buildTemplateManager)
IBuildTemplateManager buildTemplateManager,
IIconRetriever iconRetriever,
IConfigurationManager configurationManager,
ILogger<ChromiumBrowserWrapper> chromiumLogger,
ILogger<BuildTemplateView> logger)
{
this.buildTemplateManager = buildTemplateManager.ThrowIfNull(nameof(buildTemplateManager));
this.logger = logger.ThrowIfNull(nameof(logger));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.InitializeComponent();
this.BuildTemplate.InitializeTemplate(iconRetriever, configurationManager, buildTemplateManager, chromiumLogger);
this.DataContextChanged += (sender, contextArgs) =>
{
if (contextArgs.NewValue is BuildEntry)
{
this.logger.LogInformation("Received data context. Setting current build");
this.CurrentBuild = contextArgs.NewValue.As<BuildEntry>();
this.CurrentBuildCode = this.buildTemplateManager.EncodeTemplate(this.CurrentBuild.Build);
}
};
}
private void BuildCodeTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
this.logger.LogInformation($"Attempting to decode provided template {sender.As<TextBox>().Text}");
try
{
this.CurrentBuild = new BuildEntry
{
Name = this.CurrentBuild.Name,
PreviousName = this.CurrentBuild.PreviousName,
Build = this.buildTemplateManager.DecodeTemplate(sender.As<TextBox>().Text)
};
this.logger.LogInformation($"Template {sender.As<TextBox>().Text} decoded");
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to decode template {sender.As<TextBox>().Text}", ex);
}
}
}
private void BackButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<BuildsListView>();
}
private void SaveButton_Clicked(object sender, EventArgs e)
{
this.buildTemplateManager.SaveBuild(this.CurrentBuild);
@@ -71,7 +96,6 @@ namespace Daybreak.Views
e.Handled = true;
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(sender.As<TextBox>().Text))
+5 -5
View File
@@ -87,11 +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>
<TextBlock Text="Launch gw as current user" 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}"
@@ -23,6 +23,8 @@ namespace Daybreak.Views
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;
@@ -47,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,
@@ -65,6 +72,7 @@ namespace Daybreak.Views
this.GWToolboxLaunchDelay = config.ExperimentalFeatures.ToolboxAutoLaunchDelay.ToString();
this.DynamicBuildLoading = config.ExperimentalFeatures.DynamicBuildLoading;
this.LaunchAsCurrentUser = config.ExperimentalFeatures.LaunchGuildwarsAsCurrentUser;
this.MacrosEnabled = config.ExperimentalFeatures.CanInterceptKeys;
}
private void SaveExperimentalSettings()
@@ -73,6 +81,7 @@ namespace Daybreak.Views
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;
+61
View File
@@ -0,0 +1,61 @@
<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="CV" Binding="{Binding CorrelationVector}" 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>
+49
View File
@@ -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 WpfExtended.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();
}
}
}
+4 -2
View File
@@ -40,7 +40,8 @@
Clicked="LaunchTexmodButton_Clicked" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="1"
Visibility="{Binding ElementName=_this, Path=ButtonsVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"></controls:OpaqueButton>
<Grid Grid.Column="2" Margin="10" Visibility="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}">
<controls:ChromiumBrowserWrapper Address="{Binding ElementName=_this, Path=RightBrowserAddress, Mode=OneWay}"
<controls:ChromiumBrowserWrapper x:Name="RightWebBrowser"
Address="{Binding ElementName=_this, Path=RightBrowserAddress, Mode=OneWay}"
Foreground="White" FavoriteUriChanged="RightBrowser_FavoriteUriChanged"
FavoriteAddress="{Binding ElementName=_this, Path=RightBrowserFavoriteAddress, Mode=OneWay}"
MaximizeClicked="RightChromiumBrowserWrapper_MaximizeClicked"
@@ -48,7 +49,8 @@
CanDownloadBuild="True"/>
</Grid>
<Grid Grid.Column="0" Margin="10" Visibility="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}">
<controls:ChromiumBrowserWrapper Address="{Binding ElementName=_this, Path=LeftBrowserAddress, Mode=OneWay}"
<controls:ChromiumBrowserWrapper x:Name="LeftWebBrowser"
Address="{Binding ElementName=_this, Path=LeftBrowserAddress, Mode=OneWay}"
Foreground="White" FavoriteUriChanged="LeftBrowser_FavoriteUriChanged"
FavoriteAddress="{Binding ElementName=_this, Path=LeftBrowserFavoriteAddress, Mode=OneWay}"
MaximizeClicked="LeftChromiumBrowserWrapper_MaximizeClicked"
+60 -67
View File
@@ -1,10 +1,15 @@
using Daybreak.Exceptions;
using Daybreak.Controls;
using Daybreak.Exceptions;
using Daybreak.Models.Builds;
using Daybreak.Services.ApplicationLauncher;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.Screens;
using Daybreak.Services.ViewManagement;
using Microsoft.Extensions.Logging;
using System;
using System.Extensions;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -16,94 +21,66 @@ 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 IBuildTemplateManager buildTemplateManager;
private readonly ILogger<ChromiumBrowserWrapper> browserLogger;
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,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> browserLogger)
{
this.browserLogger = browserLogger;
this.buildTemplateManager = buildTemplateManager.ThrowIfNull(nameof(buildTemplateManager));
this.screenManager = screenManager.ThrowIfNull(nameof(screenManager));
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
this.applicationDetector = applicationDetector.ThrowIfNull(nameof(applicationDetector));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.InitializeComponent();
this.PeriodicallyCheckGameState();
this.InitializeBrowsers();
this.NavigateToDefaults();
}
private void InitializeBrowsers()
{
this.LeftWebBrowser.InitializeBrowser(this.configurationManager, this.buildTemplateManager, this.browserLogger);
this.RightWebBrowser.InitializeBrowser(this.configurationManager, this.buildTemplateManager, this.browserLogger);
}
private void NavigateToDefaults()
{
var applicationConfiguration = this.configurationManager.GetConfiguration();
@@ -145,7 +122,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;
+28
View File
@@ -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>();
}
}
}
}
+28
View File
@@ -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>
+104
View File
@@ -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);
}
}
}
+12
View File
@@ -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>();
+83 -54
View File
@@ -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>
+66 -60
View File
@@ -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;
}
}
}
}
+8 -5
View File
@@ -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>
+20 -28
View File
@@ -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");
+46
View File
@@ -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>();
}
}
}
}
-22
View File
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
</assembly>

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