Skip to content

Commit

Permalink
Add a speaker button to play audio for the selected primary spelling …
Browse files Browse the repository at this point in the history
…and reading pair when in mining mode
  • Loading branch information
rampaa committed Feb 11, 2024
1 parent a86ea5d commit 7144ce2
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 26 deletions.
2 changes: 1 addition & 1 deletion JL.Core/Utilities/JapaneseUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public static class JapaneseUtils
{ '{', '}' }
};

private static readonly Dictionary<char, char> s_rightToLeftBracketDict = s_leftToRightBracketDict.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
private static readonly Dictionary<char, char> s_rightToLeftBracketDict = s_leftToRightBracketDict.ToDictionary(static kvp => kvp.Value, static kvp => kvp.Key);

private static readonly HashSet<char> s_brackets = s_leftToRightBracketDict.Keys.Union(s_leftToRightBracketDict.Values).ToHashSet();

Expand Down
66 changes: 41 additions & 25 deletions JL.Windows/GUI/PopupWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
using System.Windows.Media;
using Caching;
using JL.Core;
using JL.Core.Audio;
using JL.Core.Dicts;
using JL.Core.Lookup;
using JL.Core.Mining;
using JL.Core.Utilities;
using JL.Windows.GUI.UserControls;
using JL.Windows.SpeechSynthesis;
using JL.Windows.Utilities;
using NAudio.Wave;
using Timer = System.Timers.Timer;

namespace JL.Windows.GUI;
Expand Down Expand Up @@ -57,10 +55,6 @@ internal sealed partial class PopupWindow : Window

public bool MiningMode { get; private set; }

private static string? s_primarySpellingOfLastPlayedAudio = null;

private static string? s_readingOfLastPlayedAudio = null;

public static Timer PopupAutoHideTimer { get; } = new();

public static LRUCache<string, StackPanel[]> StackPanelCache { get; } = new(Utils.CacheSize, Utils.CacheSize / 10);
Expand Down Expand Up @@ -631,6 +625,26 @@ public StackPanel PrepareResultStackPanel(LookupResult result, int index, int re
}
}

if (MiningMode)
{
Button audioButton = new()
{
Content = "🔊",
Foreground = ConfigManager.DefinitionsColor,
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(5, 0, 0, 0),
HorizontalAlignment = HorizontalAlignment.Left,
Background = Brushes.Transparent,
Cursor = Cursors.Arrow,
BorderThickness = new Thickness(0),
Padding = new Thickness(0)
};

audioButton.Click += AudioButton_Click;

_ = top.Children.Add(audioButton);
}

if (result.AlternativeSpellings is not null)
{
string alternativeSpellingsText = showAOrthographyInfo && result.AlternativeSpellingsOrthographyInfoList is not null
Expand Down Expand Up @@ -1022,6 +1036,7 @@ private async void TextBox_MouseMove(object sender, MouseEventArgs? e)
|| ConfigManager.LookupOnMouseClickOnly
|| e?.LeftButton is MouseButtonState.Pressed
|| PopupContextMenu.IsVisible
|| ReadingsAudioWindow.IsItVisible()
|| (ConfigManager.RequireLookupKeyPress
&& !KeyGestureUtils.CompareKeyGesture(ConfigManager.LookupKeyKeyGesture)))
{
Expand Down Expand Up @@ -1051,6 +1066,19 @@ private async void TextBox_MouseMove(object sender, MouseEventArgs? e)
}
}

private async void AudioButton_Click(object sender, RoutedEventArgs e)
{
LookupResult lookupResult = LastLookupResults[_listViewItemIndex];
if (lookupResult.Readings is null || lookupResult.Readings.Length is 1)
{
await PopupWindowUtils.PlayAudio(lookupResult.PrimarySpelling, lookupResult.Readings?[0]).ConfigureAwait(false);
}
else
{
ReadingsAudioWindow.Show(lookupResult.PrimarySpelling, lookupResult.Readings, this);
}
}

private async void PrimarySpelling_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == ConfigManager.CopyPrimarySpellingToClipboardMouseButton)
Expand Down Expand Up @@ -1329,14 +1357,7 @@ public async Task HandleHotKey(KeyGesture keyGesture)
}
}

if (nextButton is not null)
{
ClickDictTypeButton(nextButton);
}
else
{
ClickDictTypeButton(_buttonAll);
}
ClickDictTypeButton(nextButton ?? _buttonAll);
}

