diff --git a/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs b/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs
index 5ef033898..7f2d38919 100644
--- a/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs
+++ b/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs
@@ -47,10 +47,14 @@ public class GeneralOptions : INotifyPropertyChanged
[Range(0.0, 1.0)]
public float TrackerConfidenceSassThreshold { get; set; } = 0.92f;
- public byte[] TrackerBGColor { get; set; } = { 0xFF, 0x21, 0x21, 0x21 };
+ public byte[] TrackerBGColor { get; set; } = [0xFF, 0x21, 0x21, 0x21];
public bool TrackerShadows { get; set; } = true;
+ public byte[] TrackerSpeechBGColor { get; set; } = [0xFF, 0x48, 0x3D, 0x8B];
+
+ public bool TrackerSpeechEnableBounce { get; set; } = true;
+
[YamlIgnore]
public int LaunchButton { get; set; } = (int)LaunchButtonOptions.PlayAndTrack;
@@ -172,6 +176,11 @@ public string? TwitchId
///
public bool DisplayMsuTrackWindow { get; set; }
+ ///
+ /// If the tracker speech window should open by default
+ ///
+ public bool DisplayTrackerSpeechWindow { get; set; }
+
///
/// Check for new releases on GitHub on startup
///
diff --git a/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowTrackerOptions.cs b/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowTrackerOptions.cs
index c5b91dd0b..274c9a5b2 100644
--- a/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowTrackerOptions.cs
+++ b/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowTrackerOptions.cs
@@ -18,6 +18,12 @@ public class OptionsWindowTrackerOptions
[DynamicFormFieldCheckBox(checkBoxText: "Render shadows", alignment: DynamicFormAlignment.Right)]
public bool TrackerShadows { get; set; } = true;
+ [DynamicFormFieldColorPicker(label: "Tracker speech window color:")]
+ public byte[] TrackerSpeechBGColor { get; set; } = [0xFF, 0x48, 0x3D, 0x8B];
+
+ [DynamicFormFieldCheckBox(checkBoxText: "Enable speech bounce animation", alignment: DynamicFormAlignment.Right)]
+ public bool TrackerSpeechEnableBounce { get; set; } = true;
+
[DynamicFormFieldSlider(minimumValue: 0, maximumValue:100, decimalPlaces:1, incrementAmount:.1, suffix:"%", label: "Tracker recognition threshold:", platforms: DynamicFormPlatform.Windows)]
public float TrackerRecognitionThreshold { get; set; }
diff --git a/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowViewModel.cs b/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowViewModel.cs
index 1a8eb7686..111abdf56 100644
--- a/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowViewModel.cs
+++ b/src/TrackerCouncil.Smz3.Data/ViewModels/OptionsWindowViewModel.cs
@@ -34,6 +34,8 @@ public OptionsWindowViewModel(GeneralOptions options, Dictionary
TrackerOptions.TrackerBGColor = options.TrackerBGColor;
TrackerOptions.TrackerShadows = options.TrackerShadows;
+ TrackerOptions.TrackerSpeechBGColor = options.TrackerSpeechBGColor;
+ TrackerOptions.TrackerSpeechEnableBounce = options.TrackerSpeechEnableBounce;
TrackerOptions.TrackerRecognitionThreshold = options.TrackerRecognitionThreshold * 100;
TrackerOptions.TrackerConfidenceThreshold = options.TrackerConfidenceThreshold * 100;
TrackerOptions.TrackerConfidenceSassThreshold = options.TrackerConfidenceSassThreshold * 100;
@@ -84,6 +86,8 @@ public void UpdateOptions(GeneralOptions options)
options.TrackerBGColor = TrackerOptions.TrackerBGColor;
options.TrackerShadows = TrackerOptions.TrackerShadows;
+ options.TrackerSpeechBGColor = TrackerOptions.TrackerSpeechBGColor;
+ options.TrackerSpeechEnableBounce = TrackerOptions.TrackerSpeechEnableBounce;
options.TrackerRecognitionThreshold = TrackerOptions.TrackerRecognitionThreshold / 100;
options.TrackerConfidenceThreshold = TrackerOptions.TrackerConfidenceThreshold / 100;
options.TrackerConfidenceSassThreshold = TrackerOptions.TrackerConfidenceSassThreshold / 100;
diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/ICommunicator.cs b/src/TrackerCouncil.Smz3.Tracking/Services/ICommunicator.cs
index 0b45e4888..4b9afbcae 100644
--- a/src/TrackerCouncil.Smz3.Tracking/Services/ICommunicator.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/Services/ICommunicator.cs
@@ -1,4 +1,5 @@
using System;
+using System.Speech.Synthesis;
namespace TrackerCouncil.Smz3.Tracking.Services;
@@ -69,10 +70,20 @@ public void SlowDown() { }
///
public bool IsSpeaking { get; }
+ ///
+ /// Event for when the communicator has started speaking
+ ///
+ public event EventHandler SpeakStarted;
+
///
/// Event for when the communicator has finished speaking
///
public event EventHandler SpeakCompleted;
+ ///
+ /// Event for when the communicator has reached a new viseme
+ ///
+ public event EventHandler VisemeReached;
+
}
diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/IUIService.cs b/src/TrackerCouncil.Smz3.Tracking/Services/IUIService.cs
index 0b8421650..e2c37caa5 100644
--- a/src/TrackerCouncil.Smz3.Tracking/Services/IUIService.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/Services/IUIService.cs
@@ -74,4 +74,12 @@ public interface IUIService
/// The base path of the desired sprite
/// The full path of the sprite or null if it's not found
public string? GetSpritePath(string category, string imageFileName, out string? profilePath, string? basePath = null);
+
+ ///
+ /// Gets the images for tracker talking
+ ///
+ /// The selected profile
+ /// The base path of the folder used
+ /// A dictionary of all of the available tracker speech images
+ public Dictionary GetTrackerSpeechSprites(out string? profilePath, string? basePath = null);
}
diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/TextToSpeechCommunicator.cs b/src/TrackerCouncil.Smz3.Tracking/Services/TextToSpeechCommunicator.cs
index 313a55d21..7a91fc73d 100644
--- a/src/TrackerCouncil.Smz3.Tracking/Services/TextToSpeechCommunicator.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/Services/TextToSpeechCommunicator.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Speech.Synthesis;
using Microsoft.Extensions.Logging;
using TrackerCouncil.Smz3.Data.Options;
@@ -34,6 +35,7 @@ public TextToSpeechCommunicator(TrackerOptionsAccessor trackerOptionsAccessor, I
if (IsSpeaking) return;
_startSpeakingTime = DateTime.Now;
IsSpeaking = true;
+ SpeakStarted?.Invoke(this, EventArgs.Empty);
};
_tts.SpeakCompleted += (sender, args) =>
@@ -44,6 +46,12 @@ public TextToSpeechCommunicator(TrackerOptionsAccessor trackerOptionsAccessor, I
SpeakCompleted?.Invoke(this, new SpeakCompletedEventArgs(duration));
};
+ _tts.VisemeReached += (sender, args) =>
+ {
+ if (!OperatingSystem.IsWindows()) return;
+ VisemeReached?.Invoke(this, args);
+ };
+
_canSpeak = trackerOptionsAccessor.Options?.VoiceFrequency != Shared.Enums.TrackerVoiceFrequency.Disabled;
}
@@ -143,8 +151,12 @@ public void SlowDown()
public bool IsSpeaking { get; private set; }
+ public event EventHandler? SpeakStarted;
+
public event EventHandler? SpeakCompleted;
+ public event EventHandler? VisemeReached;
+
///
public void Dispose()
{
diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/UIService.cs b/src/TrackerCouncil.Smz3.Tracking/Services/UIService.cs
index 3dc8d0400..5b83f6928 100644
--- a/src/TrackerCouncil.Smz3.Tracking/Services/UIService.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/Services/UIService.cs
@@ -165,4 +165,80 @@ UIConfig uiConfig
profilePath = null;
return null;
}
+
+ ///
+ /// Gets the images for tracker talking
+ ///
+ /// The selected profile
+ /// The base path of the folder used
+ /// A dictionary of all of the available tracker speech images
+ public Dictionary GetTrackerSpeechSprites(out string? profilePath, string? basePath = null)
+ {
+ var toReturn = new Dictionary();
+
+ foreach (var idleSprite in GetCategorySprites("Tracker", out profilePath, basePath).Where(x => x.EndsWith("_idle.png")))
+ {
+ var file = new FileInfo(idleSprite);
+
+ var reaction = file.Name.Replace("_idle.png", "").ToLower();
+ var talkSprite = idleSprite.Replace("_idle.png", "_talk.png");
+
+ if (File.Exists(talkSprite))
+ {
+ toReturn.Add(reaction, new TrackerSpeechImages()
+ {
+ ReactionName = reaction,
+ IdleImage = idleSprite,
+ TalkingImage = talkSprite,
+ });
+ }
+ }
+
+ return toReturn;
+ }
+
+ ///
+ /// Returns all of the sprites for a category
+ ///
+ /// The category of sprite
+ /// The path of the selected profile
+ /// The base path of the desired sprite
+ /// The full path of the sprite or null if it's not found
+ public List GetCategorySprites(string category, out string? profilePath, string? basePath = null)
+ {
+ var toReturn = new List();
+
+ if (!string.IsNullOrEmpty(basePath))
+ {
+ var path = Path.Combine(basePath, category);
+ if (Directory.Exists(path))
+ {
+ foreach (var spritePath in Directory.EnumerateFiles(path, "*.png"))
+ {
+ toReturn.Add(spritePath);
+ }
+ profilePath = basePath;
+ return toReturn;
+ }
+ }
+ else
+ {
+ foreach (var profile in _iconPaths)
+ {
+ var path = Path.Combine(profile, category);
+ if (Directory.Exists(path))
+ {
+ foreach (var spritePath in Directory.EnumerateFiles(path, "*.png", new EnumerationOptions() { MatchCasing = MatchCasing.CaseInsensitive }))
+ {
+ toReturn.Add(spritePath);
+ }
+ profilePath = profile;
+ return toReturn;
+ }
+ }
+ }
+
+ profilePath = null;
+ return toReturn;
+ }
}
diff --git a/src/TrackerCouncil.Smz3.Tracking/TrackerSpeechImages.cs b/src/TrackerCouncil.Smz3.Tracking/TrackerSpeechImages.cs
new file mode 100644
index 000000000..ed9eba3b2
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Tracking/TrackerSpeechImages.cs
@@ -0,0 +1,8 @@
+namespace TrackerCouncil.Smz3.Tracking;
+
+public class TrackerSpeechImages
+{
+ public required string ReactionName { get; set; }
+ public required string IdleImage { get; set; }
+ public required string TalkingImage { get; set; }
+}
diff --git a/src/TrackerCouncil.Smz3.UI/Services/TrackerSpeechWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/TrackerSpeechWindowService.cs
new file mode 100644
index 000000000..d5dad3ae9
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.UI/Services/TrackerSpeechWindowService.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia;
+using Avalonia.Media;
+using Avalonia.Threading;
+using AvaloniaControls.ControlServices;
+using TrackerCouncil.Smz3.Data.Options;
+using TrackerCouncil.Smz3.Tracking;
+using TrackerCouncil.Smz3.Tracking.Services;
+using TrackerCouncil.Smz3.UI.ViewModels;
+
+namespace TrackerCouncil.Smz3.UI.Services;
+
+public class TrackerSpeechWindowService(ICommunicator communicator, IUIService uiService, OptionsFactory optionsFactory) : ControlService
+{
+ TrackerSpeechWindowViewModel _model = new();
+
+ private DispatcherTimer _dispatcherTimer = new()
+ {
+ Interval = TimeSpan.FromSeconds(1.0 / 60),
+ };
+
+ private TrackerSpeechImages? _currentSpeechImages;
+ private Dictionary _availableSpeechImages = [];
+ private int _tickCount;
+ private readonly int _maxTicks = 12;
+ private readonly double _bounceHeight = 6;
+ private int _prevViseme;
+ private bool _enableBounce;
+
+ public TrackerSpeechWindowViewModel GetViewModel()
+ {
+ _availableSpeechImages = uiService.GetTrackerSpeechSprites(out _);
+ SetSpeechImages("default");
+
+ var options = optionsFactory.Create();
+ var bytes = options.GeneralOptions.TrackerSpeechBGColor;
+ _enableBounce = options.GeneralOptions.TrackerSpeechEnableBounce;
+ _model.Background = new SolidColorBrush(Color.FromArgb(bytes[0], bytes[1], bytes[2], bytes[3]));
+
+ if (_currentSpeechImages == null)
+ {
+ return new TrackerSpeechWindowViewModel();
+ }
+
+ _model.TrackerImage = _currentSpeechImages.IdleImage;
+
+ if (_enableBounce)
+ {
+ _model.AnimationMargin = new Thickness(0, 0, 0, -1 * _bounceHeight);
+
+ _dispatcherTimer.Tick += (sender, args) =>
+ {
+ _tickCount++;
+ var fraction = Math.Clamp(1.0 * _tickCount / _maxTicks, 0, 1);
+
+ if (fraction < 0.5)
+ {
+ _model.AnimationMargin = new Thickness(0, 0, 0, -1 * _bounceHeight + fraction * 2 * _bounceHeight);
+ }
+ else
+ {
+ _model.AnimationMargin = new Thickness(0, 0, 0, (fraction - 0.5) * 2 * -1 * _bounceHeight);
+ }
+
+ if (fraction >= 1)
+ {
+ _dispatcherTimer.Stop();
+ }
+ };
+ }
+
+ SaveOpenStatus(true);
+
+ communicator.SpeakCompleted += Communicator_SpeakCompleted;
+ communicator.VisemeReached += Communicator_VisemeReached;
+ return _model;
+ }
+
+ public void StopTimer()
+ {
+ _dispatcherTimer.Stop();
+ }
+
+ public void SaveOpenStatus(bool isOpen)
+ {
+ var options = optionsFactory.Create();
+ if (options.GeneralOptions.DisplayTrackerSpeechWindow == isOpen)
+ {
+ return;
+ }
+ options.GeneralOptions.DisplayTrackerSpeechWindow = isOpen;
+ options.Save();
+ }
+
+ private void Communicator_VisemeReached(object? sender, System.Speech.Synthesis.VisemeReachedEventArgs e)
+ {
+ if (!OperatingSystem.IsWindows()) return;
+
+ if (e.Viseme == 0)
+ {
+ _model.TrackerImage = _currentSpeechImages?.IdleImage;
+ }
+ else
+ {
+ if (_enableBounce && _prevViseme == 0 && !_dispatcherTimer.IsEnabled)
+ {
+ _tickCount = 0;
+ _dispatcherTimer.Start();
+ }
+ _model.TrackerImage = _currentSpeechImages?.TalkingImage;
+ }
+
+ _prevViseme = e.Viseme;
+ }
+
+ private void Communicator_SpeakCompleted(object? sender, SpeakCompletedEventArgs e)
+ {
+ _model.TrackerImage = _currentSpeechImages?.IdleImage;
+ _prevViseme = 0;
+ }
+
+ private void SetSpeechImages(string reaction)
+ {
+ if (_availableSpeechImages.TryGetValue(reaction.ToLower(), out var requestedSpeechImage))
+ {
+ _currentSpeechImages = requestedSpeechImage;
+ }
+ else if (_availableSpeechImages.TryGetValue("default", out var defaultSpeechImage))
+ {
+ _currentSpeechImages = defaultSpeechImage;
+ }
+ else
+ {
+ _currentSpeechImages = _availableSpeechImages.Values.FirstOrDefault();
+ }
+ }
+
+ public string GetBackgroundHex()
+ {
+ var color = _model.Background.Color;
+ return "#" + BitConverter.ToString([color.R, color.G, color.B]).Replace("-", string.Empty);
+ }
+}
+
diff --git a/src/TrackerCouncil.Smz3.UI/Services/TrackerWindowService.cs b/src/TrackerCouncil.Smz3.UI/Services/TrackerWindowService.cs
index 26e775cef..f5997327d 100644
--- a/src/TrackerCouncil.Smz3.UI/Services/TrackerWindowService.cs
+++ b/src/TrackerCouncil.Smz3.UI/Services/TrackerWindowService.cs
@@ -60,6 +60,7 @@ public TrackerWindowViewModel GetViewModel(TrackerWindow parent)
var bytes = Options.GeneralOptions.TrackerBGColor;
_model.Background = new SolidColorBrush(Color.FromArgb(bytes[0], bytes[1], bytes[2], bytes[3]));
_model.OpenTrackWindow = Options.GeneralOptions.DisplayMsuTrackWindow;
+ _model.OpenSpeechWindow = Options.GeneralOptions.DisplayTrackerSpeechWindow;
_model.AddShadows = Options.GeneralOptions.TrackerShadows;
_model.DisplayTimer = Options.GeneralOptions.TrackerTimerEnabled;
@@ -520,6 +521,13 @@ public void OpenTrackerHelpWindow()
_trackerHelpWindow.Closed += (_, _) => _trackerHelpWindow = null;
}
+ public TrackerSpeechWindow OpenTrackerSpeechWindow()
+ {
+ var window = serviceProvider.GetRequiredService();
+ window.Show(_window);
+ return window;
+ }
+
private TrackerWindowPanelViewModel? GetPanelViewModel(UIGridLocation? gridLocation)
{
return gridLocation?.Type switch
diff --git a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj
index 17260e3ef..5d12887c7 100644
--- a/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj
+++ b/src/TrackerCouncil.Smz3.UI/TrackerCouncil.Smz3.UI.csproj
@@ -6,7 +6,7 @@
true
app.manifest
true
- 9.8.3
+ 9.8.4
SMZ3CasRandomizer
Assets\smz3.ico
$(MSBuildProjectName.Replace(" ", "_"))
diff --git a/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerSpeechWindowViewModel.cs b/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerSpeechWindowViewModel.cs
new file mode 100644
index 000000000..140bd440c
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerSpeechWindowViewModel.cs
@@ -0,0 +1,12 @@
+using Avalonia;
+using Avalonia.Media;
+using ReactiveUI.Fody.Helpers;
+
+namespace TrackerCouncil.Smz3.UI.ViewModels;
+
+public class TrackerSpeechWindowViewModel : ViewModelBase
+{
+ [Reactive] public string? TrackerImage { get; set; }
+ [Reactive] public Thickness AnimationMargin { get; set; } = new(0, 0, 0, 0);
+ [Reactive] public SolidColorBrush Background { get; set; } = new(new Color(0xFF, 0x48, 0x3D, 0x8B));
+}
diff --git a/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerWindowViewModel.cs b/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerWindowViewModel.cs
index c5c9cca0d..ca7e74bec 100644
--- a/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerWindowViewModel.cs
+++ b/src/TrackerCouncil.Smz3.UI/ViewModels/TrackerWindowViewModel.cs
@@ -84,6 +84,8 @@ public class TrackerWindowViewModel : ViewModelBase
public bool OpenTrackWindow { get; set; }
+ public bool OpenSpeechWindow { get; set; }
+
public UILayout? PrevLayout { get; set; }
public UILayout? CurrentLayout { get; set; }
diff --git a/src/TrackerCouncil.Smz3.UI/Views/TrackerSpeechWindow.axaml b/src/TrackerCouncil.Smz3.UI/Views/TrackerSpeechWindow.axaml
new file mode 100644
index 000000000..e11d643c6
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.UI/Views/TrackerSpeechWindow.axaml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/TrackerCouncil.Smz3.UI/Views/TrackerSpeechWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/TrackerSpeechWindow.axaml.cs
new file mode 100644
index 000000000..3a8a1ea5a
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.UI/Views/TrackerSpeechWindow.axaml.cs
@@ -0,0 +1,55 @@
+using System.IO;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using AvaloniaControls.Controls;
+using AvaloniaControls.Extensions;
+using TrackerCouncil.Smz3.UI.Services;
+using TrackerCouncil.Smz3.UI.ViewModels;
+
+namespace TrackerCouncil.Smz3.UI;
+
+public partial class TrackerSpeechWindow : RestorableWindow
+{
+ private readonly TrackerSpeechWindowService? _service;
+ private bool _isShuttingDown;
+
+ public TrackerSpeechWindow()
+ {
+ InitializeComponent();
+ DataContext = new TrackerSpeechWindowViewModel();
+ }
+
+ public TrackerSpeechWindow(TrackerSpeechWindowService service)
+ {
+ _service = service;
+ InitializeComponent();
+ DataContext = service.GetViewModel();
+ }
+
+ public void Close(bool isShuttingDown)
+ {
+ _isShuttingDown = isShuttingDown;
+ Close();
+ }
+
+ protected override void OnClosing(WindowClosingEventArgs e)
+ {
+ _service?.StopTimer();
+ if (!_isShuttingDown && e.CloseReason != WindowCloseReason.OwnerWindowClosing && e.CloseReason != WindowCloseReason.ApplicationShutdown && e.CloseReason != WindowCloseReason.OSShutdown)
+ {
+ _service?.SaveOpenStatus(false);
+ }
+ base.OnClosing(e);
+ }
+
+ protected override string RestoreFilePath => Path.Combine(Directories.AppDataFolder, "Windows", "speech-window.json");
+
+ protected override int DefaultWidth => 250;
+
+ protected override int DefaultHeight => 250;
+
+ private async void MenuItem_OnClick(object? sender, RoutedEventArgs e)
+ {
+ await this.SetClipboardAsync(_service?.GetBackgroundHex());
+ }
+}
diff --git a/src/TrackerCouncil.Smz3.UI/Views/TrackerWindow.axaml b/src/TrackerCouncil.Smz3.UI/Views/TrackerWindow.axaml
index 34d069a0d..667866160 100644
--- a/src/TrackerCouncil.Smz3.UI/Views/TrackerWindow.axaml
+++ b/src/TrackerCouncil.Smz3.UI/Views/TrackerWindow.axaml
@@ -39,6 +39,9 @@
+
diff --git a/src/TrackerCouncil.Smz3.UI/Views/TrackerWindow.axaml.cs b/src/TrackerCouncil.Smz3.UI/Views/TrackerWindow.axaml.cs
index 663443d2c..baf63c6a3 100644
--- a/src/TrackerCouncil.Smz3.UI/Views/TrackerWindow.axaml.cs
+++ b/src/TrackerCouncil.Smz3.UI/Views/TrackerWindow.axaml.cs
@@ -26,13 +26,18 @@ public partial class TrackerWindow : RestorableWindow
private GeneratedRom? _generatedRom;
private readonly TrackerWindowViewModel _model;
private CurrentTrackWindow? _currentTrackWindow;
+ private TrackerSpeechWindow? _currentTrackerSpeechWindow;
private Dictionary _layoutImages = new();
private MainWindow? _parentWindow;
public TrackerWindow()
{
InitializeComponent();
- DataContext = _model = new TrackerWindowViewModel();
+ DataContext = _model = new TrackerWindowViewModel()
+ {
+ Panels = [ new TrackerWindowPanelViewModel() ],
+ Layouts = [ new UILayout() ]
+ };
_parentWindow = MessageWindow.GlobalParentWindow as MainWindow;
}
@@ -82,6 +87,17 @@ private void OpenCurrentTrackWindow()
_currentTrackWindow.Closed += (_, _) => _currentTrackWindow = null;
}
+ private void OpenTrackerSpeechWindow()
+ {
+ if (_currentTrackerSpeechWindow != null || _service == null)
+ {
+ return;
+ }
+
+ _currentTrackerSpeechWindow = _service.OpenTrackerSpeechWindow();
+ _currentTrackerSpeechWindow.Closed += (_, _) => _currentTrackerSpeechWindow = null;
+ }
+
private void CreateLayout()
{
MainPanel.Children.Clear();
@@ -156,6 +172,10 @@ private void Control_OnLoaded(object? sender, RoutedEventArgs e)
{
OpenCurrentTrackWindow();
}
+ if (_model.OpenSpeechWindow)
+ {
+ _service?.OpenTrackerSpeechWindow();
+ }
_service?.OpenTrackerLocationsWindow();
_service?.OpenTrackerMapWindow();
}, DispatcherPriority.Background);
@@ -208,6 +228,9 @@ private async void Window_OnClosing(object? sender, WindowClosingEventArgs e)
{
if (_service == null) return;
+ _currentTrackWindow?.Close(true);
+ _currentTrackerSpeechWindow?.Close(true);
+
if (!_finishedShutdown)
{
e.Cancel = true;
@@ -215,8 +238,6 @@ private async void Window_OnClosing(object? sender, WindowClosingEventArgs e)
await _service.Shutdown();
}
- _currentTrackWindow?.Close(true);
-
_parentWindow?.Reload();
_parentWindow?.Show();
}
@@ -275,6 +296,11 @@ private void TrackerHelpMenuItem_OnClick(object? sender, RoutedEventArgs e)
_service?.OpenTrackerHelpWindow();
}
+ private void TrackerSpeechWindowMenuItem_OnClick(object? sender, RoutedEventArgs e)
+ {
+ _service?.OpenTrackerSpeechWindow();
+ }
+
private void SpeechRecognition_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
var point = e.GetCurrentPoint(sender as Control);