215 lines
7.8 KiB
C#
215 lines
7.8 KiB
C#
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Interop;
|
|
using System.Windows.Threading;
|
|
using Microsoft.Web.WebView2.Core;
|
|
using FoxBrowser.Core;
|
|
using FoxBrowser.Native;
|
|
using FoxBrowser.UI;
|
|
|
|
namespace FoxBrowser;
|
|
|
|
public partial class MainWindow : Window
|
|
{
|
|
private TabManager? _tabManager;
|
|
private TrayIconManager? _tray;
|
|
private bool _allowClose;
|
|
private readonly DispatcherTimer _watchdogTimer = new() { Interval = TimeSpan.FromSeconds(30) };
|
|
private AudioSessionLabeler? _audioLabeler;
|
|
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
try
|
|
{
|
|
var icoPath = Path.Combine(AppContext.BaseDirectory, "FoxBrowser.ico");
|
|
if (File.Exists(icoPath))
|
|
Icon = new System.Windows.Media.Imaging.BitmapImage(new Uri(icoPath, UriKind.Absolute));
|
|
}
|
|
catch { }
|
|
SourceInitialized += (_, _) => ApplyDarkTitleBar(true);
|
|
Loaded += MainWindow_Loaded;
|
|
}
|
|
|
|
private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
_tray = new TrayIconManager(this, ExitApplication);
|
|
var environmentFactory = new WebView2EnvironmentFactory();
|
|
var recoveryMode = false;
|
|
CoreWebView2Environment environment;
|
|
try
|
|
{
|
|
environment = await environmentFactory.CreateAsync();
|
|
}
|
|
catch (Exception ex) when ((uint)ex.HResult == 0x8007139Fu)
|
|
{
|
|
// Another FoxBrowser/WebView2 process may hold the persistent profile lock.
|
|
// Use a disposable recovery profile so the browser can still start.
|
|
recoveryMode = true;
|
|
var recoveryData = Path.Combine(
|
|
Path.GetTempPath(), $"FoxBrowser_Recovery_{Environment.ProcessId}_{Guid.NewGuid():N}");
|
|
Directory.CreateDirectory(recoveryData);
|
|
environment = await environmentFactory.CreateAsync(recoveryData);
|
|
}
|
|
var startupNotice = environmentFactory.TakeStartupNotice();
|
|
if (startupNotice is not null)
|
|
System.Windows.MessageBox.Show(this, startupNotice, "FoxBrowser", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
_tabManager = new TabManager(WebViewHost, environment, !recoveryMode);
|
|
_tabManager.ActiveTabStateChanged += (_, _) => Dispatcher.Invoke(RefreshAddressBar);
|
|
DataContext = _tabManager;
|
|
await _tabManager.InitializeAsync();
|
|
var iconPath = Path.Combine(AppContext.BaseDirectory, "FoxBrowser.ico");
|
|
try
|
|
{
|
|
_audioLabeler = new AudioSessionLabeler((uint)Process.GetCurrentProcess().Id, iconPath);
|
|
}
|
|
catch
|
|
{
|
|
// Audio labeling is optional; a missing endpoint must not break browsing.
|
|
_audioLabeler = null;
|
|
}
|
|
PinnedExtensionStrip.ItemsSource = _tabManager.ExtensionManager?.PinnedExtensions;
|
|
_watchdogTimer.Tick += async (_, _) => await _tabManager.SoftReloadIfNeededAsync();
|
|
_watchdogTimer.Start();
|
|
BrowserStatus.Visibility = Visibility.Collapsed;
|
|
RefreshAddressBar();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
BrowserStatus.Text = $"WebView2 не запустився: {ex.Message}";
|
|
BrowserStatus.Visibility = Visibility.Visible;
|
|
}
|
|
}
|
|
|
|
private async void NewTab_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_tabManager is not null) await _tabManager.AddTabAsync();
|
|
RefreshAddressBar();
|
|
}
|
|
|
|
private async void AddressBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
|
|
{
|
|
if (e.Key != Key.Enter) return;
|
|
await NavigateFromAddressBarAsync();
|
|
Keyboard.ClearFocus();
|
|
}
|
|
|
|
private async void Go_Click(object sender, RoutedEventArgs e) => await NavigateFromAddressBarAsync();
|
|
|
|
private async void AudioOnly_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_tabManager is null) return;
|
|
await _tabManager.ToggleAudioOnlyAsync();
|
|
TabStrip.Items.Refresh();
|
|
}
|
|
|
|
private async Task NavigateFromAddressBarAsync()
|
|
{
|
|
if (_tabManager?.ActiveTab is null) return;
|
|
await _tabManager.NavigateAsync(_tabManager.ActiveTab, AddressBox.Text);
|
|
AddressBox.Text = _tabManager.ActiveTab.Address;
|
|
}
|
|
|
|
private void Back_Click(object sender, RoutedEventArgs e) => _tabManager?.GoBack();
|
|
private void Forward_Click(object sender, RoutedEventArgs e) => _tabManager?.GoForward();
|
|
private void Reload_Click(object sender, RoutedEventArgs e) => _tabManager?.Reload();
|
|
private void Extensions_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_tabManager?.ExtensionManager is null) return;
|
|
var window = new ExtensionsSettingsWindow(
|
|
_tabManager.ExtensionManager,
|
|
_tabManager.Environment,
|
|
address => _tabManager!.OpenUrlAsync(address),
|
|
_tabManager.ExtensionsAvailable)
|
|
{ Owner = this };
|
|
window.ShowDialog();
|
|
}
|
|
|
|
private void PinnedExtension_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (_tabManager?.ExtensionManager is null || sender is not System.Windows.Controls.Button button || button.DataContext is not PinnedExtensionItem item)
|
|
return;
|
|
|
|
if (string.IsNullOrWhiteSpace(item.PopupPage))
|
|
{
|
|
Extensions_Click(sender, e);
|
|
return;
|
|
}
|
|
|
|
var screenPositionWpf = button.PointToScreen(new System.Windows.Point(0, button.ActualHeight));
|
|
var screenPosition = new System.Drawing.Point((int)screenPositionWpf.X, (int)screenPositionWpf.Y);
|
|
|
|
var popup = new ExtensionPopupWindow(_tabManager.Environment, item.Extension.Id, item.PopupPage, screenPosition)
|
|
{
|
|
Owner = this
|
|
};
|
|
popup.Show();
|
|
}
|
|
|
|
private async void TabStrip_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
{
|
|
if (e.Source != TabStrip || TabStrip.SelectedItem is not BrowserTab tab || _tabManager is null) return;
|
|
AddressBox.Text = tab.Address;
|
|
await _tabManager.SwitchToTabAsync(tab);
|
|
RefreshAddressBar();
|
|
}
|
|
|
|
private void CloseTab_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if ((sender as System.Windows.Controls.Button)?.Tag is BrowserTab tab) _tabManager?.CloseTab(tab);
|
|
RefreshAddressBar();
|
|
}
|
|
|
|
private async void Window_StateChanged(object? sender, EventArgs e)
|
|
{
|
|
if (_tabManager is null) return;
|
|
var minimized = WindowState == WindowState.Minimized;
|
|
if (minimized) _tray?.MinimizeToTray();
|
|
await _tabManager.SetMinimizedModeAsync(minimized);
|
|
}
|
|
|
|
private async void Window_Closing(object? sender, CancelEventArgs e)
|
|
{
|
|
if (!_allowClose)
|
|
{
|
|
e.Cancel = true;
|
|
_tray?.MinimizeToTray();
|
|
if (_tabManager is not null) await _tabManager.SetMinimizedModeAsync(true);
|
|
}
|
|
else
|
|
{
|
|
_watchdogTimer.Stop();
|
|
_tray?.Dispose();
|
|
_audioLabeler?.Dispose();
|
|
if (_tabManager is not null) await _tabManager.DisposeAsync();
|
|
}
|
|
}
|
|
|
|
private void ExitApplication()
|
|
{
|
|
_allowClose = true;
|
|
Close();
|
|
}
|
|
|
|
private void ApplyDarkTitleBar(bool enabled)
|
|
{
|
|
var hwnd = new WindowInteropHelper(this).Handle;
|
|
Win32Interop.ApplyDarkTitleBar(hwnd, enabled);
|
|
}
|
|
|
|
private void RefreshAddressBar()
|
|
{
|
|
if (_tabManager?.ActiveTab is not null)
|
|
{
|
|
if (!ReferenceEquals(TabStrip.SelectedItem, _tabManager.ActiveTab)) TabStrip.SelectedItem = _tabManager.ActiveTab;
|
|
AddressBox.Text = _tabManager.ActiveTab.Address;
|
|
}
|
|
}
|
|
}
|