else if (KeyGestureUtils.CompareKeyGestures(keyGesture, ConfigManager.PreviousDictKeyGesture))
Expand Down Expand Up @@ -1486,17 +1507,7 @@ private async Task PlayAudio()
string primarySpelling = lastLookupResult.PrimarySpelling;
string? reading = lastLookupResult.Readings?[0];

if (WindowsUtils.AudioPlayer?.PlaybackState is PlaybackState.Playing
&& s_primarySpellingOfLastPlayedAudio == primarySpelling
&& s_readingOfLastPlayedAudio == reading)
{
return;
}

s_primarySpellingOfLastPlayedAudio = primarySpelling;
s_readingOfLastPlayedAudio = reading;

await AudioUtils.GetAndPlayAudio(primarySpelling, reading).ConfigureAwait(false);
await PopupWindowUtils.PlayAudio(primarySpelling, reading).ConfigureAwait(false);
}

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
Expand Down Expand Up @@ -1576,6 +1587,7 @@ private void OnMouseLeave(object sender, MouseEventArgs e)
if (ConfigManager.AutoHidePopupIfMouseIsNotOverIt)
{
if (PopupContextMenu.IsVisible
|| ReadingsAudioWindow.IsItVisible()
|| AddWordWindow.IsItVisible()
|| AddNameWindow.IsItVisible())
{
Expand Down Expand Up @@ -1689,6 +1701,8 @@ private void PopupContextMenu_IsVisibleChanged(object sender, DependencyProperty

private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ReadingsAudioWindow.HideWindow();

if (ChildPopupWindow is { MiningMode: true })
{
if (e.ChangedButton is not MouseButton.Right)
Expand Down Expand Up @@ -1724,6 +1738,8 @@ public void HidePopup()
_ = mainWindow.ChangeVisibility().ConfigureAwait(true);
}

ReadingsAudioWindow.HideWindow();

if (!IsVisible)
{
return;
Expand Down
26 changes: 26 additions & 0 deletions JL.Windows/GUI/ReadingsAudioWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Window x:Class="JL.Windows.GUI.ReadingsAudioWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="ReadingsAudioWindow" SizeToContent="WidthAndHeight" WindowStyle="None" Topmost="True"
ShowInTaskbar="False" AllowsTransparency="True" Focusable="False" x:ClassModifier="internal">
<Grid>
<ListView x:Name="ReadingsListView" VirtualizingStackPanel.VirtualizationMode="Recycling" Focusable="False"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
VirtualizingPanel.IsVirtualizingWhenGrouping="True" VirtualizingPanel.ScrollUnit="Pixel"
BorderThickness="0" ScrollViewer.CanContentScroll="True" VirtualizingPanel.IsVirtualizing="True"
ScrollViewer.VerticalScrollBarVisibility="Auto" VirtualizingPanel.IsContainerVirtualizable="True"
HorizontalContentAlignment="Stretch"
Background="{Binding Path=Background, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
Opacity="70">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Focusable" Value="False" />
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ReadingsListView_PreviewMouseLeftButtonDown" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
</Window>
129 changes: 129 additions & 0 deletions JL.Windows/GUI/ReadingsAudioWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using JL.Windows.Utilities;

namespace JL.Windows.GUI;
/// <summary>
/// Interaction logic for ReadingsAudioWindow.xaml
/// </summary>
internal sealed partial class ReadingsAudioWindow : Window
{
private static string? s_primarySpelling;
private nint _windowHandle;

private static ReadingsAudioWindow? s_instance;

private ReadingsAudioWindow()
{
InitializeComponent();
}

public static bool IsItVisible()
{
return s_instance?.IsVisible ?? false;
}

public static void Show(string primarySpelling, string[] readings, Window window)
{
s_primarySpelling = primarySpelling;
ReadingsAudioWindow currentInstance = s_instance ??= new ReadingsAudioWindow();
currentInstance.ReadingsListView.ItemsSource = readings;
currentInstance.Background = ConfigManager.PopupBackgroundColor;
currentInstance.Foreground = ConfigManager.DefinitionsColor;
currentInstance.FontFamily = ConfigManager.PopupFont;
currentInstance.Show();
currentInstance.UpdatePosition(window.PointToScreen(Mouse.GetPosition(window)));
WinApi.BringToFront(currentInstance._windowHandle);
_ = currentInstance.Focus();
}

protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_windowHandle = new WindowInteropHelper(this).Handle;
}

protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);

if (ConfigManager.Focusable)
{
WinApi.AllowActivation(_windowHandle);
}
else
{
WinApi.PreventActivation(_windowHandle);
}
}

private async void ReadingsListView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
string selectedReading = (string)((ListViewItem)sender).Content;
Hide();
await PopupWindowUtils.PlayAudio(s_primarySpelling!, selectedReading).ConfigureAwait(false);
}

private void UpdatePosition(Point cursorPosition)
{
double mouseX = cursorPosition.X / WindowsUtils.Dpi.DpiScaleX;
double mouseY = cursorPosition.Y / WindowsUtils.Dpi.DpiScaleY;

bool needsFlipX = (mouseX + ActualWidth) > (WindowsUtils.ActiveScreen.Bounds.X + WindowsUtils.DpiAwareWorkAreaWidth);
bool needsFlipY = (mouseY + ActualHeight) > (WindowsUtils.ActiveScreen.Bounds.Y + WindowsUtils.DpiAwareWorkAreaHeight);

double newLeft;
double newTop;

if (needsFlipX)
{
// flip Leftwards while preventing -OOB
newLeft = mouseX - ActualWidth - 5;
if (newLeft < WindowsUtils.ActiveScreen.Bounds.X)
{
newLeft = WindowsUtils.ActiveScreen.Bounds.X;
}
}
else
{
// no flip
newLeft = mouseX - 5;
}

if (needsFlipY)
{
// flip Upwards while preventing -OOB
newTop = mouseY - (ActualHeight + 15);
if (newTop < WindowsUtils.ActiveScreen.Bounds.Y)
{
newTop = WindowsUtils.ActiveScreen.Bounds.Y;
}
}
else
{
// no flip
newTop = mouseY + 15;
}

// stick to edges if +OOB
if ((newLeft + ActualWidth) > (WindowsUtils.ActiveScreen.Bounds.X + WindowsUtils.DpiAwareWorkAreaWidth))
{
newLeft = WindowsUtils.ActiveScreen.Bounds.X + WindowsUtils.DpiAwareWorkAreaWidth - ActualWidth;
}

if ((newTop + ActualHeight) > (WindowsUtils.ActiveScreen.Bounds.Y + WindowsUtils.DpiAwareWorkAreaHeight))
{
newTop = WindowsUtils.ActiveScreen.Bounds.Y + WindowsUtils.DpiAwareWorkAreaHeight - ActualHeight;
}

Left = newLeft;
Top = newTop;
}

public static void HideWindow()
{
s_instance?.Hide();
}
}
20 changes: 20 additions & 0 deletions JL.Windows/Utilities/PopupWindowUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using JL.Core.Audio;
using JL.Core.Dicts;
using JL.Core.Dicts.PitchAccent;
using JL.Core.Utilities;
using JL.Windows.GUI;
using JL.Windows.GUI.UserControls;
using NAudio.Wave;

namespace JL.Windows.Utilities;

internal static class PopupWindowUtils
{
private static string? s_primarySpellingOfLastPlayedAudio = null;

private static string? s_readingOfLastPlayedAudio = null;
public static DoubleCollection StrokeDashArray { get; set; } = new() { 1, 1 };

public static TextBlock CreateTextBlock(string name, string text, Brush foregroundBrush, double fontSize, ContextMenu contextMenu, VerticalAlignment verticalAlignment, Thickness margin)
Expand Down Expand Up @@ -249,4 +254,19 @@ public static void ShowMiningModeResults(PopupWindow popupWindow)
SetPopupAutoHideTimer();
}
}

public static async Task PlayAudio(string primarySpelling, string? reading)
{
if (WindowsUtils.AudioPlayer?.PlaybackState is PlaybackState.Playing
&& s_primarySpellingOfLastPlayedAudio == primarySpelling
&& s_readingOfLastPlayedAudio == reading)
{
return;
}

s_primarySpellingOfLastPlayedAudio = primarySpelling;
s_readingOfLastPlayedAudio = reading;

await AudioUtils.GetAndPlayAudio(primarySpelling, reading).ConfigureAwait(false);
}
}

0 comments on commit 7144ce2

Please sign in to comment.