mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-09-16 13:29:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef4befdc38 | ||
|
|
33f49ddffa | ||
|
|
49aa249b5b | ||
|
|
c387d44890 | ||
|
|
d4d09e7267 | ||
|
|
dd7bf02a97 | ||
|
|
6d0eeb478f | ||
|
|
d4e9de48b1 | ||
|
|
6bd9877925 | ||
|
|
d38df6596f | ||
|
|
b4e31e859c | ||
|
|
1acd07c1be | ||
|
|
b9b25bb098 | ||
|
|
5ea2981dc7 | ||
|
|
4fb96ca3a4 | ||
|
|
6de52f5585 | ||
|
|
6622e41f64 | ||
|
|
8405e4d1e5 | ||
|
|
0edc3f1560 | ||
|
|
c109fe7561 | ||
|
|
b5413dca5c | ||
|
|
6c2d4a1e70 | ||
|
|
be03f1a32d | ||
|
|
3ed1cc453a | ||
|
|
16030063ed |
@@ -0,0 +1,96 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT license.
|
||||
|
||||
# This continuous integration pipeline is triggered anytime a user pushes code to the repo.
|
||||
# This pipeline builds the Wpf project, runs unit tests, then saves the MSIX build artifact.
|
||||
name: Daybreak CD Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
targetplatform: [x64]
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
env:
|
||||
Configuration: Release
|
||||
Solution_Path: Daybreak.sln
|
||||
Test_Project_Path: Daybreak.Tests\Daybreak.Tests.csproj
|
||||
Wpf_Project_Path: Daybreak\Daybreal.csproj
|
||||
Actions_Allow_Unsecure_Commands: true
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get Latest Tag
|
||||
id: getLatestTag
|
||||
uses: WyriHaximus/github-action-get-previous-tag@v1
|
||||
|
||||
- name: Generate changelog
|
||||
id: gen_changelog
|
||||
run: |
|
||||
$changeLog = git log --no-merges --pretty="%h - %s (%an)<br />" ${{ env.LatestReleaseTag }}..HEAD
|
||||
echo "::set-env name=Changelog::$changeLog"
|
||||
env:
|
||||
LatestReleaseTag: ${{steps.getLatestTag.outputs.tag}}
|
||||
|
||||
- name: Print changelog
|
||||
run: |
|
||||
echo "${{ env.Changelog }}"
|
||||
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.202'
|
||||
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.0.1
|
||||
|
||||
- name: Restore project
|
||||
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
|
||||
env:
|
||||
RuntimeIdentifier: win-${{ matrix.targetplatform }}
|
||||
|
||||
- name: Build Daybreak project
|
||||
run: dotnet build Daybreak -c $env:Configuration
|
||||
|
||||
- name: Set version variable
|
||||
run: |
|
||||
$version = .\Scripts\GetBuildVersion.ps1
|
||||
echo "::set-env name=Version::$version"
|
||||
|
||||
- name: Create publish files
|
||||
run: dotnet publish .\Daybreak\Daybreak.csproj -c $env:Configuration -r $env:RuntimeIdentifier -p:PublishReadyToRun=true -p:PublishSingleFile=true --self-contained true -o .\Publish
|
||||
env:
|
||||
RuntimeIdentifier: win-${{ matrix.targetplatform }}
|
||||
|
||||
- name: Pack publish files
|
||||
run: |
|
||||
Write-Host $env
|
||||
.\Scripts\BuildRelease.ps1 -version $env:Version
|
||||
shell: pwsh
|
||||
|
||||
- name: Publish release
|
||||
uses: Xotl/cool-github-releases@v1.1.2
|
||||
with:
|
||||
mode: update
|
||||
tag_name: v${{ env.Version }}
|
||||
release_name: Daybreak v${{ env.Version }}
|
||||
assets: .\Publish\daybreakv${{ env.Version }}.zip
|
||||
github_token: ${{ env.GITHUB_TOKEN }}
|
||||
replace_assets: true
|
||||
body_mrkdwn: ${{ env.Changelog }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT license.
|
||||
|
||||
# This continuous integration pipeline is triggered anytime a user pushes code to the repo.
|
||||
# This pipeline builds the Wpf project, runs unit tests, then saves the MSIX build artifact.
|
||||
name: Daybreak CI Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
targetplatform: [x64]
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
env:
|
||||
Solution_Path: Daybreak.sln
|
||||
Test_Project_Path: Daybreak.Tests\Daybreak.Tests.csproj
|
||||
Wpf_Project_Path: Daybreak\Daybreal.csproj
|
||||
Actions_Allow_Unsecure_Commands: true
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.202'
|
||||
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.0.1
|
||||
|
||||
- name: Execute Unit Tests
|
||||
run: dotnet test $env:Test_Project_Path
|
||||
|
||||
- name: Restore the Wpf application to populate the obj folder
|
||||
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
|
||||
env:
|
||||
Configuration: Debug
|
||||
RuntimeIdentifier: win-${{ matrix.targetplatform }}
|
||||
@@ -9,6 +9,7 @@
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
*.version
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
@@ -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,11 +1,16 @@
|
||||
using Daybreak.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Configuration
|
||||
{
|
||||
public sealed class ApplicationConfiguration
|
||||
{
|
||||
[JsonProperty("SetGuildwarsWindowSizeOnLaunch")]
|
||||
public bool SetGuildwarsWindowSizeOnLaunch { get; set; }
|
||||
[JsonProperty("DesiredGuildwarsScreen")]
|
||||
public int DesiredGuildwarsScreen { get; set; }
|
||||
[JsonProperty("BrowsersEnabled")]
|
||||
public bool BrowsersEnabled { get; set; } = true;
|
||||
[JsonProperty("ToolboxPath")]
|
||||
@@ -26,5 +31,11 @@ namespace Daybreak.Configuration
|
||||
public bool AddressBarReadonly { get; set; } = true;
|
||||
[JsonProperty("ExperimentalFeatures")]
|
||||
public ExperimentalFeatures ExperimentalFeatures { get; set; } = new();
|
||||
[JsonProperty("ShortcutLocation")]
|
||||
public string ShortcutLocation { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
[JsonProperty("PlaceShortcut")]
|
||||
public bool PlaceShortcut { get; set; }
|
||||
[JsonProperty("AutoCheckUpdate")]
|
||||
public bool AutoCheckUpdate { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Newtonsoft.Json;
|
||||
using Daybreak.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Configuration
|
||||
{
|
||||
@@ -10,5 +12,11 @@ namespace Daybreak.Configuration
|
||||
public int ToolboxAutoLaunchDelay { get; set; } = 5000;
|
||||
[JsonProperty("DynamicBuildLoading")]
|
||||
public bool DynamicBuildLoading { get; set; } = true;
|
||||
[JsonProperty("LaunchGuildwarsAsCurrentUser")]
|
||||
public bool LaunchGuildwarsAsCurrentUser { get; set; } = true;
|
||||
[JsonProperty("CanInterceptKeys")]
|
||||
public bool CanInterceptKeys { get; set; }
|
||||
[JsonProperty("Macros")]
|
||||
public List<KeyMacro> Macros { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,11 @@ using Daybreak.Services.Credentials;
|
||||
using Daybreak.Services.IconRetrieve;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.Mutex;
|
||||
using Daybreak.Services.Privilege;
|
||||
using Daybreak.Services.Runtime;
|
||||
using Daybreak.Services.Screens;
|
||||
using Daybreak.Services.Screenshots;
|
||||
using Daybreak.Services.Shortcuts;
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Views;
|
||||
@@ -37,6 +40,9 @@ namespace Daybreak.Configuration
|
||||
serviceProducer.RegisterSingleton<IRuntimeStore, RuntimeStore>();
|
||||
serviceProducer.RegisterSingleton<IBuildTemplateManager, BuildTemplateManager>();
|
||||
serviceProducer.RegisterSingleton<IIconRetriever, IconRetriever>();
|
||||
serviceProducer.RegisterSingleton<IPrivilegeManager, PrivilegeManager>();
|
||||
serviceProducer.RegisterSingleton<IScreenManager, ScreenManager>();
|
||||
serviceProducer.RegisterSingleton<IShortcutManager, ShortcutManager>();
|
||||
}
|
||||
public static void RegisterLifetimeServices(IApplicationLifetimeProducer applicationLifetimeProducer)
|
||||
{
|
||||
@@ -45,6 +51,7 @@ namespace Daybreak.Configuration
|
||||
applicationLifetimeProducer.RegisterService<ILoggingDatabase>();
|
||||
applicationLifetimeProducer.RegisterService<IScreenshotProvider>();
|
||||
applicationLifetimeProducer.RegisterService<IApplicationUpdater>();
|
||||
applicationLifetimeProducer.RegisterService<IShortcutManager>();
|
||||
}
|
||||
public static void RegisterViews(IViewProducer viewProducer)
|
||||
{
|
||||
@@ -60,6 +67,9 @@ namespace Daybreak.Configuration
|
||||
viewProducer.RegisterView<ExecutablesView>();
|
||||
viewProducer.RegisterView<BuildTemplateView>();
|
||||
viewProducer.RegisterView<BuildsListView>();
|
||||
viewProducer.RegisterView<RequestElevationView>();
|
||||
viewProducer.RegisterView<ScreenChoiceView>();
|
||||
viewProducer.RegisterView<VersionManagementView>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,12 @@ 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;
|
||||
@@ -42,51 +34,24 @@ namespace Daybreak.Controls
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<UserControl x:Class="Daybreak.Controls.StaticGlyph"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Viewbox>
|
||||
<Polygon Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Points="209.89999389648438,829.800048828125 279.1999816894531,493.9000244140625 0,493.9000244140625 415.89996337890625,0 346.699951171875,335.8999938964844 625.800048828125,335.8999938964844"></Polygon>
|
||||
</Viewbox>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for StaticGlyph.xaml
|
||||
/// </summary>
|
||||
public partial class StaticGlyph : UserControl
|
||||
{
|
||||
public StaticGlyph()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Daybreak.Utils;
|
||||
using System;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
@@ -39,7 +38,7 @@ namespace Daybreak.Controls
|
||||
}
|
||||
|
||||
nextVisible.Source = imageSource;
|
||||
this.Transition(currentVisible, nextVisible);
|
||||
Transition(currentVisible, nextVisible);
|
||||
}
|
||||
|
||||
private Image CurrentVisible()
|
||||
@@ -64,7 +63,7 @@ namespace Daybreak.Controls
|
||||
return this.Image1;
|
||||
}
|
||||
}
|
||||
private void Transition(Image from, Image to)
|
||||
private static void Transition(Image from, Image to)
|
||||
{
|
||||
to.Visibility = Visibility.Visible;
|
||||
to.Opacity = 0;
|
||||
|
||||
@@ -10,37 +10,21 @@ namespace Daybreak.Controls
|
||||
/// <summary>
|
||||
/// Interaction logic for AccountTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class AccountTemplate : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty UsernameProperty = DependencyPropertyExtensions.Register<AccountTemplate, string>(nameof(Username));
|
||||
public static readonly DependencyProperty CharacterNameProperty = DependencyPropertyExtensions.Register<AccountTemplate, string>(nameof(CharacterName));
|
||||
public static readonly DependencyProperty PasswordProperty = DependencyPropertyExtensions.Register<AccountTemplate, string>(nameof(Password));
|
||||
public static readonly DependencyProperty IsDefaultProperty = DependencyPropertyExtensions.Register<AccountTemplate, bool>(nameof(IsDefault));
|
||||
|
||||
|
||||
public event EventHandler RemoveClicked;
|
||||
public event EventHandler DefaultClicked;
|
||||
|
||||
public string Username
|
||||
{
|
||||
get => this.GetTypedValue<string>(UsernameProperty);
|
||||
set => this.SetValue(UsernameProperty, value);
|
||||
}
|
||||
public string Password
|
||||
{
|
||||
get => this.GetTypedValue<string>(PasswordProperty);
|
||||
set => this.SetValue(PasswordProperty, value);
|
||||
}
|
||||
public string CharacterName
|
||||
{
|
||||
get => this.GetTypedValue<string>(CharacterNameProperty);
|
||||
set => this.SetValue(CharacterNameProperty, value);
|
||||
}
|
||||
public bool IsDefault
|
||||
{
|
||||
get => this.GetTypedValue<bool>(IsDefaultProperty);
|
||||
set => this.SetValue(IsDefaultProperty, value);
|
||||
}
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private string username;
|
||||
[GenerateDependencyProperty]
|
||||
private string password;
|
||||
[GenerateDependencyProperty]
|
||||
private string characterName;
|
||||
[GenerateDependencyProperty]
|
||||
private bool isDefault;
|
||||
|
||||
public AccountTemplate()
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Primary Profession: " FontSize="16" Foreground="White"></TextBlock>
|
||||
<ListView FontSize="16" Foreground="White" Grid.Column="1" BorderThickness="0"
|
||||
<TextBlock Text="Primary Profession: " FontSize="16" Foreground="White" Grid.Column="1"></TextBlock>
|
||||
<ListView FontSize="16" Foreground="White" Grid.Column="2" BorderThickness="0"
|
||||
Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Professions, Mode=OneWay}"
|
||||
SelectedItem="{Binding ElementName=_this, Path=PrimaryProfession, Mode=TwoWay}" Height="30"
|
||||
SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
@@ -43,17 +43,17 @@
|
||||
<behaviors:ScrollIntoView></behaviors:ScrollIntoView>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ListView>
|
||||
<local:HelpButton Grid.Column="2" Foreground="White" Width="20" Height="20" Margin="3"
|
||||
<local:HelpButton Grid.Column="0" Foreground="White" Width="20" Height="20" Margin="3"
|
||||
Clicked="HelpButtonPrimary_Clicked" Cursor="Hand"></local:HelpButton>
|
||||
</Grid>
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Secondary Profession: " FontSize="16" Foreground="White"></TextBlock>
|
||||
<ListView FontSize="16" Foreground="White" Grid.Column="1" BorderThickness="0"
|
||||
<TextBlock Text="Secondary Profession: " FontSize="16" Foreground="White" Grid.Column="1"></TextBlock>
|
||||
<ListView FontSize="16" Foreground="White" Grid.Column="2" BorderThickness="0"
|
||||
Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Professions, Mode=OneWay}"
|
||||
SelectedItem="{Binding ElementName=_this, Path=SecondaryProfession, Mode=TwoWay}" Height="30"
|
||||
SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
@@ -67,7 +67,7 @@
|
||||
<behaviors:ScrollIntoView></behaviors:ScrollIntoView>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ListView>
|
||||
<local:HelpButton Grid.Column="2" Foreground="White" Width="20" Height="20" Margin="3"
|
||||
<local:HelpButton Grid.Column="0" Foreground="White" Width="20" Height="20" Margin="3"
|
||||
Clicked="HelpButtonSecondary_Clicked" Cursor="Hand"></local:HelpButton>
|
||||
</Grid>
|
||||
<Grid Grid.Row="3">
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<UserControl x:Class="Daybreak.Controls.Templates.ScreenTemplate"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Controls.Templates"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<Rectangle Stroke="{Binding ElementName=_this, Path=Highlight, Mode=OneWay}" StrokeThickness="15"></Rectangle>
|
||||
<TextBlock FontSize="168" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Text="{Binding ElementName=_this, Path=ScreenId, Mode=OneWay}"
|
||||
Foreground="{Binding ElementName=_this, Path=Highlight, Mode=OneWay}"></TextBlock>
|
||||
<Rectangle Fill="Transparent" MouseEnter="Rectangle_MouseEnter" MouseLeave="Rectangle_MouseLeave" MouseLeftButtonDown="Rectangle_MouseLeftButtonDown"></Rectangle>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,63 @@
|
||||
using Daybreak.Models;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Daybreak.Controls.Templates
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ScreenTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class ScreenTemplate : UserControl
|
||||
{
|
||||
public event EventHandler<Screen> Clicked;
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private string screenId;
|
||||
[GenerateDependencyProperty]
|
||||
private Brush highlight;
|
||||
|
||||
public ScreenTemplate()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.DataContextChanged += ScreenTemplate_DataContextChanged;
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (e.Property == ForegroundProperty)
|
||||
{
|
||||
this.Highlight = e.NewValue.As<Brush>();
|
||||
}
|
||||
}
|
||||
|
||||
private void ScreenTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is Screen screen)
|
||||
{
|
||||
this.ScreenId = screen.Id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void Rectangle_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
this.Highlight = Brushes.LightSteelBlue;
|
||||
}
|
||||
|
||||
private void Rectangle_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
this.Highlight = this.Foreground;
|
||||
}
|
||||
|
||||
private void Rectangle_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
this.Clicked?.Invoke(this, this.DataContext.As<Screen>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,28 +16,19 @@ namespace Daybreak.Controls
|
||||
/// <summary>
|
||||
/// Interaction logic for SkillTemplate.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class SkillTemplate : UserControl
|
||||
{
|
||||
public readonly static DependencyProperty ImageSourceProperty =
|
||||
DependencyPropertyExtensions.Register<SkillTemplate, ImageSource>(nameof(ImageSource));
|
||||
public readonly static DependencyProperty BorderOpacityProperty =
|
||||
DependencyPropertyExtensions.Register<SkillTemplate, double>(nameof(BorderOpacity), new PropertyMetadata(0d));
|
||||
|
||||
public event EventHandler<RoutedEventArgs> Clicked;
|
||||
public event EventHandler RemoveClicked;
|
||||
|
||||
private readonly IIconRetriever iconRetriever;
|
||||
|
||||
public ImageSource ImageSource
|
||||
{
|
||||
get => this.GetTypedValue<ImageSource>(ImageSourceProperty);
|
||||
set => this.SetValue(ImageSourceProperty, value);
|
||||
}
|
||||
public double BorderOpacity
|
||||
{
|
||||
get => this.GetTypedValue<double>(BorderOpacityProperty);
|
||||
set => this.SetValue(BorderOpacityProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private ImageSource imageSource;
|
||||
[GenerateDependencyProperty]
|
||||
private double borderOpacity;
|
||||
|
||||
public SkillTemplate()
|
||||
{
|
||||
@@ -48,15 +39,22 @@ namespace Daybreak.Controls
|
||||
|
||||
private void SkillTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is Skill skill && skill != Skill.NoSkill)
|
||||
if (e.NewValue is Skill skill)
|
||||
{
|
||||
Task.Run(() => GetImageStream(skill)).ContinueWith((previousTask) =>
|
||||
if (skill != Skill.NoSkill)
|
||||
{
|
||||
this.Dispatcher.Invoke(() =>
|
||||
Task.Run(() => GetImageStream(skill)).ContinueWith((previousTask) =>
|
||||
{
|
||||
this.ImageSource = GetImageSource(previousTask.Result);
|
||||
this.Dispatcher.Invoke(() =>
|
||||
{
|
||||
this.ImageSource = GetImageSource(previousTask.Result);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (this.ImageSource is not null)
|
||||
{
|
||||
this.ImageSource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,19 +9,22 @@
|
||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
|
||||
<Version>0.7.6</Version>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
|
||||
<Version>0.8.9</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.32" />
|
||||
<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="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageReference Include="Slim" Version="1.2.2" />
|
||||
<PackageReference Include="System.Windows.Interactivity.WPF" Version="2.0.20525" />
|
||||
<PackageReference Include="SystemExtensions.NetStandard" Version="1.1.4" />
|
||||
<PackageReference Include="WCL" Version="1.0.2" />
|
||||
<PackageReference Include="WpfExtended" Version="0.2.0" />
|
||||
<PackageReference Include="WpfExtended" Version="0.2.1" />
|
||||
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.1" />
|
||||
<PackageReference Include="WpfScreenHelper" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -62,4 +65,8 @@
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'">
|
||||
<Exec Command="echo.>$(Version).version" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -16,43 +16,46 @@
|
||||
Title="Daybreak"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Window.Resources>
|
||||
<Style x:Key="Window_SettingsButton" TargetType="ToggleButton">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Grid>
|
||||
<Rectangle x:Name="OverlayRect" Fill="{TemplateBinding Background}" Opacity="0"></Rectangle>
|
||||
<Viewbox Margin="5" Stretch="Uniform">
|
||||
<Grid>
|
||||
<Path Stroke="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
<ResourceDictionary>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
|
||||
<Style x:Key="Window_SettingsButton" TargetType="ToggleButton">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToggleButton}">
|
||||
<Grid>
|
||||
<Rectangle x:Name="OverlayRect" Fill="{TemplateBinding Background}" Opacity="0"></Rectangle>
|
||||
<Viewbox Margin="5" Stretch="Uniform">
|
||||
<Grid>
|
||||
<Path Stroke="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Data="m76.89,45.018c0,-1.587 -0.151,-3.138 -0.383,-4.66l11.227,-6.48l-11.639,-20.136l-11.616,6.47c-2.421,-1.931 -5.479,-3.506 -8.479,-4.664l0,-11.548l-22,0l0,11.548c-4,1.112 -5.649,2.617 -8.005,4.448l-11.251,-6.604l-11.914,20.006l11.12,6.604c-0.262,1.643 -0.45,3.308 -0.45,5.016c0,1.587 0.153,3.136 0.376,4.658l-11.228,6.484l11.634,20.135l11.424,-6.472c2.431,1.933 4.294,3.506 8.294,4.665l0,12.512l22,0l0,-12.512c3,-1.115 5.839,-2.618 8.19,-4.449l11.348,6.604l11.96,-20.006l-11.095,-6.603c0.258,-1.64 0.487,-3.304 0.487,-5.016zm-31.704,14.137c-7.799,0 -14.138,-6.332 -14.138,-14.138c0,-7.808 6.339,-14.136 14.138,-14.136c7.81,0 14.14,6.328 14.14,14.136c-0.001,7.806 -6.331,14.138 -14.14,14.138z"></Path>
|
||||
<Path Fill="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
<Path Fill="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Data="m56.5,89l-22,0c-1.381,0 -2.5,-1.119 -2.5,-2.5l0,-10.199c-2.612,-0.955 -4.464,-2.126 -6.274,-3.49l-10.229,5.671c-1.192,0.661 -2.694,0.244 -3.377,-0.936l-11.633,-20.135c-0.331,-0.574 -0.422,-1.257 -0.25,-1.897s0.591,-1.188 1.165,-1.519l9.794,-5.654c-0.121,-1.164 -0.181,-2.262 -0.181,-3.323c0,-1.317 0.112,-2.592 0.254,-3.721l-9.656,-5.75c-0.57,-0.34 -0.982,-0.893 -1.145,-1.537c-0.163,-0.644 -0.063,-1.326 0.279,-1.896l11.977,-20.006c0.702,-1.173 2.218,-1.564 3.399,-0.878l9.927,5.762c1.618,-1.213 3.362,-2.346 5.949,-3.273l0,-10.219c0.001,-1.381 1.12,-2.5 2.501,-2.5l22,0c1.381,0 2.5,1.119 2.5,2.5l0,10.377c2.124,0.938 4.104,2.059 5.919,3.35l9.939,-5.657c1.19,-0.677 2.713,-0.269 3.4,0.922l11.639,20.135c0.332,0.574 0.423,1.257 0.251,1.897s-0.591,1.188 -1.165,1.519l-9.781,5.646c0.126,1.165 0.188,2.266 0.188,3.329c0,1.327 -0.108,2.597 -0.246,3.717l9.692,5.752c0.571,0.339 0.983,0.891 1.147,1.533c0.163,0.644 0.065,1.325 -0.274,1.896l-11.897,20.005c-0.703,1.183 -2.234,1.573 -3.416,0.876l-9.798,-5.765c-1.782,1.25 -3.634,2.29 -5.598,3.144l0,10.324c0,1.381 -1.119,2.5 -2.5,2.5zm-19.5,-5l17,0l0,-9.512c0,-1.045 0.649,-1.979 1.629,-2.344c2.624,-0.975 5.004,-2.309 7.275,-4.078c0.809,-0.629 1.921,-0.702 2.804,-0.182l9.078,5.341l9.347,-15.716l-8.974,-5.325c-0.88,-0.522 -1.354,-1.528 -1.194,-2.538c0.198,-1.259 0.425,-2.923 0.425,-4.629c0,-1.314 -0.116,-2.716 -0.354,-4.284c-0.154,-1.018 0.33,-2.026 1.222,-2.541l9.061,-5.23l-9.145,-15.818l-9.208,5.241c-0.888,0.505 -1.995,0.42 -2.795,-0.219c-2.199,-1.754 -4.747,-3.196 -7.57,-4.286c-0.964,-0.372 -1.6,-1.299 -1.6,-2.332l0,-9.548l-17.001,0l0,9.548c0,1.123 -0.748,2.107 -1.83,2.408c-3.47,0.965 -5.073,2.212 -7.293,3.938l-0.098,0.076c-0.804,0.625 -1.909,0.7 -2.789,0.188l-9.243,-5.365l-9.423,15.739l8.937,5.321c0.879,0.524 1.351,1.531 1.189,2.542c-0.202,1.27 -0.435,2.943 -0.435,4.622c0,1.318 0.112,2.724 0.342,4.297c0.148,1.013 -0.337,2.015 -1.224,2.526l-9.065,5.234l9.151,15.842l9.525,-5.28c0.883,-0.49 1.976,-0.4 2.768,0.229c2.384,1.896 4.207,3.214 7.684,4.221c1.069,0.31 1.804,1.29 1.804,2.402l0,9.512zm8.186,-22.345c-9.174,0 -16.638,-7.464 -16.638,-16.638c0,-9.173 7.464,-16.636 16.638,-16.636c9.175,0 16.64,7.463 16.64,16.636c-0.001,9.174 -7.466,16.638 -16.64,16.638zm0,-28.273c-6.417,0 -11.638,5.22 -11.638,11.636c0,6.417 5.221,11.638 11.638,11.638c6.418,0 11.64,-5.221 11.64,-11.638c-0.001,-6.416 -5.222,-11.636 -11.64,-11.636z"></Path>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="Opacity" TargetName="OverlayRect" Value="0.5"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="False"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="Opacity" TargetName="OverlayRect" Value="0"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="Opacity" TargetName="OverlayRect" Value="0.5"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="False"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter Property="Opacity" TargetName="OverlayRect" Value="0"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid Background="Black">
|
||||
<Grid.RowDefinitions>
|
||||
@@ -82,9 +85,20 @@
|
||||
Text="{Binding ElementName=_this, Path=CreditText, Mode=OneWay}" VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right" Margin="0, 0, 20, 30" FontSize="22" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Clicked="CreditTextBox_MouseLeftButtonDown" Cursor="Hand"></controls:OpaqueButton>
|
||||
<TextBox Grid.Row="1" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Opacity="0.6"
|
||||
Text="{Binding ElementName=_this, Path=CurrentVersionText, Mode=OneWay}" IsReadOnly="True" VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right" Margin="0, 0, 20, 10" FontSize="10" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></TextBox>
|
||||
<WrapPanel Grid.Row="1" Margin="0, 0, 20, 10" VerticalAlignment="Bottom" HorizontalAlignment="Right">
|
||||
<controls:OpaqueButton Grid.Row="1" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Opacity="0.6"
|
||||
Text="{Binding ElementName=_this, Path=CurrentVersionText, Mode=OneWay}" FontSize="10"
|
||||
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Clicked="VersionText_Clicked"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Grid.Row="1" Background="Transparent" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Opacity="0.6" Text=" - " FontSize="10" BorderBrush="Transparent" BorderThickness="0"
|
||||
Visibility="{Binding ElementName=_this, Path=IsRunningAsAdmin, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Clicked="VersionText_Clicked"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Grid.Row="1" Background="Transparent" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
Opacity="0.6" Text="[Admin]" FontSize="10" BorderBrush="Transparent" BorderThickness="0"
|
||||
Visibility="{Binding ElementName=_this, Path=IsRunningAsAdmin, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Clicked="VersionText_Clicked"></controls:OpaqueButton>
|
||||
</WrapPanel>
|
||||
<Grid x:Name="Container" Grid.Row="1">
|
||||
</Grid>
|
||||
<wcl:Border OnResize="Border_OnResize" Grid.RowSpan="2" Active="True"></wcl:Border>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Daybreak.Services.Bloogum;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Privilege;
|
||||
using Daybreak.Services.Runtime;
|
||||
using Daybreak.Services.Screenshots;
|
||||
using Daybreak.Services.Updater;
|
||||
@@ -21,41 +23,52 @@ namespace Daybreak.Launch
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public static readonly DependencyProperty CreditTextProperty = DependencyPropertyExtensions.Register<MainWindow, string>(nameof(CreditText));
|
||||
public static readonly DependencyProperty CurrentVersionTextProperty = DependencyPropertyExtensions.Register<MainWindow, string>(nameof(CurrentVersionText));
|
||||
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IScreenshotProvider screenshotProvider;
|
||||
private readonly IBloogumClient bloogumClient;
|
||||
private readonly IApplicationUpdater applicationUpdater;
|
||||
private readonly IPrivilegeManager privilegeManager;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly CancellationTokenSource cancellationToken = new();
|
||||
|
||||
public string CreditText
|
||||
{
|
||||
get => this.GetTypedValue<string>(CreditTextProperty);
|
||||
set => this.SetValue(CreditTextProperty, value);
|
||||
}
|
||||
private bool canCheckUpdate = false;
|
||||
|
||||
public string CurrentVersionText
|
||||
{
|
||||
get => this.GetTypedValue<string>(CurrentVersionTextProperty);
|
||||
set => this.SetValue(CurrentVersionTextProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private string creditText;
|
||||
[GenerateDependencyProperty]
|
||||
private string currentVersionText;
|
||||
[GenerateDependencyProperty]
|
||||
private bool isRunningAsAdmin;
|
||||
|
||||
public MainWindow(
|
||||
IViewManager viewManager,
|
||||
IScreenshotProvider screenshotProvider,
|
||||
IBloogumClient bloogumClient,
|
||||
IApplicationUpdater applicationUpdater)
|
||||
IApplicationUpdater applicationUpdater,
|
||||
IPrivilegeManager privilegeManager,
|
||||
IConfigurationManager configurationManager)
|
||||
{
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.screenshotProvider = screenshotProvider.ThrowIfNull(nameof(screenshotProvider));
|
||||
this.bloogumClient = bloogumClient.ThrowIfNull(nameof(bloogumClient));
|
||||
this.applicationUpdater = applicationUpdater.ThrowIfNull(nameof(applicationUpdater));
|
||||
this.privilegeManager = privilegeManager.ThrowIfNull(nameof(privilegeManager));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.InitializeComponent();
|
||||
this.CurrentVersionText = this.applicationUpdater.CurrentVersion;
|
||||
this.LoadConfiguration();
|
||||
this.configurationManager.ConfigurationChanged += (s, e) => this.LoadConfiguration();
|
||||
this.CurrentVersionText = this.applicationUpdater.CurrentVersion.ToString();
|
||||
this.IsRunningAsAdmin = this.privilegeManager.AdminPrivileges;
|
||||
}
|
||||
|
||||
private void LoadConfiguration()
|
||||
{
|
||||
var configuration = this.configurationManager.GetConfiguration();
|
||||
this.canCheckUpdate = configuration.AutoCheckUpdate;
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
@@ -140,6 +153,11 @@ namespace Daybreak.Launch
|
||||
}
|
||||
}
|
||||
|
||||
private void VersionText_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<VersionManagementView>();
|
||||
}
|
||||
|
||||
private void SetImage(ImageSource imageSource)
|
||||
{
|
||||
this.ImageViewer.ShowImage(imageSource);
|
||||
@@ -160,7 +178,7 @@ namespace Daybreak.Launch
|
||||
|
||||
private async void CheckForUpdates()
|
||||
{
|
||||
var updateAvailable = await this.applicationUpdater.UpdateAvailable().ConfigureAwait(true);
|
||||
var updateAvailable = this.canCheckUpdate is true && await this.applicationUpdater.UpdateAvailable().ConfigureAwait(true);
|
||||
if (updateAvailable)
|
||||
{
|
||||
this.viewManager.ShowView<AskUpdateView>();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public sealed class ElevationRequest
|
||||
{
|
||||
public object DataContext { get; set; }
|
||||
public Type View { get; set; }
|
||||
public string MessageToUser { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Daybreak.Models.Github
|
||||
{
|
||||
public sealed class GithubRefTag
|
||||
{
|
||||
[JsonProperty("ref")]
|
||||
public string Ref;
|
||||
[JsonProperty("node_id")]
|
||||
public string NodeId;
|
||||
[JsonProperty("url")]
|
||||
public string Url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public sealed class KeyMacro
|
||||
{
|
||||
public List<Keys> Keys { get; set; }
|
||||
public Keys TargetKey { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public sealed class KeyboardHookEventArgs
|
||||
{
|
||||
public bool Handled { get; set; }
|
||||
public KeyboardState KeyboardState { get; }
|
||||
public KeyboardInput KeyboardInput { get; }
|
||||
|
||||
public KeyboardHookEventArgs(
|
||||
KeyboardState keyboardState,
|
||||
KeyboardInput keyboardInput)
|
||||
{
|
||||
this.KeyboardState = keyboardState;
|
||||
this.KeyboardInput = keyboardInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KeyboardInput
|
||||
{
|
||||
/// <summary>
|
||||
/// A virtual-key code. The code must be a value in the range 1 to 254.
|
||||
/// </summary>
|
||||
public int VirtualCode;
|
||||
|
||||
// EDT: added a conversion from VirtualCode to Keys.
|
||||
/// <summary>
|
||||
/// The VirtualCode converted to typeof(Keys) for higher usability.
|
||||
/// </summary>
|
||||
public Keys Key { get { return (Keys)VirtualCode; } }
|
||||
|
||||
/// <summary>
|
||||
/// A hardware scan code for the key.
|
||||
/// </summary>
|
||||
public int HardwareScanCode;
|
||||
|
||||
/// <summary>
|
||||
/// The extended-key flag, event-injected Flags, context code, and transition-state flag. This member is specified as follows. An application can use the following values to test the keystroke Flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected from a process running at lower integrity level.
|
||||
/// </summary>
|
||||
public int Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The time stamp stamp for this message, equivalent to what GetMessageTime would return for this message.
|
||||
/// </summary>
|
||||
public int TimeStamp;
|
||||
|
||||
/// <summary>
|
||||
/// Additional information associated with the message.
|
||||
/// </summary>
|
||||
public IntPtr AdditionalInformation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public enum KeyboardState
|
||||
{
|
||||
KeyDown = 0x0100,
|
||||
KeyUp = 0x0101,
|
||||
SysKeyDown = 0x0104,
|
||||
SysKeyUp = 0x0105
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace Daybreak.Models
|
||||
{
|
||||
public sealed class Screen
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Rect Size { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ namespace Daybreak.Models
|
||||
public sealed class UpdateStatus : INotifyPropertyChanged
|
||||
{
|
||||
public static readonly UpdateStep StartingStep = new("Starting");
|
||||
public static readonly UpdateStep InitializingDownload = new("Initializing download");
|
||||
public static readonly UpdateStep CheckingLatestVersion = new("Checking latest version");
|
||||
public static UpdateStep Downloading(double progress) => new DownloadUpdateStep("Downloading", progress);
|
||||
public static readonly UpdateStep DownloadFinished = new("Download finished. Application will restart in order to apply the update.");
|
||||
public static readonly UpdateStep DownloadFinished = new("Download finished. Application will restart in order to apply the update");
|
||||
public static readonly UpdateStep FailedDownload = new("Update failed. Please check logs for details");
|
||||
|
||||
private UpdateStep currentStep = StartingStep;
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
|
||||
namespace Daybreak.Models.Versioning
|
||||
{
|
||||
public sealed class Version
|
||||
{
|
||||
private List<VersionToken> parts = new();
|
||||
|
||||
public bool HasPrefix { get; private set; }
|
||||
public IEnumerable<VersionToken> VersionTokens { get => this.parts; }
|
||||
public string VersionString { get => this.ToString(); }
|
||||
|
||||
private Version()
|
||||
{
|
||||
}
|
||||
|
||||
public Version(string version)
|
||||
{
|
||||
if (TryParseParts(version, out var parts, out var hasPrefix) is false)
|
||||
{
|
||||
throw new ArgumentException($"Provided argument is not valid: {version}");
|
||||
}
|
||||
|
||||
this.parts = parts;
|
||||
this.HasPrefix = hasPrefix;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var version = string.Join('.', parts.OfType<VersionNumberToken>());
|
||||
if (parts.Last() is VersionStringToken)
|
||||
{
|
||||
version += "-" + parts.Last();
|
||||
}
|
||||
|
||||
if (this.HasPrefix)
|
||||
{
|
||||
version = 'v' + version;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
public static Version Parse(string version)
|
||||
{
|
||||
if (TryParse(version, out var parsedVersion) is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to parse version: {version}");
|
||||
}
|
||||
|
||||
return parsedVersion;
|
||||
}
|
||||
|
||||
public static bool TryParse(string version, out Version parsedVersion)
|
||||
{
|
||||
if (TryParseParts(version, out var parts, out var hasPrefix) is false)
|
||||
{
|
||||
parsedVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = parts.Count - 1; i >= 1; i--)
|
||||
{
|
||||
if (parts[i] is VersionNumberToken && parts[i].ToString() == "0")
|
||||
{
|
||||
parts.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
parsedVersion = new Version
|
||||
{
|
||||
parts = parts,
|
||||
HasPrefix = hasPrefix
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseParts(string version, out List<VersionToken> parts, out bool hasPrefix)
|
||||
{
|
||||
hasPrefix = false;
|
||||
parts = null;
|
||||
if (version.IsNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version.StartsWith('.') || version.EndsWith('.'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var tokens = version.Trim().Split('.');
|
||||
if (tokens.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tokens[0].StartsWith('v'))
|
||||
{
|
||||
hasPrefix = true;
|
||||
tokens[0] = tokens[0][1..];
|
||||
}
|
||||
|
||||
parts = new List<VersionToken>();
|
||||
for(int i = 0; i < tokens.Length - 1; i++)
|
||||
{
|
||||
var token = tokens[i];
|
||||
if (token.IsNullOrWhiteSpace() || token.All(c => char.IsDigit(c)) is false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add(VersionToken.Parse(token));
|
||||
}
|
||||
|
||||
var lastToken = tokens[^1];
|
||||
if (lastToken.Contains('-'))
|
||||
{
|
||||
var lastTokenParts = lastToken.Split('-');
|
||||
if (lastTokenParts.Length > 2 || lastTokenParts.Length < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lastTokenParts[0].IsNullOrWhiteSpace() || lastTokenParts[0].All(c => char.IsDigit(c)) is false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add(VersionToken.Parse(lastTokenParts[0]));
|
||||
if (lastTokenParts[1].IsNullOrWhiteSpace() || lastTokenParts[1].All(c => char.IsLetter(c)) is false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add(VersionToken.Parse(lastTokenParts[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lastToken.IsNullOrWhiteSpace() || lastToken.Any(c => char.IsDigit(c) is false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parts.Add(VersionToken.Parse(lastToken));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Daybreak.Models.Versioning
|
||||
{
|
||||
public sealed class VersionNumberToken : VersionToken
|
||||
{
|
||||
public int Number { get; }
|
||||
internal VersionNumberToken(int number)
|
||||
{
|
||||
this.Number = number;
|
||||
}
|
||||
|
||||
protected override string Stringify()
|
||||
{
|
||||
return this.Number.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Daybreak.Models.Versioning
|
||||
{
|
||||
public sealed class VersionStringToken : VersionToken
|
||||
{
|
||||
public string Token { get; }
|
||||
internal VersionStringToken(string token)
|
||||
{
|
||||
this.Token = token;
|
||||
}
|
||||
|
||||
protected override string Stringify()
|
||||
{
|
||||
return this.Token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Daybreak.Models.Versioning
|
||||
{
|
||||
public abstract class VersionToken
|
||||
{
|
||||
public static VersionToken Parse(string token)
|
||||
{
|
||||
if (token.All(c => char.IsDigit(c)))
|
||||
{
|
||||
var number = int.Parse(token);
|
||||
return new VersionNumberToken(number);
|
||||
}
|
||||
|
||||
if (token.All(c => char.IsLetter(c)))
|
||||
{
|
||||
return new VersionStringToken(token);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Failed to parse version token {token}");
|
||||
}
|
||||
|
||||
protected abstract string Stringify();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Stringify();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@ 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.Win32;
|
||||
using Pepa.Wpf.Utilities;
|
||||
using System;
|
||||
@@ -15,11 +17,13 @@ using System.Linq;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace Daybreak.Services.ApplicationLauncher
|
||||
{
|
||||
public class ApplicationLauncher : IApplicationLauncher
|
||||
{
|
||||
private const int MaxRetries = 10;
|
||||
private const string TexModProcessName = "TexMod";
|
||||
private const string UModProcessName = "uMod";
|
||||
private const string ToolboxProcessName = "GWToolbox";
|
||||
@@ -30,6 +34,7 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
private readonly ICredentialManager credentialManager;
|
||||
private readonly IMutexHandler mutexHandler;
|
||||
private readonly ILogger logger;
|
||||
private readonly IPrivilegeManager privilegeManager;
|
||||
|
||||
public bool IsTexmodRunning => TexModProcessDetected();
|
||||
public bool IsGuildwarsRunning => this.GuildwarsProcessDetected();
|
||||
@@ -39,32 +44,41 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
IConfigurationManager configurationManager,
|
||||
ICredentialManager credentialManager,
|
||||
IMutexHandler mutexHandler,
|
||||
ILogger logger)
|
||||
ILogger logger,
|
||||
IPrivilegeManager privilegeManager)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.mutexHandler = mutexHandler.ThrowIfNull(nameof(mutexHandler));
|
||||
this.credentialManager = credentialManager.ThrowIfNull(nameof(credentialManager));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.privilegeManager = privilegeManager.ThrowIfNull(nameof(privilegeManager));
|
||||
}
|
||||
|
||||
public async Task LaunchGuildwars()
|
||||
public async Task<bool> LaunchGuildwars()
|
||||
{
|
||||
var configuration = this.configurationManager.GetConfiguration();
|
||||
var auth = await this.credentialManager.GetDefaultCredentials().ConfigureAwait(false);
|
||||
auth.Do(
|
||||
onSome: (credentials) =>
|
||||
return await auth.Switch(
|
||||
onSome: async (credentials) =>
|
||||
{
|
||||
if (configuration.ExperimentalFeatures.MultiLaunchSupport is true)
|
||||
{
|
||||
if (this.privilegeManager.AdminPrivileges is false)
|
||||
{
|
||||
this.privilegeManager.RequestAdminPrivileges<MainView>("You need administrator rights in order to start using multi-launch");
|
||||
return false;
|
||||
}
|
||||
|
||||
ClearGwLocks();
|
||||
}
|
||||
|
||||
LaunchGuildwarsProcess(credentials.Username, credentials.Password, credentials.CharacterName);
|
||||
return await LaunchGuildwarsProcess(credentials.Username, credentials.Password, credentials.CharacterName);
|
||||
},
|
||||
onNone: () =>
|
||||
{
|
||||
throw new CredentialsNotFoundException($"No credentials available");
|
||||
});
|
||||
})
|
||||
.ExtractValue();
|
||||
}
|
||||
|
||||
public Task LaunchGuildwarsToolbox()
|
||||
@@ -103,7 +117,34 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
});
|
||||
}
|
||||
|
||||
private void LaunchGuildwarsProcess(string email, Models.SecureString password, string character)
|
||||
public void RestartDaybreakAsAdmin()
|
||||
{
|
||||
this.logger.LogInformation("Restarting daybreak with admin rights");
|
||||
var processName = Process.GetCurrentProcess()?.MainModule?.FileName;
|
||||
if (processName.IsNullOrWhiteSpace() || File.Exists(processName) is false)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to find executable. Aborting restart");
|
||||
}
|
||||
|
||||
var process = new Process()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
Verb = "runas",
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
UseShellExecute = true,
|
||||
FileName = processName
|
||||
}
|
||||
};
|
||||
if (process.Start() is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to start {processName} as admin");
|
||||
}
|
||||
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
private async Task<bool> LaunchGuildwarsProcess(string email, Models.SecureString password, string character)
|
||||
{
|
||||
var executable = this.configurationManager.GetConfiguration().GuildwarsPaths.Where(path => path.Default).FirstOrDefault();
|
||||
if (executable is null)
|
||||
@@ -129,10 +170,54 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
args.Add(character);
|
||||
}
|
||||
|
||||
if (Process.Start(executable.Path, args) is null)
|
||||
var identity = this.configurationManager.GetConfiguration().ExperimentalFeatures.LaunchGuildwarsAsCurrentUser ?
|
||||
System.Security.Principal.WindowsIdentity.GetCurrent().Name :
|
||||
System.Security.Principal.WindowsIdentity.GetAnonymous().Name;
|
||||
this.logger.LogInformation($"Launching guildwars as [{identity}] identity");
|
||||
var process = new Process()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
Arguments = string.Join(" ", args),
|
||||
FileName = executable.Path
|
||||
}
|
||||
};
|
||||
if (process.Start() is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to launch {executable}");
|
||||
}
|
||||
|
||||
var retries = 0;
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
retries++;
|
||||
var gwProcess = Process.GetProcessesByName("gw").FirstOrDefault();
|
||||
if (gwProcess is null && retries < MaxRetries)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (gwProcess is null && retries >= MaxRetries)
|
||||
{
|
||||
throw new InvalidOperationException("Newly launched gw process not detected");
|
||||
}
|
||||
|
||||
if (gwProcess.MainWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int titleLength = NativeMethods.GetWindowTextLength(gwProcess.MainWindowHandle);
|
||||
var titleBuffer = new StringBuilder(titleLength);
|
||||
var readCount = NativeMethods.GetWindowText(gwProcess.MainWindowHandle, titleBuffer, titleLength + 1);
|
||||
var title = titleBuffer.ToString();
|
||||
if (title != "Guild Wars")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool GuildwarsProcessDetected()
|
||||
|
||||
@@ -7,8 +7,9 @@ namespace Daybreak.Services.ApplicationLauncher
|
||||
bool IsGuildwarsRunning { get; }
|
||||
bool IsToolboxRunning { get; }
|
||||
bool IsTexmodRunning { get; }
|
||||
Task LaunchGuildwars();
|
||||
Task<bool> LaunchGuildwars();
|
||||
Task LaunchGuildwarsToolbox();
|
||||
Task LaunchTexmod();
|
||||
void RestartDaybreakAsAdmin();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Daybreak.Configuration;
|
||||
using Daybreak.Exceptions;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using System;
|
||||
@@ -15,6 +14,8 @@ namespace Daybreak.Services.Configuration
|
||||
private ApplicationConfiguration applicationConfiguration;
|
||||
private readonly ILogger logger;
|
||||
|
||||
public event EventHandler ConfigurationChanged;
|
||||
|
||||
public ConfigurationManager(ILogger logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,117 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
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 logger;
|
||||
private IntPtr windowsHookHandle;
|
||||
private IntPtr user32LibraryHandle;
|
||||
private NativeMethods.HookProc hookProc;
|
||||
|
||||
public event EventHandler<KeyboardHookEventArgs> KeyboardPressed;
|
||||
|
||||
public KeyboardHookService(
|
||||
ILogger 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,118 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.KeyboardHook;
|
||||
using Daybreak.Services.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 ILogger logger;
|
||||
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(
|
||||
ILogger logger,
|
||||
IKeyboardHookService keyboardHookService,
|
||||
IConfigurationManager configurationManager)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
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,4 +1,5 @@
|
||||
using System.Windows.Controls;
|
||||
using System;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Services.ViewManagement
|
||||
{
|
||||
@@ -11,5 +12,9 @@ namespace Daybreak.Services.ViewManagement
|
||||
|
||||
void ShowView<T>(object dataContext)
|
||||
where T : UserControl;
|
||||
|
||||
void ShowView(Type type);
|
||||
|
||||
void ShowView(Type type, object dataContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Slim;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Services.ViewManagement
|
||||
@@ -32,17 +33,33 @@ namespace Daybreak.Services.ViewManagement
|
||||
|
||||
public void ShowView<T>() where T : UserControl
|
||||
{
|
||||
var view = this.serviceManager.GetService<T>();
|
||||
this.container.Children.Clear();
|
||||
this.container.Children.Add(view);
|
||||
this.ShowViewInner(typeof(T), null);
|
||||
}
|
||||
|
||||
public void ShowView<T>(object dataContext) where T : UserControl
|
||||
{
|
||||
var view = this.serviceManager.GetService<T>();
|
||||
this.container.Children.Clear();
|
||||
this.container.Children.Add(view);
|
||||
view.DataContext = dataContext;
|
||||
this.ShowViewInner(typeof(T), dataContext);
|
||||
}
|
||||
|
||||
public void ShowView(Type type)
|
||||
{
|
||||
this.ShowViewInner(type, null);
|
||||
}
|
||||
|
||||
public void ShowView(Type type, object dataContext)
|
||||
{
|
||||
this.ShowViewInner(type, dataContext);
|
||||
}
|
||||
|
||||
private void ShowViewInner(Type viewType, object dataContext)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var view = this.serviceManager.GetService(viewType).As<UserControl>();
|
||||
this.container.Children.Clear();
|
||||
this.container.Children.Add(view);
|
||||
view.DataContext = dataContext;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Services.Privilege
|
||||
{
|
||||
public interface IPrivilegeManager
|
||||
{
|
||||
bool AdminPrivileges { get; }
|
||||
|
||||
void RequestAdminPrivileges<TCancelView>(string messageToUser, object dataContextOfCancelView = null)
|
||||
where TCancelView : UserControl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Utils;
|
||||
using Daybreak.Views;
|
||||
using System.Extensions;
|
||||
using System.Security.Principal;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Services.Privilege
|
||||
{
|
||||
public sealed class PrivilegeManager : IPrivilegeManager
|
||||
{
|
||||
public bool AdminPrivileges => new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly ILogger logger;
|
||||
|
||||
public PrivilegeManager(
|
||||
IViewManager viewManager,
|
||||
ILogger logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
}
|
||||
|
||||
public void RequestAdminPrivileges<TCancelView>(string messageToUser, object dataContextOfCancelView = null)
|
||||
where TCancelView : UserControl
|
||||
{
|
||||
this.logger.LogInformation("Requesting admin privileges");
|
||||
this.viewManager.ShowView<RequestElevationView>(new ElevationRequest { View = typeof(TCancelView), DataContext = dataContextOfCancelView, MessageToUser = messageToUser });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Daybreak.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Services.Screens
|
||||
{
|
||||
public interface IScreenManager
|
||||
{
|
||||
IEnumerable<Screen> Screens { get; }
|
||||
void MoveGuildwarsToScreen(Screen screen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Utils;
|
||||
using Pepa.Wpf.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
|
||||
namespace Daybreak.Services.Screens
|
||||
{
|
||||
public sealed class ScreenManager : IScreenManager
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
|
||||
public IEnumerable<Screen> Screens { get; } = WpfScreenHelper.Screen.AllScreens
|
||||
.Select((screen, index) => new Screen { Id = index, Size = screen.Bounds });
|
||||
|
||||
public ScreenManager(
|
||||
ILogger logger)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
}
|
||||
|
||||
public void MoveGuildwarsToScreen(Screen screen)
|
||||
{
|
||||
this.logger.LogInformation($"Attempting to move guildwars to screen {screen.Id}");
|
||||
var hwnd = GetMainWindowHandle();
|
||||
NativeMethods.SetWindowPos(hwnd, NativeMethods.HWND_TOP, screen.Size.Left.ToInt(), screen.Size.Top.ToInt(), screen.Size.Width.ToInt(), screen.Size.Height.ToInt(), NativeMethods.SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
private static IntPtr GetMainWindowHandle()
|
||||
{
|
||||
var process = Process.GetProcessesByName("gw").FirstOrDefault();
|
||||
return process is not null ? process.MainWindowHandle : throw new InvalidOperationException("Could not find guildwars process");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Daybreak.Services.ApplicationLifetime;
|
||||
|
||||
namespace Daybreak.Services.Shortcuts
|
||||
{
|
||||
public interface IShortcutManager : IApplicationLifetimeService
|
||||
{
|
||||
bool ShortcutEnabled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using Daybreak.Services.Configuration;
|
||||
using ShellLink;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.IO;
|
||||
|
||||
namespace Daybreak.Services.Shortcuts
|
||||
{
|
||||
public sealed class ShortcutManager : IShortcutManager
|
||||
{
|
||||
private const string ShortcutName = "Daybreak.lnk";
|
||||
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
|
||||
public bool ShortcutEnabled {
|
||||
get => this.ShortcutExists();
|
||||
set
|
||||
{
|
||||
if (value is true)
|
||||
{
|
||||
this.CreateShortcut();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.RemoveShortcut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ShortcutManager(IConfigurationManager configurationManager)
|
||||
{
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.configurationManager.ConfigurationChanged += (_, _) => this.LoadConfiguration();
|
||||
this.LoadConfiguration();
|
||||
}
|
||||
|
||||
private void LoadConfiguration()
|
||||
{
|
||||
var shortcutEnabled = this.configurationManager.GetConfiguration().PlaceShortcut;
|
||||
if (shortcutEnabled && this.ShortcutEnabled is false)
|
||||
{
|
||||
this.ShortcutEnabled = true;
|
||||
}
|
||||
else if (shortcutEnabled is false && this.ShortcutEnabled is true)
|
||||
{
|
||||
this.ShortcutEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShortcutExists()
|
||||
{
|
||||
var shortcutFolder = this.configurationManager.GetConfiguration().ShortcutLocation;
|
||||
var shortcutPath = $"{shortcutFolder}\\{ShortcutName}";
|
||||
if (File.Exists(shortcutPath))
|
||||
{
|
||||
var shortcut = Shortcut.ReadFromFile(shortcutPath);
|
||||
var currentExecutable = Process.GetCurrentProcess()?.MainModule?.FileName;
|
||||
if (shortcut.ExtraData?.EnvironmentVariableDataBlock?.TargetAnsi?.Equals(currentExecutable) is true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CreateShortcut()
|
||||
{
|
||||
if (this.ShortcutExists())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var shortcutFolder = this.configurationManager.GetConfiguration().ShortcutLocation;
|
||||
var shortcutPath = $"{shortcutFolder}\\{ShortcutName}";
|
||||
var currentExecutable = Process.GetCurrentProcess()?.MainModule?.FileName;
|
||||
var shortcut = Shortcut.CreateShortcut(currentExecutable);
|
||||
shortcut.StringData = new ShellLink.Structures.StringData
|
||||
{
|
||||
WorkingDir = Path.GetDirectoryName(currentExecutable),
|
||||
RelativePath = "Daybreak.exe"
|
||||
};
|
||||
shortcut.WriteToFile(shortcutPath);
|
||||
}
|
||||
|
||||
private void RemoveShortcut()
|
||||
{
|
||||
if (this.ShortcutExists() is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var shortcutFolder = this.configurationManager.GetConfiguration().ShortcutLocation;
|
||||
var shortcutPath = $"{shortcutFolder}\\{ShortcutName}";
|
||||
File.Delete(shortcutPath);
|
||||
}
|
||||
|
||||
public void OnStartup()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnClosing()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Exceptions;
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Models.Github;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.Runtime;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
@@ -16,11 +18,13 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Version = Daybreak.Models.Versioning.Version;
|
||||
|
||||
namespace Daybreak.Services.Updater
|
||||
{
|
||||
public sealed class ApplicationUpdater : IApplicationUpdater
|
||||
{
|
||||
private const string LaunchActionName = "Launch_Daybreak";
|
||||
private const string UpdateDesiredKey = "UpdateDesired";
|
||||
private const string ExecutionPolicyKey = "ExecutionPolicy";
|
||||
private const string UpdatedKey = "Updating";
|
||||
@@ -32,13 +36,22 @@ namespace Daybreak.Services.Updater
|
||||
private const string OutputPathTag = "{OUTPUTPATH}";
|
||||
private const string ExecutionPolicyTag = "{EXECUTIONPOLICY}";
|
||||
private const string ProcessIdTag = "{PROCESSID}";
|
||||
private const string ExecutableNameTag = "{EXECUTABLE}";
|
||||
private const string WorkingDirectoryTag = "{WORKINGDIRECTORY}";
|
||||
private const string RefTagPrefix = "/refs/tags";
|
||||
private const string VersionListUrl = "https://api.github.com/repos/AlexMacocian/Daybreak/git/refs/tags";
|
||||
private const string Url = "https://github.com/AlexMacocian/Daybreak/releases/latest";
|
||||
private const string DownloadUrl = $"https://github.com/AlexMacocian/Daybreak/releases/download/v{VersionTag}/Daybreakv{VersionTag}.zip";
|
||||
private const string DownloadUrl = $"https://github.com/AlexMacocian/Daybreak/releases/download/{VersionTag}/Daybreak{VersionTag}.zip";
|
||||
private const string GetExecutionPolicyCommand = "Get-ExecutionPolicy -Scope CurrentUser";
|
||||
private const string SetExecutionPolicyCommand = $"Set-ExecutionPolicy {ExecutionPolicyTag} -Scope CurrentUser";
|
||||
private const string WaitCommand = $"Wait-Process -Id {ProcessIdTag}";
|
||||
private const string ExtractCommandTemplate = $"Expand-Archive -Path '{InputFileTag}' -DestinationPath '{OutputPathTag}' -Force";
|
||||
private const string RunClientCommand = @".\Daybreak.exe";
|
||||
private const string PrepareScheduledAction = $"$action = New-ScheduledTaskAction -Execute {ExecutableNameTag} -WorkingDirectory {WorkingDirectoryTag}";
|
||||
private const string PrepareTriggerForAction = $"$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date)";
|
||||
private const string RegisterScheduledAction = $"Register-ScheduledTask -Action $action -Trigger $trigger -TaskName {LaunchActionName} | Out-Null";
|
||||
private const string LaunchScheduledAction = $"Start-ScheduledTask -TaskName {LaunchActionName}";
|
||||
private const string SleepOneSecond = $"Start-Sleep -s 1";
|
||||
private const string UnregisterScheduledAction = $"Unregister-ScheduledTask -TaskName {LaunchActionName} -Confirm:$false";
|
||||
private const string RemoveTempFile = $"Remove-item {TempFile}";
|
||||
private const string RemovePs1 = $"Remove-item {ExtractAndRunPs1}";
|
||||
|
||||
@@ -48,7 +61,7 @@ namespace Daybreak.Services.Updater
|
||||
private readonly IRuntimeStore runtimeStore;
|
||||
private readonly HttpClient httpClient = new();
|
||||
|
||||
public string CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
public Version CurrentVersion { get; }
|
||||
|
||||
public ApplicationUpdater(
|
||||
ILogger logger,
|
||||
@@ -58,31 +71,38 @@ namespace Daybreak.Services.Updater
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.runtimeStore = runtimeStore.ThrowIfNull(nameof(runtimeStore));
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.httpClient.DefaultRequestHeaders.Add("user-agent", "Daybreak Client");
|
||||
if (Version.TryParse(Assembly.GetExecutingAssembly().GetName().Version.ToString(), out var currentVersion))
|
||||
{
|
||||
if (currentVersion.HasPrefix is false)
|
||||
{
|
||||
currentVersion = Version.Parse("v" + currentVersion);
|
||||
}
|
||||
|
||||
this.CurrentVersion = currentVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FatalException($"Application version is invalid: {Assembly.GetExecutingAssembly().GetName().Version}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadUpdate(UpdateStatus updateStatus)
|
||||
public async Task<bool> DownloadUpdate(Version version, UpdateStatus updateStatus)
|
||||
{
|
||||
updateStatus.CurrentStep = UpdateStatus.CheckingLatestVersion;
|
||||
var latestVersion = (await this.GetLatestVersion()).ExtractValue();
|
||||
if (latestVersion is null)
|
||||
updateStatus.CurrentStep = UpdateStatus.InitializingDownload;
|
||||
var uri = DownloadUrl.Replace(VersionTag, version.ToString());
|
||||
using var response = await this.httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
|
||||
if (response.IsSuccessStatusCode is false)
|
||||
{
|
||||
this.logger.LogWarning("Failed to retrieve latest version. Aborting update");
|
||||
return false;
|
||||
}
|
||||
|
||||
using var downloadLatestResponse = await this.httpClient.GetAsync(
|
||||
DownloadUrl.Replace(VersionTag, latestVersion));
|
||||
|
||||
if (downloadLatestResponse.IsSuccessStatusCode is false)
|
||||
{
|
||||
this.logger.LogWarning("Failed to download latest version. Aborting udpate");
|
||||
updateStatus.CurrentStep = UpdateStatus.FailedDownload;
|
||||
this.logger.LogError($"Failed to download update. Details: {await response.Content.ReadAsStringAsync()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
using var downloadStream = await this.httpClient.GetStreamAsync(uri);
|
||||
this.logger.LogInformation("Beginning update download");
|
||||
var downloadStream = await downloadLatestResponse.Content.ReadAsStreamAsync();
|
||||
var fileStream = File.OpenWrite(TempFile);
|
||||
var downloadSize = (double)downloadStream.Length;
|
||||
var downloadSize = (double)response.Content.Headers.ContentLength;
|
||||
var buffer = new byte[1024];
|
||||
var length = 0;
|
||||
double downloaded = 0;
|
||||
@@ -105,12 +125,29 @@ namespace Daybreak.Services.Updater
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DownloadLatestUpdate(UpdateStatus updateStatus)
|
||||
{
|
||||
updateStatus.CurrentStep = UpdateStatus.CheckingLatestVersion;
|
||||
var latestVersion = (await this.GetLatestVersion()).ExtractValue();
|
||||
if (latestVersion is null)
|
||||
{
|
||||
this.logger.LogWarning("Failed to retrieve latest version. Aborting update");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Version.TryParse(latestVersion, out var parsedVersion) is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not parse retrieved version: {latestVersion}");
|
||||
}
|
||||
|
||||
return await this.DownloadUpdate(parsedVersion, updateStatus);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAvailable()
|
||||
{
|
||||
var version = string.Join('.', this.CurrentVersion.Split('.'));
|
||||
var maybeLatestVersion = await this.GetLatestVersion();
|
||||
return maybeLatestVersion.Switch(
|
||||
onSome: latestVersion => string.Compare(version, latestVersion, true) < 0,
|
||||
onSome: latestVersion => string.Compare(this.CurrentVersion.ToString().Trim('v'), latestVersion, true) < 0,
|
||||
onNone: () =>
|
||||
{
|
||||
this.logger.LogWarning("Failed to retrieve latest version");
|
||||
@@ -118,6 +155,20 @@ namespace Daybreak.Services.Updater
|
||||
}).ExtractValue();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Version>> GetVersions()
|
||||
{
|
||||
this.logger.LogInformation($"Retrieving version list from {VersionListUrl}");
|
||||
var response = await this.httpClient.GetAsync(VersionListUrl);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var serializedList = await response.Content.ReadAsStringAsync();
|
||||
var versionList = serializedList.Deserialize<GithubRefTag[]>();
|
||||
return versionList.Select(v => v.Ref.Remove(0, RefTagPrefix.Length)).Select(v => new Version(v));
|
||||
}
|
||||
|
||||
return new List<Version>();
|
||||
}
|
||||
|
||||
public void PeriodicallyCheckForUpdates()
|
||||
{
|
||||
System.Extensions.TaskExtensions.RunPeriodicAsync(async () =>
|
||||
@@ -251,8 +302,15 @@ namespace Daybreak.Services.Updater
|
||||
.Replace(InputFileTag, Path.GetFullPath(TempFile))
|
||||
.Replace(OutputPathTag, Directory.GetCurrentDirectory()),
|
||||
RemoveTempFile,
|
||||
PrepareScheduledAction
|
||||
.Replace(ExecutableNameTag, Process.GetCurrentProcess()?.MainModule?.FileName)
|
||||
.Replace(WorkingDirectoryTag, Directory.GetCurrentDirectory()),
|
||||
PrepareTriggerForAction,
|
||||
RegisterScheduledAction,
|
||||
LaunchScheduledAction,
|
||||
SleepOneSecond,
|
||||
UnregisterScheduledAction,
|
||||
RemovePs1,
|
||||
RunClientCommand
|
||||
});
|
||||
var process = new Process()
|
||||
{
|
||||
@@ -260,12 +318,10 @@ namespace Daybreak.Services.Updater
|
||||
{
|
||||
FileName = "powershell.exe",
|
||||
Arguments = $@"{Directory.GetCurrentDirectory()}\{ExtractAndRunPs1}",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = true,
|
||||
WindowStyle = ProcessWindowStyle.Maximized,
|
||||
WorkingDirectory = Directory.GetCurrentDirectory()
|
||||
WorkingDirectory = Directory.GetCurrentDirectory(),
|
||||
Verb = "runas"
|
||||
},
|
||||
};
|
||||
this.logger.LogInformation("Created extractor script. Attempting to launch powershell");
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Models.Versioning;
|
||||
using Daybreak.Services.ApplicationLifetime;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.Updater
|
||||
{
|
||||
public interface IApplicationUpdater : IApplicationLifetimeService
|
||||
{
|
||||
string CurrentVersion { get; }
|
||||
Version CurrentVersion { get; }
|
||||
void FinalizeUpdate();
|
||||
void PeriodicallyCheckForUpdates();
|
||||
Task<IEnumerable<Version>> GetVersions();
|
||||
Task<bool> UpdateAvailable();
|
||||
Task<bool> DownloadUpdate(UpdateStatus updateStatus);
|
||||
Task<bool> DownloadUpdate(Version version, UpdateStatus updateStatus);
|
||||
Task<bool> DownloadLatestUpdate(UpdateStatus updateStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,14 @@ namespace Pepa.Wpf.Utilities
|
||||
{
|
||||
static class NativeMethods
|
||||
{
|
||||
public static uint WM_KEYDOWN = 0x0100;
|
||||
public static uint SWP_SHOWWINDOW = 0x0040;
|
||||
public static IntPtr HWND_TOPMOST = new(-1);
|
||||
public static IntPtr HWND_TOP = IntPtr.Zero;
|
||||
public const int WH_KEYBOARD_LL = 13;
|
||||
|
||||
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct SystemHandleInformation
|
||||
{
|
||||
@@ -89,7 +97,7 @@ namespace Pepa.Wpf.Utilities
|
||||
|
||||
public const int WM_SYSCOMMAND = 0x112;
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
@@ -103,7 +111,27 @@ namespace Pepa.Wpf.Utilities
|
||||
public static extern NtStatus NtQueryObject(IntPtr ObjectHandle, ObjectInformationClass ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength, out int ReturnLength);
|
||||
[DllImport("ntdll.dll")]
|
||||
public static extern NtStatus NtQuerySystemInformation(SystemInformationClass SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength, out int ReturnLength);
|
||||
[DllImport("kernel32.dll")]
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, StringBuilder lpExeName, ref uint lpdwSize);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool SetWindowPos(IntPtr hwnd, IntPtr insertAfter, int x, int y, int cx, int cy, uint flags);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool ShowWindow(IntPtr hwnd, int cmd);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern int GetWindowTextLength(IntPtr hWnd);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
public static extern IntPtr LoadLibrary(string lpFileName);
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool UnhookWindowsHookEx(IntPtr hHook);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr CallNextHookEx(IntPtr hHook, int code, IntPtr wParam, IntPtr lParam);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetForegroundWindow();
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,10 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace Daybreak.Utils
|
||||
{
|
||||
public static class SerializationExtensions
|
||||
{
|
||||
public static byte[] SerializeBytes(this object obj)
|
||||
{
|
||||
byte[] returnArray;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
var bf = new BinaryFormatter();
|
||||
bf.Serialize(memoryStream, obj);
|
||||
returnArray = memoryStream.ToArray();
|
||||
}
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
public static T DeserializeBytes<T>(this byte[] serializedObject)
|
||||
{
|
||||
T obj;
|
||||
using (var memoryStream = new MemoryStream(serializedObject))
|
||||
{
|
||||
var bf = new BinaryFormatter();
|
||||
obj = (T)bf.Deserialize(memoryStream);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static string Serialize<T>(this T obj)
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj);
|
||||
|
||||
@@ -9,13 +9,19 @@
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Width="400" Height="200"
|
||||
Background="White">
|
||||
<TextBlock Text="An update has been detected. Do you wish to update?" FontSize="20" TextWrapping="Wrap"
|
||||
Foreground="Black"></TextBlock>
|
||||
<controls:OpaqueButton Text="No" Foreground="Black" TransparentBackground="Gray" BackgroundOpacity="0.2" Width="50" Height="30"
|
||||
FontSize="16" HorizontalAlignment="Center" Margin="0, 0, 80, 0"
|
||||
Clicked="NoButton_Clicked"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="Yes" Foreground="Black" TransparentBackground="Gray" BackgroundOpacity="0.2" Width="50" Height="30"
|
||||
FontSize="16" HorizontalAlignment="Center" Margin="80, 0, 0, 0"
|
||||
Clicked="YesButton_Clicked"></controls:OpaqueButton>
|
||||
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
|
||||
<TextBlock Text="An update has been detected. Do you want to download the update?" HorizontalAlignment="Center"
|
||||
FontSize="16" Foreground="Black" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<controls:OpaqueButton Text="Yes" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="YesButton_Clicked" Foreground="Black" FontSize="16"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="No" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="NoButton_Clicked" Foreground="Black" Grid.Column="1" FontSize="16"></controls:OpaqueButton>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.Privilege;
|
||||
using Daybreak.Services.Runtime;
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Utils;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Views
|
||||
@@ -18,18 +20,35 @@ namespace Daybreak.Views
|
||||
private readonly ILogger logger;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IRuntimeStore runtimeStore;
|
||||
private readonly IPrivilegeManager privilegeManager;
|
||||
private readonly IApplicationUpdater applicationUpdater;
|
||||
|
||||
public AskUpdateView(
|
||||
ILogger logger,
|
||||
IViewManager viewManager,
|
||||
IRuntimeStore runtimeStore)
|
||||
IRuntimeStore runtimeStore,
|
||||
IPrivilegeManager privilegeManager,
|
||||
IApplicationUpdater applicationUpdater)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.runtimeStore = runtimeStore.ThrowIfNull(nameof(runtimeStore));
|
||||
this.privilegeManager = privilegeManager.ThrowIfNull(nameof(privilegeManager));
|
||||
this.applicationUpdater = applicationUpdater.ThrowIfNull(nameof(applicationUpdater));
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private bool CheckIfAdmin()
|
||||
{
|
||||
if (this.privilegeManager.AdminPrivileges is false)
|
||||
{
|
||||
this.privilegeManager.RequestAdminPrivileges<MainView>("Application needs to be in administrator mode in order to update.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void NoButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.logger.LogInformation("User declined update");
|
||||
@@ -37,11 +56,17 @@ namespace Daybreak.Views
|
||||
this.viewManager.ShowView<MainView>();
|
||||
}
|
||||
|
||||
private void YesButton_Clicked(object sender, System.EventArgs e)
|
||||
private async void YesButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.logger.LogInformation("User accepted update");
|
||||
this.runtimeStore.StoreValue(UpdateDesiredKey, true);
|
||||
this.viewManager.ShowView<UpdateView>();
|
||||
if (this.CheckIfAdmin() is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var latestVersion = (await this.applicationUpdater.GetVersions()).Last();
|
||||
this.viewManager.ShowView<UpdateView>(latestVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,10 +87,11 @@
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Multi-launch support" Foreground="White" FontSize="22" VerticalAlignment="Center" Margin="0, 0, 15, 0" Height="30"></TextBlock>
|
||||
<TextBlock Text="GWToolbox startup delay (in ms)" Foreground="White" FontSize="22" VerticalAlignment="Center" Margin="0, 0, 15, 0" Height="30"></TextBlock>
|
||||
<TextBlock Text="Detect build templates (in browser)" Foreground="White" FontSize="22" VerticalAlignment="Center" Margin="0, 0, 15, 0" Height="30"></TextBlock>
|
||||
<StackPanel Margin="0, 0, 15, 0">
|
||||
<TextBlock Text="Multi-launch support" Foreground="White" FontSize="22" Height="30"></TextBlock>
|
||||
<TextBlock Text="GWToolbox startup delay (in ms)" Foreground="White" FontSize="22" Height="30"></TextBlock>
|
||||
<TextBlock Text="Detect build templates (in browser)" Foreground="White" FontSize="22" Height="30"></TextBlock>
|
||||
<TextBlock Text="Launch gw as current user" Foreground="White" FontSize="22" Height="30"></TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<ToggleButton IsChecked="{Binding ElementName=_this, Path=MultiLaunch, Mode=TwoWay}" Style="{StaticResource AnimatedSwitch}"
|
||||
@@ -100,6 +101,8 @@
|
||||
CommandManager.PreviewCanExecute="TextBox_DisallowPaste"></TextBox>
|
||||
<ToggleButton IsChecked="{Binding ElementName=_this, Path=DynamicBuildLoading, Mode=TwoWay}" Style="{StaticResource AnimatedSwitch}"
|
||||
Height="30" Width="60"></ToggleButton>
|
||||
<ToggleButton IsChecked="{Binding ElementName=_this, Path=LaunchAsCurrentUser, Mode=TwoWay}" Style="{StaticResource AnimatedSwitch}"
|
||||
Height="30" Width="60"></ToggleButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -21,10 +21,19 @@ namespace Daybreak.Views
|
||||
DependencyPropertyExtensions.Register<ExperimentalSettingsView, string>(nameof(GWToolboxLaunchDelay));
|
||||
public static readonly DependencyProperty DynamicBuildLoadingProperty =
|
||||
DependencyPropertyExtensions.Register<ExperimentalSettingsView, bool>(nameof(DynamicBuildLoading));
|
||||
public static readonly DependencyProperty LaunchAsCurrentUserProperty =
|
||||
DependencyPropertyExtensions.Register<ExperimentalSettingsView, bool>(nameof(LaunchAsCurrentUser));
|
||||
public static readonly DependencyProperty MacrosEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<ExperimentalSettingsView, bool>(nameof(MacrosEnabled));
|
||||
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
|
||||
public bool LaunchAsCurrentUser
|
||||
{
|
||||
get => this.GetTypedValue<bool>(LaunchAsCurrentUserProperty);
|
||||
set => this.SetValue(LaunchAsCurrentUserProperty, value);
|
||||
}
|
||||
public bool MultiLaunch
|
||||
{
|
||||
get => this.GetTypedValue<bool>(MultiLaunchProperty);
|
||||
@@ -40,6 +49,11 @@ namespace Daybreak.Views
|
||||
get => this.GetTypedValue<bool>(DynamicBuildLoadingProperty);
|
||||
set => this.SetValue(DynamicBuildLoadingProperty, value);
|
||||
}
|
||||
public bool MacrosEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(MacrosEnabledProperty);
|
||||
set => this.SetValue(MacrosEnabledProperty, value);
|
||||
}
|
||||
|
||||
public ExperimentalSettingsView(
|
||||
IViewManager viewManager,
|
||||
@@ -57,6 +71,8 @@ namespace Daybreak.Views
|
||||
this.MultiLaunch = config.ExperimentalFeatures.MultiLaunchSupport;
|
||||
this.GWToolboxLaunchDelay = config.ExperimentalFeatures.ToolboxAutoLaunchDelay.ToString();
|
||||
this.DynamicBuildLoading = config.ExperimentalFeatures.DynamicBuildLoading;
|
||||
this.LaunchAsCurrentUser = config.ExperimentalFeatures.LaunchGuildwarsAsCurrentUser;
|
||||
this.MacrosEnabled = config.ExperimentalFeatures.CanInterceptKeys;
|
||||
}
|
||||
|
||||
private void SaveExperimentalSettings()
|
||||
@@ -64,6 +80,8 @@ namespace Daybreak.Views
|
||||
var config = this.configurationManager.GetConfiguration();
|
||||
config.ExperimentalFeatures.MultiLaunchSupport = this.MultiLaunch;
|
||||
config.ExperimentalFeatures.DynamicBuildLoading = this.DynamicBuildLoading;
|
||||
config.ExperimentalFeatures.LaunchGuildwarsAsCurrentUser = this.LaunchAsCurrentUser;
|
||||
config.ExperimentalFeatures.CanInterceptKeys = this.MacrosEnabled;
|
||||
if (int.TryParse(this.GWToolboxLaunchDelay, out var gwToolboxLaunchDelay))
|
||||
{
|
||||
config.ExperimentalFeatures.ToolboxAutoLaunchDelay = gwToolboxLaunchDelay;
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Services.ApplicationLauncher;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Screens;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
@@ -16,86 +18,45 @@ namespace Daybreak.Views
|
||||
/// <summary>
|
||||
/// Interaction logic for StartupView.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class MainView : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty ButtonsVisibleProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(ButtonsVisible), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty LaunchButtonEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(LaunchButtonEnabled));
|
||||
public static readonly DependencyProperty LaunchToolboxButtonEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(LaunchToolboxButtonEnabled));
|
||||
public static readonly DependencyProperty LaunchTexmodButtonEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(LaunchTexmodButtonEnabled));
|
||||
public static readonly DependencyProperty BrowsersEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, bool>(nameof(BrowsersEnabled), new PropertyMetadata(true));
|
||||
public static readonly DependencyProperty RightBrowserAddressProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, string>(nameof(RightBrowserAddress));
|
||||
public static readonly DependencyProperty LeftBrowserAddressProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, string>(nameof(LeftBrowserAddress));
|
||||
public static readonly DependencyProperty RightBrowserFavoriteAddressProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, string>(nameof(RightBrowserFavoriteAddress));
|
||||
public static readonly DependencyProperty LeftBrowserFavoriteAddressProperty =
|
||||
DependencyPropertyExtensions.Register<MainView, string>(nameof(LeftBrowserFavoriteAddress));
|
||||
|
||||
private readonly IApplicationLauncher applicationDetector;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly IScreenManager screenManager;
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new();
|
||||
|
||||
private bool leftBrowserMaximized = false;
|
||||
private bool rightBrowserMaximized = false;
|
||||
|
||||
public bool ButtonsVisible
|
||||
{
|
||||
get => this.GetTypedValue<bool>(ButtonsVisibleProperty);
|
||||
set => this.SetTypedValue(ButtonsVisibleProperty, value);
|
||||
}
|
||||
public string RightBrowserFavoriteAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(RightBrowserFavoriteAddressProperty);
|
||||
set => this.SetTypedValue(RightBrowserFavoriteAddressProperty, value);
|
||||
}
|
||||
public string LeftBrowserFavoriteAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(LeftBrowserFavoriteAddressProperty);
|
||||
set => this.SetTypedValue(LeftBrowserFavoriteAddressProperty, value);
|
||||
}
|
||||
public string RightBrowserAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(RightBrowserAddressProperty);
|
||||
set => this.SetTypedValue(RightBrowserAddressProperty, value);
|
||||
}
|
||||
public string LeftBrowserAddress
|
||||
{
|
||||
get => this.GetTypedValue<string>(LeftBrowserAddressProperty);
|
||||
set => this.SetTypedValue(LeftBrowserAddressProperty, value);
|
||||
}
|
||||
public bool LaunchButtonEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(LaunchButtonEnabledProperty);
|
||||
set => this.SetTypedValue(LaunchButtonEnabledProperty, value);
|
||||
}
|
||||
public bool LaunchToolboxButtonEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(LaunchToolboxButtonEnabledProperty);
|
||||
set => this.SetTypedValue(LaunchToolboxButtonEnabledProperty, value);
|
||||
}
|
||||
public bool LaunchTexmodButtonEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(LaunchTexmodButtonEnabledProperty);
|
||||
set => this.SetTypedValue(LaunchTexmodButtonEnabledProperty, value);
|
||||
}
|
||||
public bool BrowsersEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(BrowsersEnabledProperty);
|
||||
set => this.SetTypedValue(BrowsersEnabledProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool buttonsVisible;
|
||||
[GenerateDependencyProperty]
|
||||
private string rightBrowserFavoriteAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private string leftBrowserFavoriteAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private string rightBrowserAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private string leftBrowserAddress;
|
||||
[GenerateDependencyProperty]
|
||||
private bool launchButtonEnabled;
|
||||
[GenerateDependencyProperty]
|
||||
private bool launchToolboxButtonEnabled;
|
||||
[GenerateDependencyProperty]
|
||||
private bool launchTexmodButtonEnabled;
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool browsersEnabled;
|
||||
|
||||
public MainView(
|
||||
IApplicationLauncher applicationDetector,
|
||||
IViewManager viewManager,
|
||||
IConfigurationManager configurationManager)
|
||||
IConfigurationManager configurationManager,
|
||||
IScreenManager screenManager)
|
||||
{
|
||||
this.screenManager = screenManager.ThrowIfNull(nameof(screenManager));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.applicationDetector = applicationDetector.ThrowIfNull(nameof(applicationDetector));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
@@ -145,7 +106,23 @@ namespace Daybreak.Views
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.applicationDetector.LaunchGuildwars();
|
||||
if (await this.applicationDetector.LaunchGuildwars() is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.configurationManager.GetConfiguration().SetGuildwarsWindowSizeOnLaunch)
|
||||
{
|
||||
var id = this.configurationManager.GetConfiguration().DesiredGuildwarsScreen;
|
||||
var desiredScreen = this.screenManager.Screens.Skip(id).FirstOrDefault();
|
||||
if (desiredScreen is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to set guildwars on desired screen. No screen with id {id}");
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
this.screenManager.MoveGuildwarsToScreen(desiredScreen);
|
||||
}
|
||||
|
||||
if (this.configurationManager.GetConfiguration().ToolboxAutoLaunch is true)
|
||||
{
|
||||
var delay = this.configurationManager.GetConfiguration().ExperimentalFeatures.ToolboxAutoLaunchDelay;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<UserControl x:Class="Daybreak.Views.RequestElevationView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
mc:Ignorable="d"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
|
||||
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding MessageToUser}" Margin="10, 0, 10, 0" Foreground="Black"
|
||||
TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="16"></TextBlock>
|
||||
<TextBlock Text="Do you want to restart the application with administrator rights?" HorizontalAlignment="Center"
|
||||
FontSize="16" Foreground="Black" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<controls:OpaqueButton Text="Yes" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="YesButton_Clicked" Foreground="Black" FontSize="16"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="No" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="NoButton_Clicked" Foreground="Black" Grid.Column="1" FontSize="16"></controls:OpaqueButton>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,50 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.ApplicationLauncher;
|
||||
using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Utils;
|
||||
using System.Extensions;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for RequestElevationView.xaml
|
||||
/// </summary>
|
||||
public partial class RequestElevationView : UserControl
|
||||
{
|
||||
private readonly IApplicationLauncher applicationLauncher;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly ILogger logger;
|
||||
|
||||
public RequestElevationView(
|
||||
IApplicationLauncher applicationLauncher,
|
||||
IViewManager viewManager,
|
||||
ILogger logger)
|
||||
{
|
||||
this.applicationLauncher = applicationLauncher.ThrowIfNull(nameof(applicationLauncher));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void YesButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.applicationLauncher.RestartDaybreakAsAdmin();
|
||||
}
|
||||
|
||||
private void NoButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (this.DataContext is ElevationRequest elevationRequest)
|
||||
{
|
||||
this.logger.LogWarning($"Elevation request denied. Showing {elevationRequest.View} with {elevationRequest.DataContext}");
|
||||
this.viewManager.ShowView(elevationRequest.View, elevationRequest.DataContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logger.LogWarning("Elevation request context is not set. Returning to home page");
|
||||
this.viewManager.ShowView<MainView>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<UserControl x:Class="Daybreak.Views.ScreenChoiceView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<TextBlock Text="Choose screen" FontSize="18" Foreground="White" HorizontalAlignment="Center"></TextBlock>
|
||||
<controls:OpaqueButton Text="Test" Foreground="White" HighlightOpacity="0.3" Highlight="White" FontSize="18" Width="80"
|
||||
HorizontalAlignment="Right" Margin="0, 0, 50, 0" Clicked="OpaqueButton_Clicked"
|
||||
IsEnabled="{Binding ElementName=_this, Path=CanTest, Mode=OneWay}"></controls:OpaqueButton>
|
||||
<controls:SaveButton Foreground="White" Height="30" Width="30" Grid.Column="1" HorizontalAlignment="Right" Margin="5"
|
||||
Clicked="SaveButton_Clicked"></controls:SaveButton>
|
||||
<Viewbox Grid.Row="1">
|
||||
<Grid x:Name="ScreenContainer"></Grid>
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,104 @@
|
||||
using Daybreak.Controls.Templates;
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Services.ApplicationLauncher;
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Screens;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ScreenChoiceView.xaml
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class ScreenChoiceView : UserControl
|
||||
{
|
||||
private readonly IScreenManager screenManager;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly IApplicationLauncher applicationLauncher;
|
||||
private int selectedId;
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private bool canTest;
|
||||
|
||||
public ScreenChoiceView(
|
||||
IViewManager viewManager,
|
||||
IScreenManager screenManager,
|
||||
IConfigurationManager configurationManager,
|
||||
IApplicationLauncher applicationLauncher)
|
||||
{
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.screenManager = screenManager.ThrowIfNull(nameof(screenManager));
|
||||
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
|
||||
this.applicationLauncher = applicationLauncher.ThrowIfNull(nameof(applicationLauncher));
|
||||
this.InitializeComponent();
|
||||
this.selectedId = configurationManager.GetConfiguration().DesiredGuildwarsScreen;
|
||||
this.CanTest = applicationLauncher.IsGuildwarsRunning;
|
||||
this.SetupView();
|
||||
}
|
||||
|
||||
private void SetupView()
|
||||
{
|
||||
foreach(var screen in this.screenManager.Screens)
|
||||
{
|
||||
var screenTemplate = new ScreenTemplate
|
||||
{
|
||||
DataContext = screen,
|
||||
Margin = new System.Windows.Thickness(screen.Size.Left, screen.Size.Top, 0, 0),
|
||||
Width = screen.Size.Width,
|
||||
Height = screen.Size.Height,
|
||||
VerticalAlignment = System.Windows.VerticalAlignment.Top,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
|
||||
Foreground = screen.Id == this.selectedId ? Brushes.LightGreen : Brushes.White
|
||||
};
|
||||
screenTemplate.Clicked += ScreenTemplate_Clicked;
|
||||
this.ScreenContainer.Children.Add(screenTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScreenTemplate_Clicked(object sender, Screen e)
|
||||
{
|
||||
this.SelectScreen(e);
|
||||
}
|
||||
|
||||
private void SelectScreen(Screen screen)
|
||||
{
|
||||
this.selectedId = screen.Id;
|
||||
foreach(var template in this.ScreenContainer.Children.OfType<ScreenTemplate>())
|
||||
{
|
||||
template.Foreground = template.DataContext.As<Screen>().Id == this.selectedId ? Brushes.LightGreen : Brushes.White;
|
||||
}
|
||||
}
|
||||
|
||||
private void BackButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<SettingsView>();
|
||||
}
|
||||
|
||||
private void SaveButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.configurationManager.GetConfiguration().DesiredGuildwarsScreen = this.selectedId;
|
||||
this.viewManager.ShowView<SettingsCategoryView>();
|
||||
}
|
||||
|
||||
private void OpaqueButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
var screen = this.screenManager.Screens.Skip(this.selectedId).FirstOrDefault();
|
||||
if (screen is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to test placement. No screen with id {this.selectedId}");
|
||||
}
|
||||
|
||||
this.screenManager.MoveGuildwarsToScreen(screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,12 @@
|
||||
<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>
|
||||
</WrapPanel>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace Daybreak.Views
|
||||
this.viewManager.ShowView<BuildsListView>();
|
||||
}
|
||||
|
||||
private void VersionButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<VersionManagementView>(this);
|
||||
}
|
||||
|
||||
private void FileButton_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<ExecutablesView>();
|
||||
|
||||
@@ -66,60 +66,89 @@
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Launcher settings" FontSize="22" Foreground="White" HorizontalAlignment="Center" Grid.ColumnSpan="2"></TextBlock>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<controls:SaveButton Foreground="White" Height="30" Width="30" Grid.Column="1" HorizontalAlignment="Right" Margin="5"
|
||||
Clicked="SaveButton_Clicked"></controls:SaveButton>
|
||||
<StackPanel Orientation="Vertical" Grid.Row="1">
|
||||
<TextBlock Text="Texmod path" FontSize="22" Foreground="White"></TextBlock>
|
||||
<TextBlock Text="GWToolbox path: " FontSize="22" Foreground="White"></TextBlock>
|
||||
<TextBlock Text="GWToolbox auto-launch: " FontSize="22" Foreground="White"></TextBlock>
|
||||
<TextBlock Text="Browsers enabled: " FontSize="22" Foreground="White"/>
|
||||
<TextBlock Text="Browsers address bar readonly: " FontSize="22" Foreground="White"/>
|
||||
<TextBlock Text="Left browser homepage: " FontSize="22" Foreground="White"></TextBlock>
|
||||
<TextBlock Text="Right browser homepage: " FontSize="22" Foreground="White"></TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="1">
|
||||
<Grid>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Launcher settings" FontSize="22" Foreground="White" HorizontalAlignment="Center" Grid.ColumnSpan="2"></TextBlock>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<controls:SaveButton Foreground="White" Height="30" Width="30" Grid.Column="1" HorizontalAlignment="Right" Margin="5"
|
||||
Clicked="SaveButton_Clicked"></controls:SaveButton>
|
||||
<StackPanel Orientation="Vertical" Grid.Row="1">
|
||||
<TextBlock Text="Auto check for updates: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Texmod path: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="GWToolbox auto-launch: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="GWToolbox path: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Browsers enabled: " FontSize="22" Foreground="White" Height="30"/>
|
||||
<TextBlock Text="Browsers address bar readonly: " FontSize="22" Foreground="White" Height="30"/>
|
||||
<TextBlock Text="Left browser homepage: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Right browser homepage: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Auto-place on desired screen: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Desired screen id: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Place Shortcut: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
<TextBlock Text="Shortcut folder: " FontSize="22" Foreground="White" Height="30"></TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="1">
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=AutoCheckUpdate, Mode=TwoWay}"></ToggleButton>
|
||||
<Grid Height="30">
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=TexmodPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="TexmodFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=ToolboxAutoLaunch, Mode=TwoWay}"></ToggleButton>
|
||||
<Grid Height="30">
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=ToolboxPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="ToolboxFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=TwoWay}"></ToggleButton>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=AddressBarReadonly, Mode=TwoWay}"></ToggleButton>
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=TexmodPath, Mode=TwoWay}"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="TexmodFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<Grid>
|
||||
FontSize="22" Background="Transparent" Foreground="White" Height="30"
|
||||
Text="{Binding ElementName=_this, Path=LeftBrowserUrl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Margin="0, 0, 30, 0" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=ToolboxPath, Mode=TwoWay}"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="ToolboxFilePickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=ToolboxAutoLaunch, Mode=TwoWay}"></ToggleButton>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=TwoWay}"></ToggleButton>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=AddressBarReadonly, Mode=TwoWay}"></ToggleButton>
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=LeftBrowserUrl, Mode=TwoWay}"
|
||||
TextChanged="LeftBrowserUrl_TextChanged"></TextBox>
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=RightBrowserUrl, Mode=TwoWay}"
|
||||
TextChanged="RightBrowserUrl_TextChanged"></TextBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
FontSize="22" Background="Transparent" Foreground="White" Height="30"
|
||||
Text="{Binding ElementName=_this, Path=RightBrowserUrl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=AutoPlaceOnScreen, Mode=TwoWay}"></ToggleButton>
|
||||
<Grid Height="30">
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=DesiredScreen, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
PreviewTextInput="TextBox_AllowOnlyNumbers" Margin="0, 0, 30, 0"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="ScreenPickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
|
||||
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=ShortcutPlaced, Mode=TwoWay}"></ToggleButton>
|
||||
<Grid Height="30">
|
||||
<TextBox HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch"
|
||||
FontSize="22" Background="Transparent" Foreground="White"
|
||||
Text="{Binding ElementName=_this, Path=ShortcutFolder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="0, 0, 30, 0"></TextBox>
|
||||
<controls:FilePickerGlyph Height="30" Width="30" HorizontalAlignment="Right"
|
||||
Foreground="White" BorderBrush="Gray" BorderThickness="0"
|
||||
Clicked="ShortcutFolderPickerGlyph_Clicked"></controls:FilePickerGlyph>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Daybreak.Services.Configuration;
|
||||
using Daybreak.Services.Shortcuts;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
@@ -6,67 +7,44 @@ using System.Extensions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for SettingsView.xaml
|
||||
/// </summary>
|
||||
public partial class SettingsView : UserControl
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
|
||||
public partial class SettingsView : System.Windows.Controls.UserControl
|
||||
{
|
||||
public static readonly DependencyProperty TexmodPathProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(TexmodPath));
|
||||
public static readonly DependencyProperty ToolboxPathProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(ToolboxPath));
|
||||
public static readonly DependencyProperty AddressBarReadonlyProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, bool>(nameof(AddressBarReadonly));
|
||||
public static readonly DependencyProperty BrowsersEnabledProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, bool>(nameof(BrowsersEnabled));
|
||||
public static readonly DependencyProperty LeftBrowserUrlProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(LeftBrowserUrl));
|
||||
public static readonly DependencyProperty RightBrowserUrlProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, string>(nameof(RightBrowserUrl));
|
||||
public static readonly DependencyProperty ToolboxAutoLaunchProperty =
|
||||
DependencyPropertyExtensions.Register<SettingsView, bool>(nameof(ToolboxAutoLaunch));
|
||||
|
||||
private readonly IConfigurationManager configurationManager;
|
||||
private readonly IViewManager viewManager;
|
||||
|
||||
public string TexmodPath
|
||||
{
|
||||
get => this.GetTypedValue<string>(TexmodPathProperty);
|
||||
set => this.SetValue(TexmodPathProperty, value);
|
||||
}
|
||||
public bool ToolboxAutoLaunch
|
||||
{
|
||||
get => this.GetTypedValue<bool>(ToolboxAutoLaunchProperty);
|
||||
set => this.SetValue(ToolboxAutoLaunchProperty, value);
|
||||
}
|
||||
public string ToolboxPath
|
||||
{
|
||||
get => this.GetTypedValue<string>(ToolboxPathProperty);
|
||||
set => this.SetValue(ToolboxPathProperty, value);
|
||||
}
|
||||
public bool AddressBarReadonly
|
||||
{
|
||||
get => this.GetTypedValue<bool>(AddressBarReadonlyProperty);
|
||||
set => this.SetValue(AddressBarReadonlyProperty, value);
|
||||
}
|
||||
public string LeftBrowserUrl
|
||||
{
|
||||
get => this.GetTypedValue<string>(LeftBrowserUrlProperty);
|
||||
set => this.SetValue(LeftBrowserUrlProperty, value);
|
||||
}
|
||||
public string RightBrowserUrl
|
||||
{
|
||||
get => this.GetTypedValue<string>(RightBrowserUrlProperty);
|
||||
set => this.SetValue(RightBrowserUrlProperty, value);
|
||||
}
|
||||
public bool BrowsersEnabled
|
||||
{
|
||||
get => this.GetTypedValue<bool>(BrowsersEnabledProperty);
|
||||
set => this.SetValue(BrowsersEnabledProperty, value);
|
||||
}
|
||||
[GenerateDependencyProperty]
|
||||
private string texmodPath;
|
||||
[GenerateDependencyProperty]
|
||||
private bool toolboxAutoLaunch;
|
||||
[GenerateDependencyProperty]
|
||||
private string toolboxPath;
|
||||
[GenerateDependencyProperty]
|
||||
private bool addressBarReadonly;
|
||||
[GenerateDependencyProperty]
|
||||
private string leftBrowserUrl;
|
||||
[GenerateDependencyProperty]
|
||||
private string rightBrowserUrl;
|
||||
[GenerateDependencyProperty]
|
||||
private bool browsersEnabled;
|
||||
[GenerateDependencyProperty]
|
||||
private bool autoPlaceOnScreen;
|
||||
[GenerateDependencyProperty]
|
||||
private string desiredScreen;
|
||||
[GenerateDependencyProperty]
|
||||
private string shortcutFolder;
|
||||
[GenerateDependencyProperty]
|
||||
private bool shortcutPlaced;
|
||||
[GenerateDependencyProperty]
|
||||
private bool autoCheckUpdate;
|
||||
|
||||
public SettingsView(
|
||||
IConfigurationManager configurationManager,
|
||||
@@ -88,6 +66,11 @@ namespace Daybreak.Views
|
||||
this.ToolboxAutoLaunch = config.ToolboxAutoLaunch;
|
||||
this.TexmodPath = config.TexmodPath;
|
||||
this.BrowsersEnabled = config.BrowsersEnabled;
|
||||
this.AutoPlaceOnScreen = config.SetGuildwarsWindowSizeOnLaunch;
|
||||
this.DesiredScreen = config.DesiredGuildwarsScreen.ToString();
|
||||
this.ShortcutFolder = config.ShortcutLocation;
|
||||
this.ShortcutPlaced = config.PlaceShortcut;
|
||||
this.AutoCheckUpdate = config.AutoCheckUpdate;
|
||||
}
|
||||
|
||||
private void SaveButton_Clicked(object sender, EventArgs e)
|
||||
@@ -100,13 +83,18 @@ namespace Daybreak.Views
|
||||
currentConfig.ToolboxAutoLaunch = this.ToolboxAutoLaunch;
|
||||
currentConfig.TexmodPath = this.TexmodPath;
|
||||
currentConfig.BrowsersEnabled = this.BrowsersEnabled;
|
||||
currentConfig.SetGuildwarsWindowSizeOnLaunch = this.AutoPlaceOnScreen;
|
||||
currentConfig.DesiredGuildwarsScreen = int.Parse(this.DesiredScreen);
|
||||
currentConfig.ShortcutLocation = this.ShortcutFolder;
|
||||
currentConfig.PlaceShortcut = this.ShortcutPlaced;
|
||||
currentConfig.AutoCheckUpdate = this.AutoCheckUpdate;
|
||||
this.configurationManager.SaveConfiguration(currentConfig);
|
||||
this.viewManager.ShowView<SettingsCategoryView>();
|
||||
}
|
||||
|
||||
private void ToolboxFilePickerGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
var filePicker = new OpenFileDialog()
|
||||
var filePicker = new Microsoft.Win32.OpenFileDialog()
|
||||
{
|
||||
CheckFileExists = true,
|
||||
CheckPathExists = true,
|
||||
@@ -121,7 +109,7 @@ namespace Daybreak.Views
|
||||
|
||||
private void TexmodFilePickerGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
var filePicker = new OpenFileDialog()
|
||||
var filePicker = new Microsoft.Win32.OpenFileDialog()
|
||||
{
|
||||
CheckFileExists = true,
|
||||
CheckPathExists = true,
|
||||
@@ -134,19 +122,37 @@ namespace Daybreak.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void ShortcutFolderPickerGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
var folderPicker = new FolderBrowserDialog()
|
||||
{
|
||||
Description = "Select shortcut folder",
|
||||
UseDescriptionForTitle = true,
|
||||
SelectedPath = this.ShortcutFolder,
|
||||
ShowNewFolderButton = true
|
||||
};
|
||||
if (folderPicker.ShowDialog() is DialogResult.OK)
|
||||
{
|
||||
this.ShortcutFolder = folderPicker.SelectedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void ScreenPickerGlyph_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<ScreenChoiceView>();
|
||||
}
|
||||
|
||||
private void BackButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.viewManager.ShowView<SettingsCategoryView>();
|
||||
}
|
||||
|
||||
private void LeftBrowserUrl_TextChanged(object sender, TextChangedEventArgs e)
|
||||
private void TextBox_AllowOnlyNumbers(object sender, System.Windows.Input.TextCompositionEventArgs e)
|
||||
{
|
||||
this.LeftBrowserUrl = sender.As<TextBox>().Text;
|
||||
}
|
||||
|
||||
private void RightBrowserUrl_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
this.RightBrowserUrl = sender.As<TextBox>().Text;
|
||||
if (int.TryParse(e.Text, out _) is false)
|
||||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
Loaded="UpdateView_Loaded"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
|
||||
</UserControl.Resources>
|
||||
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 10" Foreground="Black"
|
||||
TextWrapping="Wrap"></TextBlock>
|
||||
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 0" Foreground="Black"
|
||||
TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="16"></TextBlock>
|
||||
<ProgressBar Minimum="0" Maximum="100" Value="{Binding ElementName=_this, Path=ProgressValue, Mode=OneWay}" Width="300" Height="20"></ProgressBar>
|
||||
<controls:OpaqueButton Text="Ok" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="30" Height="20"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ContinueButtonEnabled, Mode=OneWay}"
|
||||
Clicked="OpaqueButton_Clicked" Foreground="Black"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="Ok" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
|
||||
Clicked="OpaqueButton_Clicked" Foreground="Black" FontSize="16"
|
||||
Visibility="{Binding ElementName=_this, Path=ContinueButtonEnabled, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"></controls:OpaqueButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -3,46 +3,33 @@ using Daybreak.Services.Logging;
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Daybreak.Utils;
|
||||
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 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,
|
||||
@@ -71,8 +58,14 @@ namespace Daybreak.Views
|
||||
|
||||
private async void UpdateView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (this.DataContext is not Version version)
|
||||
{
|
||||
this.viewManager.ShowView<MainView>();
|
||||
throw new InvalidOperationException("No version specified for download");
|
||||
}
|
||||
|
||||
this.logger.LogInformation("Starting update procedure");
|
||||
var success = await applicationUpdater.DownloadUpdate(updateStatus).ConfigureAwait(true);
|
||||
var success = await applicationUpdater.DownloadUpdate(version, updateStatus).ConfigureAwait(true);
|
||||
if (success is false)
|
||||
{
|
||||
this.logger.LogError("Update procedure failed");
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<UserControl x:Class="Daybreak.Views.VersionManagementView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked"></controls:BackButton>
|
||||
<TextBlock Text="Manage application version" FontSize="18" Foreground="White" HorizontalAlignment="Center"></TextBlock>
|
||||
<WrapPanel Grid.Row="1" HorizontalAlignment="Center" Margin="10">
|
||||
<TextBlock Text="Current version: " Foreground="White" FontSize="16"></TextBlock>
|
||||
<controls:OpaqueButton Text="{Binding ElementName=_this, Path=CurrentVersion.VersionString, Mode=OneWay}" Foreground="White" FontSize="16"
|
||||
Cursor="Hand" BorderBrush="Transparent" BorderThickness="0" Clicked="CurrentVersion_Clicked"></controls:OpaqueButton>
|
||||
</WrapPanel>
|
||||
<ListView Grid.Row="2"
|
||||
Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Versions, Mode=OneWay}"
|
||||
HorizontalContentAlignment="Stretch" Width="230" ScrollViewer.CanContentScroll="True"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="5">
|
||||
<controls:OpaqueButton Text="{Binding VersionString}" Clicked="DesiredVersion_Clicked"
|
||||
Foreground="White" FontSize="16" BorderThickness="0"
|
||||
HorizontalAlignment="Left"></controls:OpaqueButton>
|
||||
<controls:BackButton Foreground="White" Width="30" Height="30" HorizontalAlignment="Right"
|
||||
Clicked="DesiredVersion_DownloadButton_Clicked">
|
||||
<controls:BackButton.RenderTransform>
|
||||
<RotateTransform Angle="270" CenterX="15" CenterY="15"></RotateTransform>
|
||||
</controls:BackButton.RenderTransform>
|
||||
</controls:BackButton>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,92 @@
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using Version = Daybreak.Models.Versioning.Version;
|
||||
|
||||
namespace Daybreak.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for VersionManagementView.xaml
|
||||
/// </summary>
|
||||
public partial class VersionManagementView : UserControl
|
||||
{
|
||||
private const string VersionPlaceholder = "{VERSION}";
|
||||
private const string ReleaseURL = $"https://github.com/AlexMacocian/Daybreak/releases/tag/{VersionPlaceholder}";
|
||||
|
||||
private readonly IApplicationUpdater applicationUpdater;
|
||||
private readonly IViewManager viewManager;
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private Version currentVersion;
|
||||
|
||||
public ObservableCollection<Version> Versions { get; } = new();
|
||||
|
||||
public VersionManagementView(
|
||||
IApplicationUpdater applicationUpdater,
|
||||
IViewManager viewManager)
|
||||
{
|
||||
this.applicationUpdater = applicationUpdater.ThrowIfNull(nameof(applicationUpdater));
|
||||
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
|
||||
this.InitializeComponent();
|
||||
this.CurrentVersion = this.applicationUpdater.CurrentVersion;
|
||||
this.LoadVersionList();
|
||||
}
|
||||
|
||||
private async void LoadVersionList()
|
||||
{
|
||||
this.Versions.ClearAnd().AddRange(await this.applicationUpdater.GetVersions());
|
||||
}
|
||||
|
||||
private void CurrentVersion_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("explorer.exe", ReleaseURL.Replace(VersionPlaceholder, this.currentVersion.ToString()));
|
||||
}
|
||||
|
||||
private void DesiredVersion_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is not UserControl userControl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (userControl.DataContext is not Version desiredVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Process.Start("explorer.exe", ReleaseURL.Replace(VersionPlaceholder, desiredVersion.ToString()));
|
||||
}
|
||||
|
||||
private void DesiredVersion_DownloadButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is not UserControl userControl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (userControl.DataContext is not Version desiredVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewManager.ShowView<UpdateView>(desiredVersion);
|
||||
}
|
||||
|
||||
private void BackButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (this.DataContext is UserControl userControl)
|
||||
{
|
||||
this.viewManager.ShowView(userControl.GetType());
|
||||
}
|
||||
else
|
||||
{
|
||||
this.viewManager.ShowView<MainView>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
@@ -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 categories.
|
||||
To adjust functionality, choose one of the settings categories
|
||||

|
||||
|
||||
## 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.
|
||||

|
||||
|
||||
## 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.
|
||||

|
||||
|
||||
## 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.
|
||||

|
||||
|
||||
## Multibox/Multilaunch support
|
||||
To enable multiboxing (multi-launch), go into Experimental settings. Then, switch between executables/accounts and launch them.
|
||||

|
||||
|
||||
## Build management / Template management
|
||||
To manage builds, go into Settings, Builds. Here you can select/remove builds.
|
||||

|
||||
|
||||
## 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.
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
## Screen selection/binding
|
||||
To launch the guildwars executable to a specific screen, open Settings, Launcher Settings either fill in the "Desired screen id",
|
||||

|
||||
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.
|
||||

|
||||
|
||||
## 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.
|
||||

|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Write-Host "Retrieving version"
|
||||
$filepath = Get-ChildItem -Path .\Daybreak -Filter *.version
|
||||
$version = $filepath.BaseName
|
||||
Write-Host "Version: $version"
|
||||
return $version
|
||||
Reference in New Issue
Block a user