From 261bcd576dff9fa78fc206a19e8807bb3857067c Mon Sep 17 00:00:00 2001 From: Patrick Magee Date: Sun, 27 Oct 2024 14:01:20 +0000 Subject: [PATCH] - support expiring timers after a period of game time --- src/Dota2Helper.Desktop/Program.cs | 1 + src/Dota2Helper.Desktop/appsettings.json | 28 +++-- src/Dota2Helper/App.axaml.cs | 29 ++--- src/Dota2Helper/Core/Audio/AudioPlayer.cs | 78 +++++++++++-- .../BackgroundServices/GameStateService.cs | 2 +- .../Core/Configuration/TimerOptions.cs | 5 +- src/Dota2Helper/Core/Gsi/GsiConfigService.cs | 3 +- .../Core/Listeners/DotaListener.cs | 13 +-- .../Core/Listeners/DynamicListenerStrategy.cs | 18 ++- src/Dota2Helper/Core/Timers/DotaTimer.cs | 103 ++++++++++++------ src/Dota2Helper/Core/Timers/DotaTimers.cs | 34 +++--- .../ViewModels/MainWindowViewModel.cs | 2 - src/Dota2Helper/ViewModels/TimersViewModel.cs | 1 + .../ViewModels/WindowExtensions.cs | 41 ------- src/Dota2Helper/Views/MainWindow.axaml | 1 - src/Dota2Helper/Views/MainWindow.axaml.cs | 2 +- src/Dota2Helper/Views/SettingsView.axaml | 19 +++- src/Dota2Helper/Views/TimersView.axaml | 2 +- 18 files changed, 232 insertions(+), 150 deletions(-) delete mode 100644 src/Dota2Helper/ViewModels/WindowExtensions.cs diff --git a/src/Dota2Helper.Desktop/Program.cs b/src/Dota2Helper.Desktop/Program.cs index dbf3a41..1a7faaa 100644 --- a/src/Dota2Helper.Desktop/Program.cs +++ b/src/Dota2Helper.Desktop/Program.cs @@ -17,5 +17,6 @@ private static AppBuilder BuildAvaloniaApp() .WithInterFont() .LogToTrace() .UseReactiveUI(); + } } diff --git a/src/Dota2Helper.Desktop/appsettings.json b/src/Dota2Helper.Desktop/appsettings.json index a038488..d7708db 100644 --- a/src/Dota2Helper.Desktop/appsettings.json +++ b/src/Dota2Helper.Desktop/appsettings.json @@ -6,8 +6,9 @@ "Label": "Stack", "First": "02:00", "Interval": "01:00", - "Reminder": "00:15", + "Reminder": "00:20", "AudioFile": "audio/Stack.mp3", + "ExpireAt": "20:00", "IsManualReset": false, "IsEnabled": true, "IsSoundEnabled": true, @@ -16,11 +17,12 @@ { "Speech": "Pull", "Label": "Pull 1", - "First": "02:00", + "First": "02:15", + "ExpireAt": "15:00", "Interval": "01:00", "Offset": "-00:15", "Reminder": "00:15", - "AudioFile": "audio/Stack.mp3", + "AudioFile": "", "IsManualReset": false, "IsEnabled": false, "IsSoundEnabled": true, @@ -29,10 +31,12 @@ { "Speech": "Pull", "Label": "Pull 2", - "First": "02:00", - "Interval": "00:45", + "ExpireAt": "15:00", + "First": "02:45", + "Interval": "01:00", + "Offset": "-00:30", "Reminder": "00:15", - "AudioFile": "audio/Stack.mp3", + "AudioFile": "", "IsManualReset": false, "IsEnabled": false, "IsSoundEnabled": true, @@ -42,6 +46,7 @@ "Speech": "Wisdom", "Label": "Wisdom", "First": "07:00", + "ExpireAt": "30:00", "Interval": "07:00", "Reminder": "00:45", "AudioFile": "audio/Wisdom.mp3", @@ -56,6 +61,7 @@ "First": "00:00", "Interval": "03:00", "Reminder": "00:20", + "ExpireAt": "30:00", "AudioFile": "audio/Bounty.mp3", "IsManualReset": false, "IsEnabled": true, @@ -67,6 +73,7 @@ "Label": "Power", "First": "06:00", "Interval": "06:00", + "ExpireAt": "35:00", "Reminder": "00:20", "AudioFile": "audio/Power.mp3", "IsManualReset": false, @@ -80,6 +87,7 @@ "First": "03:00", "Interval": "03:00", "Reminder": "00:15", + "ExpireAt": "25:00", "AudioFile": "audio/Lotus.mp3", "IsManualReset": false, "IsEnabled": true, @@ -92,9 +100,10 @@ "First": "20:00", "Interval": "10:00", "Reminder": "00:45", + "ExpireAt": "35:00", "AudioFile": "audio/tormentor.wav", "IsManualReset": true, - "IsEnabled": true, + "IsEnabled": false, "IsTts": false, "IsSoundEnabled": true }, @@ -104,9 +113,10 @@ "First": "20:00", "Interval": "10:00", "Reminder": "00:45", + "ExpireAt": "35:00", "AudioFile": "audio/tormentor.wav", "IsManualReset": true, - "IsEnabled": true, + "IsEnabled": false, "IsTts": false, "IsSoundEnabled": true }, @@ -119,7 +129,7 @@ "AudioFile": "audio/roshan.wav", "IsManualReset": true, "IsEnabled": true, - "IsTts": true, + "IsTts": false, "IsSoundEnabled": true }, { diff --git a/src/Dota2Helper/App.axaml.cs b/src/Dota2Helper/App.axaml.cs index 90be2d8..25d03bf 100644 --- a/src/Dota2Helper/App.axaml.cs +++ b/src/Dota2Helper/App.axaml.cs @@ -44,18 +44,8 @@ public override async void OnFrameworkInitializationCompleted() desktop.ShutdownRequested += (sender, args) => { - foreach (var listener in Host.Services.GetServices()) - { - try - { - listener.Dispose(); - } - catch (Exception e) - { - Host.Services.GetRequiredService>().LogError(e, "Error disposing listener"); - } - } - + Host.Services.GetRequiredService().Dispose(); + Host.Services.GetRequiredService().Dispose(); Host.Dispose(); Host = null; }; @@ -75,8 +65,19 @@ static IHost CreateHost() builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); - builder.Services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); + + if (Design.IsDesignMode) + { + builder.Services.AddSingleton(); + } + else if (OperatingSystem.IsWindows()) + { + builder.Services.AddSingleton(); + } + else if (OperatingSystem.IsMacOS()) + { + builder.Services.AddSingleton(); + } builder.Logging.ClearProviders(); builder.Logging.AddDebug(); diff --git a/src/Dota2Helper/Core/Audio/AudioPlayer.cs b/src/Dota2Helper/Core/Audio/AudioPlayer.cs index d574c4b..bc2e7a7 100644 --- a/src/Dota2Helper/Core/Audio/AudioPlayer.cs +++ b/src/Dota2Helper/Core/Audio/AudioPlayer.cs @@ -1,19 +1,79 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Runtime.Versioning; using System.Speech.Synthesis; using Dota2Helper.Core.Timers; using LibVLCSharp.Shared; namespace Dota2Helper.Core.Audio; -[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")] +public interface ITextToSpeech : IDisposable +{ + void Speak(string text); + int Volume { get; set; } +} + +public class TextToSpeechMac : ITextToSpeech +{ + public void Speak(string text) + { + throw new PlatformNotSupportedException("macOS is not supported."); + } + + public int Volume { get; set; } + + public void Dispose() + { + } +} + +public class StubTextToSpeech : ITextToSpeech +{ + public void Speak(string text) + { + } + + public int Volume { get; set; } + + public void Dispose() + { + } +} + +[SupportedOSPlatform("windows")] +public class TextToSpeechWindows : ITextToSpeech +{ + readonly SpeechSynthesizer _synthesizer = new(); + + public void Speak(string text) + { + _synthesizer.SpeakAsync(text); + } + + public int Volume + { + set => _synthesizer.Volume = value; + get => _synthesizer.Volume; + } + + public void Dispose() + { + _synthesizer.Dispose(); + } +} + public class AudioPlayer : IDisposable { readonly static LibVLC LibVlc = new(); readonly MediaPlayer _player = new(LibVlc); readonly Queue _queue = new(); - readonly SpeechSynthesizer _synthesizer = new(); + readonly ITextToSpeech _synthesizer; + + public AudioPlayer(ITextToSpeech synthesizer) + { + _synthesizer = synthesizer; + } public void QueueReminder(DotaTimer timer) { @@ -26,18 +86,18 @@ public void QueueReminder(DotaTimer timer) _queue.Enqueue(AudioQueueItem.FromTimer(timer)); } - public int Volume + public int Volume { set { _player.Volume = value; - _synthesizer.Volume = value; + _synthesizer.Volume = value; } get => _player.Volume; } - + public bool HasReminderQueued => _queue.Count > 0; - + public void PlayReminder() { if (!_queue.TryDequeue(out var item)) return; @@ -47,12 +107,12 @@ public void PlayReminder() using (var reminderAudio = new Media(LibVlc, item.Value)) { _player.Play(reminderAudio); - } + } } - + if (item.AudioQueueItemType == AudioQueueItemType.TextToSpeech) { - _synthesizer.SpeakAsync(item.Value); + _synthesizer.Speak(item.Value); } } diff --git a/src/Dota2Helper/Core/BackgroundServices/GameStateService.cs b/src/Dota2Helper/Core/BackgroundServices/GameStateService.cs index da675a4..bb3e8e6 100644 --- a/src/Dota2Helper/Core/BackgroundServices/GameStateService.cs +++ b/src/Dota2Helper/Core/BackgroundServices/GameStateService.cs @@ -39,7 +39,7 @@ await Dispatcher.UIThread.InvokeAsync(() => timers.Do(timer => logger.LogError(ex, "Error updating timer"); } } - ), DispatcherPriority.Background, stoppingToken); + ), DispatcherPriority.Render, stoppingToken); } catch (Exception ex) { diff --git a/src/Dota2Helper/Core/Configuration/TimerOptions.cs b/src/Dota2Helper/Core/Configuration/TimerOptions.cs index 77d64de..9144b49 100644 --- a/src/Dota2Helper/Core/Configuration/TimerOptions.cs +++ b/src/Dota2Helper/Core/Configuration/TimerOptions.cs @@ -18,13 +18,16 @@ public class TimerOptions [JsonRequired] [JsonConverter(typeof(TimeSpanConverter))] public required TimeSpan Interval { get; set; } + + [JsonConverter(typeof(TimeSpanConverter))] + public TimeSpan? ExpireAt { get; set; } [JsonRequired] [JsonConverter(typeof(TimeSpanConverter))] public required TimeSpan Reminder { get; set; } [JsonConverter(typeof(TimeSpanConverter))] - public TimeSpan Offset { get; set; } = TimeSpan.Zero; + public TimeSpan? Offset { get; set; } /// /// The audio file for effect sound diff --git a/src/Dota2Helper/Core/Gsi/GsiConfigService.cs b/src/Dota2Helper/Core/Gsi/GsiConfigService.cs index d901f21..5cd7e68 100644 --- a/src/Dota2Helper/Core/Gsi/GsiConfigService.cs +++ b/src/Dota2Helper/Core/Gsi/GsiConfigService.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; +using Avalonia.Controls; using Microsoft.Extensions.Logging; using Microsoft.Win32; @@ -12,7 +13,7 @@ namespace Dota2Helper.Core.Gsi; public partial class GsiConfigService(ILogger logger) { - const string ConfigFile = "gamestate_integration_d2helper.cfg"; + string ConfigFile => Design.IsDesignMode ? "gamestate_integration_design.cfg" : "gamestate_integration_d2helper.cfg"; string? FindGameStateIntegrationPath() { diff --git a/src/Dota2Helper/Core/Listeners/DotaListener.cs b/src/Dota2Helper/Core/Listeners/DotaListener.cs index 72100ab..5cc13b6 100644 --- a/src/Dota2Helper/Core/Listeners/DotaListener.cs +++ b/src/Dota2Helper/Core/Listeners/DotaListener.cs @@ -34,16 +34,9 @@ public class DotaListener(ILogger logger, IOptions setti if (_listener == null) { - if (!Design.IsDesignMode) - { - _listener = new HttpListener(); - _listener.Prefixes.Add(gsiConfig.GetUri().ToString()); - _listener.Start(); - } - else - { - return null; - } + _listener = new HttpListener(); + _listener.Prefixes.Add(gsiConfig.GetUri().ToString()); + _listener.Start(); } try diff --git a/src/Dota2Helper/Core/Listeners/DynamicListenerStrategy.cs b/src/Dota2Helper/Core/Listeners/DynamicListenerStrategy.cs index 8463eae..270800a 100644 --- a/src/Dota2Helper/Core/Listeners/DynamicListenerStrategy.cs +++ b/src/Dota2Helper/Core/Listeners/DynamicListenerStrategy.cs @@ -1,14 +1,13 @@ -using System.Collections.Generic; -using System.Diagnostics; +using System.Diagnostics; using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Avalonia.Controls; using Microsoft.Extensions.Logging; namespace Dota2Helper.Core.Listeners; -public class DynamicListenerStrategy(ILogger logger, FakeDotaListener fakeDotaListener, DotaListener dotaListener) : IListenerStrategy +public class DynamicListenerStrategy( + ILogger logger, + FakeDotaListener fakeDotaListener, + DotaListener dotaListener) : IListenerStrategy { IDotaListener? _listener; @@ -18,11 +17,6 @@ public class DynamicListenerStrategy(ILogger logger, Fa public void UpdateListener() { - if (Design.IsDesignMode) - { - _listener = fakeDotaListener; - } - if (_dota2 == null) { _dota2 = Process.GetProcessesByName("dota2").FirstOrDefault(); @@ -32,9 +26,11 @@ public void UpdateListener() logger.LogInformation("Dota2 is running, caching detected process for exit event"); _dota2.EnableRaisingEvents = true; + _dota2.Exited += (sender, args) => { logger.LogInformation("Dota2 exited, switching to FakeDotaListener"); + _listener = fakeDotaListener; _dota2 = null; }; diff --git a/src/Dota2Helper/Core/Timers/DotaTimer.cs b/src/Dota2Helper/Core/Timers/DotaTimer.cs index ed93be4..d21e33f 100644 --- a/src/Dota2Helper/Core/Timers/DotaTimer.cs +++ b/src/Dota2Helper/Core/Timers/DotaTimer.cs @@ -5,14 +5,28 @@ namespace Dota2Helper.Core.Timers; public class DotaTimer : ReactiveObject { + TimeSpan? _expireAt; TimeSpan _manualResetTime; + TimeSpan? _offset; + TimeSpan _timeRemaining; + TimeSpan _reminder; + + bool _isEnabled; + bool _isActive; + bool _isReminderActive; + bool _isSoundEnabled; + bool _isSoundPlayed; + bool _isTts; + bool _isVisible; + bool _isExpired; protected internal DotaTimer( string label, TimeSpan first, TimeSpan interval, TimeSpan reminder, - TimeSpan offset, + TimeSpan? offset, + TimeSpan? expireAt, string audioFile, bool isManualReset, string speech, @@ -20,6 +34,7 @@ protected internal DotaTimer( bool isSoundEnabled, bool isEnabled) { + Label = label; First = first; Interval = interval; @@ -28,31 +43,41 @@ protected internal DotaTimer( IsManualReset = isManualReset; Speech = speech; Offset = offset; + ExpireAt = expireAt; IsEnabled = isEnabled; IsSoundEnabled = isSoundEnabled; IsTts = isTts; - IsActive = true; + IsExpired = false; + IsVisible = true; + } + + public bool IsExpired + { + get => _isExpired; + set => this.RaiseAndSetIfChanged(ref _isExpired, value); + } + + public TimeSpan? ExpireAt + { + get => _expireAt; + set => this.RaiseAndSetIfChanged(ref _expireAt, value); } public bool IsManualReset { get; protected init; } public string Speech { get; } - bool _isTts; - public bool IsTts { get => _isTts; set => this.RaiseAndSetIfChanged(ref _isTts, value); } - TimeSpan _reminder; - public TimeSpan Reminder { get => _reminder; - set => this.RaiseAndSetIfChanged(ref _reminder, value); + private set => this.RaiseAndSetIfChanged(ref _reminder, value); } public int ReminderInSeconds @@ -63,11 +88,15 @@ public int ReminderInSeconds public int OffsetInSeconds { - get => (int) Offset.TotalSeconds; + get => (int) (Offset?.TotalSeconds ?? TimeSpan.Zero.TotalSeconds); set => Offset = TimeSpan.FromSeconds(value); } - bool _isEnabled; + public int ExpireInMinutes + { + get => (int) (ExpireAt?.Minutes ?? TimeSpan.Zero.Minutes); + set => ExpireAt = TimeSpan.FromMinutes(value); + } public bool IsEnabled { @@ -75,45 +104,36 @@ public bool IsEnabled set => this.RaiseAndSetIfChanged(ref _isEnabled, value); } - public bool IsReminderActive => TimeRemaining - Reminder <= TimeSpan.Zero; + + public bool IsReminderActive + { + get => _isReminderActive; + set => this.RaiseAndSetIfChanged(ref _isReminderActive, value); + } public string AudioFile { get; } public string Label { get; } public TimeSpan First { get; } public TimeSpan Interval { get; } - TimeSpan _offset; - public TimeSpan Offset + public TimeSpan? Offset { get => _offset; - set - { - this.RaisePropertyChanging(); - _offset = value; - this.RaisePropertyChanged(); - this.RaisePropertyChanged(nameof(IsReminderActive)); - } + private set => this.RaiseAndSetIfChanged(ref _offset, value); } - TimeSpan _timeRemaining; - public TimeSpan TimeRemaining { get => _timeRemaining; private set => this.RaiseAndSetIfChanged(ref _timeRemaining, value); } - bool _isActive; - public bool IsActive { get => _isActive; private set => this.RaiseAndSetIfChanged(ref _isActive, value); } - - bool _isSoundEnabled; - public bool IsSoundEnabled { get => _isSoundEnabled; @@ -122,12 +142,10 @@ public bool IsSoundEnabled public string EnableDisableSoundTooltip => IsSoundEnabled ? "Disable sound" : "Enable sound"; - public bool IsTriggered => TimeRemaining <= TimeSpan.Zero; + bool IsTriggered => TimeRemaining <= TimeSpan.Zero; public bool IsPendingManualReset => !IsActive && IsManualReset; - bool _isSoundPlayed; - bool IsSoundPlayed { get => _isSoundPlayed; @@ -135,6 +153,7 @@ bool IsSoundPlayed } public string EnableDisableTimerTooltip => IsEnabled ? "Disable timer" : "Enable timer"; + public string EnableDisableTtsTooltip => "TTS or Effect"; public event EventHandler? OnReminder; @@ -149,9 +168,9 @@ TimeSpan GetObjectiveTime(TimeSpan gameTime) { var time = gameTime > First ? Interval : First + (gameTime < TimeSpan.Zero ? gameTime.Negate() : TimeSpan.Zero); - if (Offset != TimeSpan.Zero) + if (Offset.HasValue) { - time = time.Add(Offset); + time = time.Add(Offset.Value); } return time; @@ -159,6 +178,15 @@ TimeSpan GetObjectiveTime(TimeSpan gameTime) public void Update(TimeSpan gameTime) { + IsExpired = gameTime >= ExpireAt; + IsVisible = !IsExpired && IsEnabled; + + if (IsExpired || !IsVisible || !IsEnabled) + { + TimeRemaining = TimeSpan.Zero; + return; + } + if (!IsActive) { TimeRemaining = TimeSpan.Zero; @@ -177,13 +205,24 @@ public void Update(TimeSpan gameTime) IsActive = false; } - if (IsSoundEnabled && IsReminderActive && !IsSoundPlayed) + if (IsSoundEnabled && CheckIsReminderActive() && !IsSoundPlayed) { OnReminder?.Invoke(this, EventArgs.Empty); IsSoundPlayed = true; } } + public bool IsVisible + { + get => _isVisible; + set => this.RaiseAndSetIfChanged(ref _isVisible, value); + } + + bool CheckIsReminderActive() + { + return IsReminderActive = TimeRemaining - Reminder <= TimeSpan.Zero; + } + TimeSpan CalculateTimeRemaining(TimeSpan gameTime) { var interval = GetObjectiveTime(gameTime); diff --git a/src/Dota2Helper/Core/Timers/DotaTimers.cs b/src/Dota2Helper/Core/Timers/DotaTimers.cs index 2d349db..cf09904 100644 --- a/src/Dota2Helper/Core/Timers/DotaTimers.cs +++ b/src/Dota2Helper/Core/Timers/DotaTimers.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Linq; using Dota2Helper.Core.Configuration; using Microsoft.Extensions.Options; @@ -17,17 +15,18 @@ public DotaTimers(IOptions settings) foreach (var item in settings.Value.Timers) { timers.Add(new DotaTimer( - item.Label, - item.First, - item.Interval, - item.Reminder, - item.Offset, - item.AudioFile, - item.IsManualReset, - item.Speech, - item.IsTts, - item.IsSoundEnabled, - item.IsEnabled)); + label: item.Label, + first: item.First, + interval: item.Interval, + reminder: item.Reminder, + offset: item.Offset, + expireAt: item.ExpireAt, + audioFile: item.AudioFile, + isManualReset: item.IsManualReset, + speech: item.Speech, + isTts: item.IsTts, + isSoundEnabled: item.IsSoundEnabled, + isEnabled: item.IsEnabled)); } foreach (var timer in timers) @@ -40,7 +39,14 @@ public void Do(Action func) { foreach (var timer in this) { - func(timer); + try + { + func(timer); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + } } } } \ No newline at end of file diff --git a/src/Dota2Helper/ViewModels/MainWindowViewModel.cs b/src/Dota2Helper/ViewModels/MainWindowViewModel.cs index 928fb49..2d5f3ae 100644 --- a/src/Dota2Helper/ViewModels/MainWindowViewModel.cs +++ b/src/Dota2Helper/ViewModels/MainWindowViewModel.cs @@ -8,8 +8,6 @@ public class MainWindowViewModel : ViewModelBase { ViewModelBase _contentViewModel; - - public ViewModelBase ContentViewModel { get => _contentViewModel; diff --git a/src/Dota2Helper/ViewModels/TimersViewModel.cs b/src/Dota2Helper/ViewModels/TimersViewModel.cs index 11c0a99..33f1cf5 100644 --- a/src/Dota2Helper/ViewModels/TimersViewModel.cs +++ b/src/Dota2Helper/ViewModels/TimersViewModel.cs @@ -25,6 +25,7 @@ public class TimersViewModel : ViewModelBase PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }; + public DotaTimers Timers { get; } public TimersViewModel( diff --git a/src/Dota2Helper/ViewModels/WindowExtensions.cs b/src/Dota2Helper/ViewModels/WindowExtensions.cs deleted file mode 100644 index b002930..0000000 --- a/src/Dota2Helper/ViewModels/WindowExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.Controls; - -namespace Dota2Helper.ViewModels; - -internal static class WindowExtensions -{ - const int GWL_STYLE = -16, GWL_EXSTYLE = -20; - const int WS_MAXIMIZEBOX = 0x10000, WS_MINIMIZEBOX = 0x20000, WS_SYSMENU = 0x80000; - const int WS_EX_DLGMODALFRAME = 0x0001; - - [DllImport("user32.dll")] - static extern int GetWindowLong(IntPtr hwnd, int index); - - [DllImport("user32.dll")] - static extern int SetWindowLong(IntPtr hwnd, int index, int value); - - [DllImport("user32.dll")] - static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int cx, int cy, uint uFlags); - - const uint SWP_NOSIZE = 0x0001; - const uint SWP_NOMOVE = 0x0002; - const uint SWP_NOZORDER = 0x0004; - const uint SWP_FRAMECHANGED = 0x0020; - - internal static void HideMinimizeAndMaximizeButtons(this Window window) - { - var platformHandle = window.TryGetPlatformHandle(); - if (platformHandle is not null) - { - var hwnd = platformHandle.Handle; - var currentStyle = GetWindowLong(hwnd, GWL_STYLE); - var currentExStyle = GetWindowLong(hwnd, GWL_EXSTYLE); - - SetWindowLong(hwnd, GWL_STYLE, (currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX)); - SetWindowLong(hwnd, GWL_EXSTYLE, (currentExStyle | WS_EX_DLGMODALFRAME)); - SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); - } - } -} \ No newline at end of file diff --git a/src/Dota2Helper/Views/MainWindow.axaml b/src/Dota2Helper/Views/MainWindow.axaml index d780cc4..e83960c 100644 --- a/src/Dota2Helper/Views/MainWindow.axaml +++ b/src/Dota2Helper/Views/MainWindow.axaml @@ -15,7 +15,6 @@ ExtendClientAreaChromeHints="SystemChrome" Title="D2 Helper" Opened="TopLevel_OnOpened" SizeToContent="WidthAndHeight"> -