Compare commits

...
20 Commits
Author SHA1 Message Date
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
90 changed files with 2180 additions and 933 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>
+134
View File
@@ -0,0 +1,134 @@
using Daybreak.Models.Versioning;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Linq;
using Version = Daybreak.Models.Versioning.Version;
namespace Daybreak.Tests.Models
{
[TestClass]
public class VersionTests
{
[DataRow("v0.1.0", "v0.1")]
[DataRow("0.1.0", "0.1")]
[DataRow("v0.1.0.0.0", "v0.1")]
[DataRow("0.1.0.0.0", "0.1")]
[DataRow("v0", "v0")]
[DataRow("0", "0")]
[DataRow("v0.1", "v0.1")]
[DataRow("0.1-release", "0.1-release")]
[DataRow("v0.1-release", "v0.1-release")]
[TestMethod]
public void VersionToString(string version, string expectedString)
{
var parsedVersion = Version.Parse(version);
parsedVersion.ToString().Should().Be(expectedString);
}
[DataRow("v0", false)]
[DataRow("0", false)]
[DataRow("v0.1", false)]
[DataRow("0.1-release", true)]
[DataRow("v0.1-release", true)]
[TestMethod]
public void VersionHasStringSuffix(string version, bool hasSuffix)
{
var parsedVersion = Version.Parse(version);
if (hasSuffix)
{
parsedVersion.VersionTokens.Last().Should().BeOfType<VersionStringToken>();
}
else
{
parsedVersion.VersionTokens.Last().Should().BeOfType<VersionNumberToken>();
}
}
[DataRow("v0", true)]
[DataRow("0", false)]
[DataRow("v0.1", true)]
[DataRow("0.1", false)]
[TestMethod]
public void VersionTokensHasPrefix(string version, bool hasPrefix)
{
var parsedVersion = Version.Parse(version);
parsedVersion.HasPrefix.Should().Be(hasPrefix);
}
[DataRow(null, false)]
[DataRow("", false)]
[DataRow("asd", false)]
[DataRow("ads.pls.er", false)]
[DataRow("0.0.1", true)]
[DataRow("1", true)]
[DataRow("0.1.asd", false)]
[DataRow("0.", false)]
[DataRow(".1", false)]
[DataRow("0.1-", false)]
[DataRow("0.1-release", true)]
[DataRow("0..1", false)]
[TestMethod]
public void TryParseVersionTest(string version, bool result)
{
var success = Version.TryParse(version, out var parsedVersion);
success.Should().Be(result);
if (success is true)
{
parsedVersion.Should().NotBeNull();
}
}
[DataRow(null, false)]
[DataRow("", false)]
[DataRow("asd", false)]
[DataRow("ads.pls.er", false)]
[DataRow("0.0.1", true)]
[DataRow("1", true)]
[DataRow("0.1.asd", false)]
[DataRow("0.", false)]
[DataRow(".1", false)]
[DataRow("0.1-", false)]
[DataRow("0.1-release", true)]
[DataRow("0..1", false)]
[TestMethod]
public void ParseVersionTest(string version, bool shouldNotThrow)
{
var action = new Action(() => Version.Parse(version));
if (shouldNotThrow is false)
{
action.Should().Throw<InvalidOperationException>();
}
else
{
action.Should().NotThrow<InvalidOperationException>();
}
}
[DataRow(null, false)]
[DataRow("", false)]
[DataRow("asd", false)]
[DataRow("ads.pls.er", false)]
[DataRow("0.0.1", true)]
[DataRow("1", true)]
[DataRow("0.1.asd", false)]
[DataRow("0.", false)]
[DataRow(".1", false)]
[DataRow("0.1-", false)]
[DataRow("0.1-release", true)]
[DataRow("0..1", false)]
[TestMethod]
public void VersionConstructorTest(string version, bool shouldNotThrow)
{
var action = new Action(() => new Version(version));
if (shouldNotThrow is false)
{
action.Should().Throw<ArgumentException>();
}
else
{
action.Should().NotThrow<ArgumentException>();
}
}
}
}
@@ -1,7 +1,7 @@
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Logging;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Generic;
@@ -17,7 +17,7 @@ namespace Daybreak.Tests.Services
[TestInitialize]
public void Initialize()
{
buildTemplateManager = new BuildTemplateManager(new Mock<ILogger>().Object);
buildTemplateManager = new BuildTemplateManager(new Mock<ILogger<BuildTemplateManager>>().Object);
}
[TestMethod]
@@ -0,0 +1,60 @@
using Daybreak.Services.Logging;
using FluentAssertions;
using LiteDB;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
namespace Daybreak.Tests.Services
{
[TestClass]
public class JsonLoggerProviderTests
{
private ILogsManager logsManager;
private ILoggerProvider loggerProvider;
private ILiteDatabase liteDatabase;
[TestInitialize]
public void InitializeProvider()
{
File.Delete("Daybreak.db");
this.liteDatabase = new LiteDatabase("Daybreak.db");
this.logsManager = new JsonLogsManager(this.liteDatabase);
this.loggerProvider = new JsonLoggerProvider(this.logsManager);
}
[TestCleanup]
public void TestCleanup()
{
this.liteDatabase.Dispose();
}
[TestMethod]
public void CreateLoggerReturnsLogger()
{
var logger = this.loggerProvider.CreateLogger("SomeCategory");
logger.Should().NotBeNull();
}
[TestMethod]
public void LoggerLogsAndReaderReads()
{
var logger = this.loggerProvider.CreateLogger("SomeCategory");
logger.LogInformation("Logging some stuff");
logger.LogError("Logging some error");
this.logsManager.GetLogs().Should().HaveCount(2);
}
[TestMethod]
public void DeletingLogsShouldDeleteLogs()
{
var logger = this.loggerProvider.CreateLogger("SomeCategory");
logger.LogInformation("Logging some stuff");
logger.LogError("Logging some error");
this.logsManager.GetLogs().Should().HaveCount(2);
this.logsManager.DeleteLogs();
this.logsManager.GetLogs().Should().HaveCount(0);
}
}
}
@@ -1,5 +1,6 @@
using Daybreak.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace Daybreak.Configuration
@@ -30,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();
}
}
+34 -3
View File
@@ -11,23 +11,49 @@ using Daybreak.Services.Privilege;
using Daybreak.Services.Runtime;
using Daybreak.Services.Screens;
using Daybreak.Services.Screenshots;
using Daybreak.Services.Shortcuts;
using Daybreak.Services.Updater;
using Daybreak.Services.ViewManagement;
using Daybreak.Views;
using Microsoft.Extensions.Http.Logging;
using Microsoft.Extensions.Logging;
using Slim;
using System.Extensions;
using System.Net.Http;
using System.Windows.Extensions.Http;
using System.Windows.Extensions;
using LiteDB;
namespace Daybreak.Configuration
{
public static class ProjectConfiguration
{
public static void RegisterResolvers(IServiceManager serviceManager)
{
serviceManager.ThrowIfNull(nameof(serviceManager));
serviceManager.RegisterLoggerFactory((sp) =>
{
var factory = new LoggerFactory();
factory.AddProvider(new JsonLoggerProvider(sp.GetService<ILogsManager>()));
return factory;
});
serviceManager.RegisterResolver(
new HttpClientResolver()
.WithHttpMessageHandlerFactory((serviceProvider, categoryType) =>
{
var loggerType = typeof(ILogger<>).MakeGenericType(categoryType);
var logger = serviceProvider.GetService(loggerType).As<ILogger>();
var handler = new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
return handler;
}));
}
public static void RegisterServices(IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterSingleton<ICredentialManager, CredentialManager>();
serviceProducer.RegisterSingleton<ILoggingDatabase, FlatLoggingDatabase>();
serviceProducer.RegisterSingleton<ILogger, Logger>();
serviceProducer.RegisterSingleton<ApplicationLifetimeManager>();
serviceProducer.RegisterSingleton<ViewManager>();
serviceProducer.RegisterSingleton<IApplicationLauncher, ApplicationLauncher>();
@@ -41,14 +67,17 @@ namespace Daybreak.Configuration
serviceProducer.RegisterSingleton<IIconRetriever, IconRetriever>();
serviceProducer.RegisterSingleton<IPrivilegeManager, PrivilegeManager>();
serviceProducer.RegisterSingleton<IScreenManager, ScreenManager>();
serviceProducer.RegisterSingleton<IShortcutManager, ShortcutManager>();
serviceProducer.RegisterSingleton<ILogsManager, JsonLogsManager>();
serviceProducer.RegisterSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
}
public static void RegisterLifetimeServices(IApplicationLifetimeProducer applicationLifetimeProducer)
{
applicationLifetimeProducer.ThrowIfNull(nameof(applicationLifetimeProducer));
applicationLifetimeProducer.RegisterService<ILoggingDatabase>();
applicationLifetimeProducer.RegisterService<IScreenshotProvider>();
applicationLifetimeProducer.RegisterService<IApplicationUpdater>();
applicationLifetimeProducer.RegisterService<IShortcutManager>();
}
public static void RegisterViews(IViewProducer viewProducer)
{
@@ -66,6 +95,8 @@ namespace Daybreak.Configuration
viewProducer.RegisterView<BuildsListView>();
viewProducer.RegisterView<RequestElevationView>();
viewProducer.RegisterView<ScreenChoiceView>();
viewProducer.RegisterView<VersionManagementView>();
viewProducer.RegisterView<LogsView>();
}
}
}
@@ -3,8 +3,8 @@ using Daybreak.Models.Browser;
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.Logging;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Web.WebView2.Core;
using System;
using System.Diagnostics;
@@ -19,79 +19,44 @@ namespace Daybreak.Controls
/// <summary>
/// Interaction logic for ChromiumBrowserWrapper.xaml
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class ChromiumBrowserWrapper : UserControl
{
private const string BrowserDownloadLink = "https://developer.microsoft.com/en-us/microsoft-edge/webview2/";
public readonly static DependencyProperty AddressProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, string>(nameof(Address));
public readonly static DependencyProperty FavoriteAddressProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, string>(nameof(FavoriteAddress));
public readonly static DependencyProperty NavigatingProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(Navigating));
public readonly static DependencyProperty BrowserEnabledProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(BrowserEnabled));
public readonly static DependencyProperty AddressBarReadonlyProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(AddressBarReadonly));
public readonly static DependencyProperty BrowserSupportedProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(BrowserSupported), new PropertyMetadata(true));
public readonly static DependencyProperty ControlsEnabledProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(ControlsEnabled), new PropertyMetadata(true));
public readonly static DependencyProperty CanNavigateProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(CanNavigate), new PropertyMetadata(true));
public readonly static DependencyProperty CanDownloadBuildProperty = DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, bool>(nameof(CanDownloadBuild), new PropertyMetadata(false));
public event EventHandler<string> FavoriteUriChanged;
public event EventHandler MaximizeClicked;
public event EventHandler<Build> BuildDecoded;
private readonly IConfigurationManager configurationManager;
private readonly ILogger logger;
private readonly ILogger<ChromiumBrowserWrapper> logger;
private readonly IBuildTemplateManager buildTemplateManager;
private CoreWebView2Environment coreWebView2Environment;
public bool CanDownloadBuild
{
get => this.GetTypedValue<bool>(CanDownloadBuildProperty);
set => this.SetTypedValue<bool>(CanDownloadBuildProperty, value);
}
public bool CanNavigate
{
get => this.GetTypedValue<bool>(CanNavigateProperty);
set => this.SetTypedValue<bool>(CanNavigateProperty, value);
}
public string Address
{
get => this.GetTypedValue<string>(AddressProperty);
set => this.SetTypedValue(AddressProperty, value);
}
public string FavoriteAddress
{
get => this.GetTypedValue<string>(FavoriteAddressProperty);
set => this.SetTypedValue(FavoriteAddressProperty, value);
}
public bool Navigating
{
get => this.GetTypedValue<bool>(NavigatingProperty);
private set => this.SetTypedValue<bool>(NavigatingProperty, value);
}
public bool AddressBarReadonly
{
get => this.GetTypedValue<bool>(AddressBarReadonlyProperty);
set => this.SetTypedValue<bool>(AddressBarReadonlyProperty, value);
}
public bool BrowserEnabled
{
get => this.GetTypedValue<bool>(BrowserEnabledProperty);
private set => this.SetTypedValue<bool>(BrowserEnabledProperty, value);
}
public bool BrowserSupported
{
get => this.GetTypedValue<bool>(BrowserSupportedProperty);
private set => this.SetTypedValue<bool>(BrowserSupportedProperty, value);
}
public bool ControlsEnabled
{
get => this.GetTypedValue<bool>(ControlsEnabledProperty);
set => this.SetTypedValue<bool>(ControlsEnabledProperty, value);
}
[GenerateDependencyProperty(InitialValue = true)]
private bool canDownloadBuild;
[GenerateDependencyProperty(InitialValue = true)]
private bool canNavigate;
[GenerateDependencyProperty(InitialValue = true)]
private bool controlsEnabled;
[GenerateDependencyProperty(InitialValue = true)]
private bool browserSupported;
[GenerateDependencyProperty]
private bool addressBarReadonly;
[GenerateDependencyProperty]
private bool browserEnabled;
[GenerateDependencyProperty]
private bool navigating;
[GenerateDependencyProperty]
private string favoriteAddress;
[GenerateDependencyProperty]
private string address;
public ChromiumBrowserWrapper()
{
this.configurationManager = Launcher.ApplicationServiceManager.GetService<IConfigurationManager>();
this.logger = Launcher.ApplicationServiceManager.GetService<ILogger>();
this.logger = Launcher.ApplicationServiceManager.GetService<ILogger<ChromiumBrowserWrapper>>();
this.buildTemplateManager = Launcher.ApplicationServiceManager.GetService<IBuildTemplateManager>();
this.InitializeComponent();
this.InitializeEnvironment();
@@ -209,7 +174,7 @@ namespace Daybreak.Controls
}
catch(Exception e)
{
this.logger.LogError(e);
this.logger.LogError(e, $"Exception encountered when deserializing {nameof(BrowserPayload)}");
}
if (payload?.Key == BrowserPayload.PayloadKeys.ContextMenu)
{
+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()
{
@@ -1,103 +1,48 @@
using Daybreak.Launch;
using Daybreak.Models.Builds;
using Daybreak.Services.IconRetrieve;
using System.Collections;
using Daybreak.Models.Builds;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Extensions;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Daybreak.Controls
{
/// <summary>
/// Interaction logic for BuildTemplate.xaml
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class BuildTemplate : UserControl
{
private const string 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 +50,18 @@ namespace Daybreak.Controls
public BuildTemplate()
{
this.InitializeComponent();
this.InitializeProperties();
this.DataContextChanged += BuildTemplate_DataContextChanged;
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (this.loadedProperties is false)
{
return;
}
if (e.Property == PrimaryProfessionProperty || e.Property == SecondaryProfessionProperty)
{
if (e.Property == PrimaryProfessionProperty)
@@ -126,6 +77,21 @@ namespace Daybreak.Controls
}
}
private void InitializeProperties()
{
this.PrimaryProfession = Profession.None;
this.SecondaryProfession = Profession.None;
this.Skill0 = Skill.NoSkill;
this.Skill1 = Skill.NoSkill;
this.Skill2 = Skill.NoSkill;
this.Skill3 = Skill.NoSkill;
this.Skill4 = Skill.NoSkill;
this.Skill5 = Skill.NoSkill;
this.Skill6 = Skill.NoSkill;
this.Skill7 = Skill.NoSkill;
this.loadedProperties = true;
}
private void BuildTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue is BuildEntry)
@@ -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()
{
@@ -11,26 +11,16 @@ 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 static readonly DependencyProperty ScreenIdProperty =
DependencyPropertyExtensions.Register<ScreenTemplate, string>(nameof(ScreenId));
public static readonly DependencyProperty HighlightProperty =
DependencyPropertyExtensions.Register<ScreenTemplate, Brush>(nameof(Highlight));
public event EventHandler<Screen> Clicked;
public string ScreenId
{
get => this.GetTypedValue<string>(ScreenIdProperty);
set => this.SetValue(ScreenIdProperty, value);
}
public Brush Highlight
{
get => this.GetTypedValue<Brush>(HighlightProperty);
set => this.SetValue(HighlightProperty, value);
}
[GenerateDependencyProperty]
private string screenId;
[GenerateDependencyProperty]
private Brush highlight;
public ScreenTemplate()
{
@@ -16,28 +16,19 @@ namespace Daybreak.Controls
/// <summary>
/// Interaction logic for SkillTemplate.xaml
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class SkillTemplate : UserControl
{
public readonly static DependencyProperty ImageSourceProperty =
DependencyPropertyExtensions.Register<SkillTemplate, ImageSource>(nameof(ImageSource));
public readonly static DependencyProperty BorderOpacityProperty =
DependencyPropertyExtensions.Register<SkillTemplate, double>(nameof(BorderOpacity), new PropertyMetadata(0d));
public event EventHandler<RoutedEventArgs> Clicked;
public event EventHandler RemoveClicked;
private readonly IIconRetriever iconRetriever;
public ImageSource ImageSource
{
get => this.GetTypedValue<ImageSource>(ImageSourceProperty);
set => this.SetValue(ImageSourceProperty, value);
}
public double BorderOpacity
{
get => this.GetTypedValue<double>(BorderOpacityProperty);
set => this.SetValue(BorderOpacityProperty, value);
}
[GenerateDependencyProperty]
private ImageSource imageSource;
[GenerateDependencyProperty]
private double borderOpacity;
public SkillTemplate()
{
+16 -4
View File
@@ -2,6 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
@@ -9,18 +10,25 @@
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<Version>0.8.2</Version>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.8.9.3</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.32" />
<PackageReference Include="LiteDB" Version="5.0.10" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.818.41" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Slim" Version="1.2.1" />
<PackageReference Include="NReco.Logging.File" Version="1.1.1" />
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
<PackageReference Include="Slim" Version="1.3.3" />
<PackageReference Include="System.Windows.Interactivity.WPF" Version="2.0.20525" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.1.4" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.2.0" />
<PackageReference Include="WCL" Version="1.0.2" />
<PackageReference Include="WpfExtended" Version="0.2.0" />
<PackageReference Include="WpfExtended" Version="0.3.3" />
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.1" />
<PackageReference Include="WpfScreenHelper" Version="1.0.0" />
</ItemGroup>
@@ -62,4 +70,8 @@
</Page>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'">
<Exec Command="echo.&gt;$(Version).version" />
</Target>
</Project>
+16 -6
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,6 +25,10 @@ namespace Daybreak.Launch
return LaunchMainWindow();
}
protected override void SetupServiceManager(IServiceManager serviceManager)
{
ProjectConfiguration.RegisterResolvers(serviceManager);
}
protected override void RegisterServices(IServiceProducer serviceProducer)
{
ProjectConfiguration.RegisterServices(this.ServiceManager);
@@ -41,10 +42,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)
+10 -7
View File
@@ -86,15 +86,18 @@
HorizontalAlignment="Right" Margin="0, 0, 20, 30" FontSize="22" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Clicked="CreditTextBox_MouseLeftButtonDown" Cursor="Hand"></controls:OpaqueButton>
<WrapPanel Grid.Row="1" Margin="0, 0, 20, 10" VerticalAlignment="Bottom" HorizontalAlignment="Right">
<TextBox Grid.Row="1" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Opacity="0.6"
Text="{Binding ElementName=_this, Path=CurrentVersionText, Mode=OneWay}" IsReadOnly="True" FontSize="10"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></TextBox>
<TextBox Grid.Row="1" Background="Transparent" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
<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}}"></TextBox>
<TextBox Grid.Row="1" Background="Transparent" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
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}}"></TextBox>
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>
+29 -23
View File
@@ -1,4 +1,5 @@
using Daybreak.Services.Bloogum;
using Daybreak.Services.Configuration;
using Daybreak.Services.Privilege;
using Daybreak.Services.Runtime;
using Daybreak.Services.Screenshots;
@@ -22,54 +23,54 @@ 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));
public static readonly DependencyProperty IsRunningAsAdminProperty = DependencyPropertyExtensions.Register<MainWindow, bool>(nameof(IsRunningAsAdmin));
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);
}
public bool IsRunningAsAdmin
{
get => this.GetTypedValue<bool>(IsRunningAsAdminProperty);
set => this.SetValue(IsRunningAsAdminProperty, value);
}
[GenerateDependencyProperty]
private string creditText;
[GenerateDependencyProperty]
private string currentVersionText;
[GenerateDependencyProperty]
private bool isRunningAsAdmin;
public MainWindow(
IViewManager viewManager,
IScreenshotProvider screenshotProvider,
IBloogumClient bloogumClient,
IApplicationUpdater applicationUpdater,
IPrivilegeManager privilegeManager)
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)
{
this.SetupImageCycle();
@@ -152,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);
@@ -172,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>();
+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 -4
View File
@@ -1,12 +1,20 @@
using System;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
namespace Daybreak.Models
{
public class Log
public sealed class Log
{
[JsonProperty("DateTime")]
public DateTime LogTime { get; set; }
[JsonProperty("Category")]
public string Category { get; set; }
[JsonProperty("EventId")]
public string EventId { get; set; }
[JsonProperty("LogLevel")]
public LogLevel LogLevel { get; set; }
[JsonProperty("Message")]
public string Message { get; set; }
public string StackTrace { get; set; }
public DateTime Timestamp { get; set; }
}
}
-10
View File
@@ -1,10 +0,0 @@
namespace Daybreak.Models
{
public enum LogLevel
{
Information,
Warning,
Error,
Critical
}
}
+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;
+154
View File
@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Extensions;
using System.Linq;
namespace Daybreak.Models.Versioning
{
public sealed class Version
{
private List<VersionToken> parts = new();
public bool HasPrefix { get; private set; }
public IEnumerable<VersionToken> VersionTokens { get => this.parts; }
public string VersionString { get => this.ToString(); }
private Version()
{
}
public Version(string version)
{
if (TryParseParts(version, out var parts, out var hasPrefix) is false)
{
throw new ArgumentException($"Provided argument is not valid: {version}");
}
this.parts = parts;
this.HasPrefix = hasPrefix;
}
public override string ToString()
{
var version = string.Join('.', parts.OfType<VersionNumberToken>());
if (parts.Last() is VersionStringToken)
{
version += "-" + parts.Last();
}
if (this.HasPrefix)
{
version = 'v' + version;
}
return version;
}
public static Version Parse(string version)
{
if (TryParse(version, out var parsedVersion) is false)
{
throw new InvalidOperationException($"Unable to parse version: {version}");
}
return parsedVersion;
}
public static bool TryParse(string version, out Version parsedVersion)
{
if (TryParseParts(version, out var parts, out var hasPrefix) is false)
{
parsedVersion = null;
return false;
}
for (int i = parts.Count - 1; i >= 1; i--)
{
if (parts[i] is VersionNumberToken && parts[i].ToString() == "0")
{
parts.RemoveAt(i);
}
}
parsedVersion = new Version
{
parts = parts,
HasPrefix = hasPrefix
};
return true;
}
private static bool TryParseParts(string version, out List<VersionToken> parts, out bool hasPrefix)
{
hasPrefix = false;
parts = null;
if (version.IsNullOrWhiteSpace())
{
return false;
}
if (version.StartsWith('.') || version.EndsWith('.'))
{
return false;
}
var tokens = version.Trim().Split('.');
if (tokens.Length == 0)
{
return false;
}
if (tokens[0].StartsWith('v'))
{
hasPrefix = true;
tokens[0] = tokens[0][1..];
}
parts = new List<VersionToken>();
for(int i = 0; i < tokens.Length - 1; i++)
{
var token = tokens[i];
if (token.IsNullOrWhiteSpace() || token.All(c => char.IsDigit(c)) is false)
{
return false;
}
parts.Add(VersionToken.Parse(token));
}
var lastToken = tokens[^1];
if (lastToken.Contains('-'))
{
var lastTokenParts = lastToken.Split('-');
if (lastTokenParts.Length > 2 || lastTokenParts.Length < 2)
{
return false;
}
if (lastTokenParts[0].IsNullOrWhiteSpace() || lastTokenParts[0].All(c => char.IsDigit(c)) is false)
{
return false;
}
parts.Add(VersionToken.Parse(lastTokenParts[0]));
if (lastTokenParts[1].IsNullOrWhiteSpace() || lastTokenParts[1].All(c => char.IsLetter(c)) is false)
{
return false;
}
parts.Add(VersionToken.Parse(lastTokenParts[1]));
}
else
{
if (lastToken.IsNullOrWhiteSpace() || lastToken.Any(c => char.IsDigit(c) is false))
{
return false;
}
parts.Add(VersionToken.Parse(lastToken));
}
return true;
}
}
}
@@ -0,0 +1,16 @@
namespace Daybreak.Models.Versioning
{
public sealed class VersionNumberToken : VersionToken
{
public int Number { get; }
internal VersionNumberToken(int number)
{
this.Number = number;
}
protected override string Stringify()
{
return this.Number.ToString();
}
}
}
@@ -0,0 +1,16 @@
namespace Daybreak.Models.Versioning
{
public sealed class VersionStringToken : VersionToken
{
public string Token { get; }
internal VersionStringToken(string token)
{
this.Token = token;
}
protected override string Stringify()
{
return this.Token;
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Linq;
namespace Daybreak.Models.Versioning
{
public abstract class VersionToken
{
public static VersionToken Parse(string token)
{
if (token.All(c => char.IsDigit(c)))
{
var number = int.Parse(token);
return new VersionNumberToken(number);
}
if (token.All(c => char.IsLetter(c)))
{
return new VersionStringToken(token);
}
throw new ArgumentException($"Failed to parse version token {token}");
}
protected abstract string Stringify();
public override string ToString()
{
return this.Stringify();
}
}
}
@@ -1,11 +1,10 @@
using Daybreak.Exceptions;
using Daybreak.Services.Configuration;
using Daybreak.Services.Credentials;
using Daybreak.Services.Logging;
using Daybreak.Services.Mutex;
using Daybreak.Services.Privilege;
using Daybreak.Utils;
using Daybreak.Views;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
using Pepa.Wpf.Utilities;
using System;
@@ -33,7 +32,7 @@ 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();
@@ -44,7 +43,7 @@ namespace Daybreak.Services.ApplicationLauncher
IConfigurationManager configurationManager,
ICredentialManager credentialManager,
IMutexHandler mutexHandler,
ILogger logger,
ILogger<ApplicationLauncher> logger,
IPrivilegeManager privilegeManager)
{
this.logger = logger.ThrowIfNull(nameof(logger));
@@ -179,10 +178,10 @@ 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}");
}
@@ -190,7 +189,7 @@ namespace Daybreak.Services.ApplicationLauncher
var retries = 0;
while (true)
{
await Task.Delay(1000);
await Task.Delay(100);
retries++;
var gwProcess = Process.GetProcessesByName("gw").FirstOrDefault();
if (gwProcess is null && retries < MaxRetries)
@@ -202,10 +201,21 @@ namespace Daybreak.Services.ApplicationLauncher
throw new InvalidOperationException("Newly launched gw process not detected");
}
if (gwProcess.MainWindowHandle != IntPtr.Zero)
if (gwProcess.MainWindowHandle == IntPtr.Zero)
{
return true;
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;
}
}
+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 Daybreak.Models;
using System.Collections.Generic;
namespace Daybreak.Services.Logging
{
public interface ILogsManager
{
void WriteLog(Log log);
IEnumerable<Log> GetLogs();
int DeleteLogs();
}
}
+45
View File
@@ -0,0 +1,45 @@
using Daybreak.Models;
using Microsoft.Extensions.Logging;
using System;
using System.Extensions;
namespace Daybreak.Services.Logging
{
public sealed class JsonLogger : ILogger
{
private readonly string category;
private readonly JsonLoggerProvider jsonLoggerProvider;
public JsonLogger(string category, JsonLoggerProvider jsonLoggerProvider)
{
this.category = category;
this.jsonLoggerProvider = jsonLoggerProvider.ThrowIfNull(nameof(jsonLoggerProvider));
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var message = formatter(state, exception);
var log = new Log
{
LogLevel = logLevel,
EventId = eventId.Name,
Message = message,
Category = category,
LogTime = DateTime.Now
};
this.jsonLoggerProvider.LogEntry(log);
}
}
}
@@ -0,0 +1,30 @@
using Daybreak.Models;
using Microsoft.Extensions.Logging;
using System.Extensions;
namespace Daybreak.Services.Logging
{
public sealed class JsonLoggerProvider : ILoggerProvider
{
private readonly ILogsManager logsManager;
public JsonLoggerProvider(ILogsManager logsManager)
{
this.logsManager = logsManager.ThrowIfNull(nameof(logsManager));
}
public void LogEntry(Log log)
{
this.logsManager.WriteLog(log);
}
public ILogger CreateLogger(string categoryName)
{
return new JsonLogger(categoryName, this);
}
public void Dispose()
{
}
}
}
@@ -0,0 +1,30 @@
using Daybreak.Models;
using LiteDB;
using System.Collections.Generic;
using System.Extensions;
namespace Daybreak.Services.Logging
{
public sealed class JsonLogsManager : ILogsManager
{
private readonly ILiteDatabase liteDatabase;
public JsonLogsManager(ILiteDatabase liteDatabase)
{
this.liteDatabase = liteDatabase.ThrowIfNull(nameof(liteDatabase));
}
public IEnumerable<Log> GetLogs()
{
return this.liteDatabase.GetCollection<Log>().FindAll();
}
public void WriteLog(Log log)
{
this.liteDatabase.GetCollection<Log>().Insert(log);
}
public int DeleteLogs()
{
return this.liteDatabase.GetCollection<Log>().DeleteAll();
}
}
}
-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);
}
}
}
@@ -1,8 +1,7 @@
using Daybreak.Models;
using Daybreak.Services.Logging;
using Daybreak.Services.ViewManagement;
using Daybreak.Utils;
using Daybreak.Views;
using Microsoft.Extensions.Logging;
using System.Extensions;
using System.Security.Principal;
using System.Windows.Controls;
@@ -14,11 +13,11 @@ namespace Daybreak.Services.Privilege
public bool AdminPrivileges => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
private readonly IViewManager viewManager;
private readonly ILogger logger;
private readonly ILogger<PrivilegeManager> logger;
public PrivilegeManager(
IViewManager viewManager,
ILogger logger)
ILogger<PrivilegeManager> logger)
{
this.logger = logger.ThrowIfNull(nameof(logger));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
+3 -4
View File
@@ -1,6 +1,5 @@
using Daybreak.Models;
using Daybreak.Services.Logging;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using Pepa.Wpf.Utilities;
using System;
using System.Collections.Generic;
@@ -12,13 +11,13 @@ namespace Daybreak.Services.Screens
{
public sealed class ScreenManager : IScreenManager
{
private readonly ILogger logger;
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 logger)
ILogger<ScreenManager> logger)
{
this.logger = logger.ThrowIfNull(nameof(logger));
}
@@ -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()
{
}
}
}
+72 -26
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,6 +19,7 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Version = Daybreak.Models.Versioning.Version;
namespace Daybreak.Services.Updater
{
@@ -35,8 +39,10 @@ namespace Daybreak.Services.Updater
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}";
@@ -51,46 +57,55 @@ namespace Daybreak.Services.Updater
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;
@@ -113,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");
@@ -126,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 () =>
@@ -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);
}
}
}
+22 -2
View File
@@ -7,9 +7,13 @@ 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
@@ -93,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);
@@ -107,11 +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>
+12 -8
View File
@@ -1,10 +1,10 @@
using Daybreak.Services.Logging;
using Daybreak.Services.Privilege;
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
@@ -16,21 +16,24 @@ 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,
IPrivilegeManager privilegeManager)
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();
}
@@ -52,7 +55,7 @@ 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);
@@ -61,7 +64,8 @@ namespace Daybreak.Views
return;
}
this.viewManager.ShowView<UpdateView>();
var latestVersion = (await this.applicationUpdater.GetVersions()).Last();
this.viewManager.ShowView<UpdateView>(latestVersion);
}
}
}
+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;
+60
View File
@@ -0,0 +1,60 @@
<UserControl x:Class="Daybreak.Views.LogsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Views"
xmlns:controls="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}" x:Key="WrapText">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</UserControl.Resources>
<Grid Background="#A0202020">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<controls:BackButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Left" Margin="5"
Clicked="BackButton_Clicked"></controls:BackButton>
<controls:BinButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="5"
Clicked="BinButton_Clicked"></controls:BinButton>
<controls:RefreshGlyph Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="0, 5, 45, 5"
Clicked="RefreshGlyph_Clicked"></controls:RefreshGlyph>
<DataGrid IsReadOnly="True" Background="Transparent" Foreground="White" Grid.Row="1"
ItemsSource="{Binding ElementName=_this, Path=Logs, Mode=OneWay}" HorizontalScrollBarVisibility="Disabled"
AutoGenerateColumns="False" HeadersVisibility="Column" EnableColumnVirtualization="True"
EnableRowVirtualization="True">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="BorderBrush" Value="#80808080"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="BorderBrush" Value="#80808080"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True" Header="DateTime" Binding="{Binding LogTime}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTextColumn IsReadOnly="True" Header="Category" Binding="{Binding Category}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTextColumn IsReadOnly="True" Header="LogLevel" Binding="{Binding LogLevel}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTextColumn IsReadOnly="True" Header="EventId" Binding="{Binding EventId}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTextColumn IsReadOnly="True" Header="Message" Binding="{Binding Message}" ElementStyle="{StaticResource WrapText}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
+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 Daybreak.Models;
namespace Daybreak.Views
{
/// <summary>
/// Interaction logic for LogsView.xaml
/// </summary>
public partial class LogsView : UserControl
{
private readonly IViewManager viewManager;
private readonly ILogsManager logManager;
public ObservableCollection<Log> Logs { get; } = new ObservableCollection<Log>();
public LogsView(
IViewManager viewManager,
ILogsManager logManager)
{
this.logManager = logManager.ThrowIfNull(nameof(logManager));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.InitializeComponent();
this.UpdateLogs();
}
private void UpdateLogs()
{
this.Logs.ClearAnd().AddRange(this.logManager.GetLogs());
}
private void BackButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<SettingsCategoryView>();
}
private void BinButton_Clicked(object sender, EventArgs e)
{
this.logManager.DeleteLogs();
this.UpdateLogs();
}
private void RefreshGlyph_Clicked(object sender, EventArgs e)
{
this.UpdateLogs();
}
}
}
+21 -64
View File
@@ -18,27 +18,10 @@ 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;
@@ -48,51 +31,24 @@ namespace Daybreak.Views
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,
@@ -163,6 +119,7 @@ namespace Daybreak.Views
{
throw new InvalidOperationException($"Unable to set guildwars on desired screen. No screen with id {id}");
}
await Task.Delay(1000);
this.screenManager.MoveGuildwarsToScreen(desiredScreen);
}
+1 -1
View File
@@ -12,7 +12,7 @@
<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"></TextBlock>
FontSize="16" Foreground="Black" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
+3 -5
View File
@@ -1,9 +1,7 @@
using Daybreak.Models;
using Daybreak.Services.ApplicationLauncher;
using Daybreak.Services.Logging;
using Daybreak.Services.Privilege;
using Daybreak.Services.ViewManagement;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using System.Extensions;
using System.Windows.Controls;
@@ -16,12 +14,12 @@ namespace Daybreak.Views
{
private readonly IApplicationLauncher applicationLauncher;
private readonly IViewManager viewManager;
private readonly ILogger logger;
private readonly ILogger<RequestElevationView> logger;
public RequestElevationView(
IApplicationLauncher applicationLauncher,
IViewManager viewManager,
ILogger logger)
ILogger<RequestElevationView> logger)
{
this.applicationLauncher = applicationLauncher.ThrowIfNull(nameof(applicationLauncher));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
+5
View File
@@ -6,6 +6,7 @@
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>
@@ -14,6 +15,10 @@
</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">
+25 -2
View File
@@ -1,12 +1,15 @@
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
@@ -14,23 +17,32 @@ 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)
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();
}
@@ -77,5 +89,16 @@ namespace Daybreak.Views
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 -65
View File
@@ -66,71 +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" Height="30"></TextBlock>
<TextBlock Text="GWToolbox path: " FontSize="22" Foreground="White" Height="30"></TextBlock>
<TextBlock Text="GWToolbox auto-launch: " 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>
</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, UpdateSourceTrigger=PropertyChanged}"></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, 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=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, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
FontSize="22" Background="Transparent" Foreground="White"
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>
<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>
</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>
+52 -67
View File
@@ -1,4 +1,5 @@
using Daybreak.Services.Configuration;
using Daybreak.Services.Shortcuts;
using Daybreak.Services.ViewManagement;
using Microsoft.Win32;
using System;
@@ -6,81 +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));
public static readonly DependencyProperty AutoPlaceOnScreenProperty =
DependencyPropertyExtensions.Register<SettingsView, bool>(nameof(AutoPlaceOnScreen));
public static readonly DependencyProperty DesiredScreenProperty =
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(DesiredScreen));
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);
}
public bool AutoPlaceOnScreen
{
get => this.GetTypedValue<bool>(AutoPlaceOnScreenProperty);
set => this.SetValue(AutoPlaceOnScreenProperty, value);
}
public string DesiredScreen
{
get => this.GetTypedValue<string>(DesiredScreenProperty);
set => this.SetValue(DesiredScreenProperty, 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,
@@ -104,6 +68,9 @@ namespace Daybreak.Views
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)
@@ -118,13 +85,16 @@ namespace Daybreak.Views
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,
@@ -139,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,
@@ -152,6 +122,21 @@ 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>();
+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>();
}
}
}
}
+27
View File
@@ -25,33 +25,60 @@ Ability to set default page for each of the two browser windows.
Rotates screenshots from "Screenshots" folder. If no screenshots are present in the folder, downloads and rotates images from http://bloogum.net/guildwars (link to page is visible when showing images from the website).
Manages build templates. Load/save builds from the guildwars build folder. Ability to dynamically load build templates from the embeded browsers. Opens embeded browser for professions, attributes and skills when selected.
Screen placing/binding. If enabled, places guildwars executable on desired screen at launch.
Shortcut management. Can manage automatically shortcuts to desired folder.
# Examples/Usage
## Settings.
To modify any settings, press the settings button on the titlebar
![Settings button](https://i.imgur.com/0QSTvNF.png)
## Settings categories.
To adjust functionality, choose one of the settings categories
![Settings categories](https://i.imgur.com/LtPDvHY.png)
## Using the embeded browsers.
When in the main view, the browsers open the default/prefferred page. To change the prefferred page, navigate to it using one of the browsers and press the star button. The current loaded page will become the default for the selected browser. Preffered page can also be selected from settings.
![Browser default selection](https://i.imgur.com/nDnyIIL.png)
## Displaying screenshots
To display other images than the ones retrieved from "http://bloogum.net/guildwars", place images in the Screenshots folder, next to the Daybreak.exe executable. If the folder doesn't exist yet, either create it or run the launcher once so that it gets created automatically.
## Account management
To adjust accounts, go into account settings. Clicking on the star next to an account sets it as the current account.
![Account settings](https://i.imgur.com/Pwycwwr.png)
## Guildwars executable management
To adjust executables, go into Guildwars settings. Clicking on the star next to the executable path sets it as the current executable.
![Guildwars settings](https://i.imgur.com/XChX19t.png)
## Multibox/Multilaunch support
To enable multiboxing (multi-launch), go into Experimental settings. Then, switch between executables/accounts and launch them.
![Multibox toggle](https://i.imgur.com/vEFF2pb.png)
## Build management / Template management
To manage builds, go into Settings, Builds. Here you can select/remove builds.
![Builds list view](https://i.imgur.com/OFxnFcZ.png)
## Build editor
Doubleclick on one of the builds to enter the template management view. Here you can click on skills to open the wiki page of the skill. You can adjust professions, attributes and add/remove skills.
![Builds management view](https://i.imgur.com/exKWJb8.png)
## Dynamically load build from browser
To dynamically load a build, while browsing in the browsers from the main page, select and rightclick any text on the page that might be a template. If it is a template, the client will open a context menu with a button called "Load build template". Click on that button to load the build and switch to build template view.
![Dynamically load build](https://i.imgur.com/V5vvgsg.png)
## Screen selection/binding
To launch the guildwars executable to a specific screen, open Settings, Launcher Settings either fill in the "Desired screen id",
![Screen selection](https://i.imgur.com/j6PJnvm.png)
or use the selection view. If you press Test button on screen selection view, it will automatically move the currently running guildwars to the selected screen.
![Screen selection view](https://i.imgur.com/gTeCnM0.png)
## Shortcuts
To manage launcher shortcuts, use the Settings, Launcher Settings menu. If you want a different folder than the desktop, use the "Shortcut folder" setting to change to the desired place. By toggling "Shortcut placed" toggle, a shortcut will be created/deleted.
![Shortcut settings](https://i.imgur.com/AF6LX9B.png)
+16
View File
@@ -0,0 +1,16 @@
Param(
[Parameter(Mandatory=$true)]
[string]$version
) #end param
Write-Output "Deleting pdb file"
Remove-item .\Publish\Daybreak.pdb
$zipPath = "Publish\daybreakv$version.zip"
Write-Output "Compressing binaries to $zipPath"
Compress-Archive .\Publish\* $zipPath -Force
Write-Output "Cleaning up files"
foreach ($file in Get-ChildItem -Path .\Publish -Exclude *.zip -Recurse -ErrorAction SilentlyContinue)
{
Write-Output "Removing $file"
Remove-item $file -Recurse -Force -ErrorAction SilentlyContinue
}
+5
View File
@@ -0,0 +1,5 @@
Write-Host "Retrieving version"
$filepath = Get-ChildItem -Path .\Daybreak -Filter *.version
$version = $filepath.BaseName
Write-Host "Version: $version"
return $version