Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
<PackageVersion Include="Grpc.Net.Client" Version="2.62.0" />
<PackageVersion Include="Grpc.Tools" Version="2.62.0" />
<PackageVersion Include="Grpc" Version="2.46.6" />
<PackageVersion Include="Infragistics.WPF.DockManager.Trial" Version="23.2.94" />
<PackageVersion Include="Infragistics.WPF.Ribbon.Trial" Version="23.2.94" />
<PackageVersion Include="Infragistics.WPF.Trial" Version="23.2.94" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.9.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
Expand Down
15 changes: 12 additions & 3 deletions src/shell/dotnet/Shell/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ public TWindow CreateWindow<TWindow>(params object[] parameters) where TWindow :
return CreateInstance<TWindow>(parameters);
}

public WebContent CreateWebContent(params object[] parameters)
{
Dispatcher.VerifyAccess();
var webContent = CreateInstance<WebContent>(parameters);
_shellWindow!.AddDockableFloatingContent(webContent);

return webContent;
}

public T? GetService<T>()
{
return Host.Services.GetService<T>();
Expand Down Expand Up @@ -96,6 +105,7 @@ protected override void OnExit(ExitEventArgs e)
private IHost? _host;

private ILogger _logger = NullLogger<App>.Instance;
private MainWindow? _shellWindow;

// TODO: Assign a unique token for each module
internal readonly string MessageRouterAccessToken = Guid.NewGuid().ToString("N");
Expand Down Expand Up @@ -234,12 +244,11 @@ private void OnAsyncStartupCompleted(StartupEventArgs e)
{
new(WebWindowOptions.ParameterName, JsonSerializer.Serialize(webWindowOptions))
}));

return;
}

ShutdownMode = ShutdownMode.OnMainWindowClose;
CreateWindow<MainWindow>().Show();
_shellWindow = CreateWindow<MainWindow>();
_shellWindow.Show();
}

private async Task StopAsync()
Expand Down
17 changes: 15 additions & 2 deletions src/shell/dotnet/Shell/ImageSource/ImageSourceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// */

using System;
using System.Drawing;
using System.Windows.Media.Imaging;

namespace MorganStanley.ComposeUI.Shell.ImageSource;
Expand All @@ -25,7 +26,7 @@ public ImageSourceProvider(IImageSourcePolicy imageSourcePolicy)
_imageSourcePolicy = imageSourcePolicy;
}

public System.Windows.Media.ImageSource? GetImageSource(Uri uri, Uri appUri)
public System.Windows.Media.ImageSource? GetImageSource(Uri uri, Uri appUri, Size? size = null)
{
if (!uri.IsAbsoluteUri)
{
Expand All @@ -34,7 +35,19 @@ public ImageSourceProvider(IImageSourcePolicy imageSourcePolicy)

if (_imageSourcePolicy.IsAllowed(uri, appUri))
{
return BitmapFrame.Create(uri);
var bitmap = new BitmapImage();
bitmap.BeginInit();

if (size != null)
{
bitmap.DecodePixelHeight = size.Value.Height;
bitmap.DecodePixelWidth = size.Value.Width;
}
bitmap.UriSource = uri;

bitmap.EndInit();

return bitmap;
}
return null;
}
Expand Down
39 changes: 27 additions & 12 deletions src/shell/dotnet/Shell/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ See the License for the specific language governing permissions and limitations
********************************************************************************************************
-->
<RibbonWindow x:Class="MorganStanley.ComposeUI.Shell.MainWindow"
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"
xmlns:local="clr-namespace:MorganStanley.ComposeUI.Shell"
mc:Ignorable="d"
Title="ComposeUI Shell"
Height="450"
Width="600"
Background="{DynamicResource {x:Static SystemColors.AppWorkspaceBrushKey}}"
WindowStartupLocation="CenterScreen"
Initialized="RibbonWindow_Initialized">
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"
xmlns:local="clr-namespace:MorganStanley.ComposeUI.Shell"
xmlns:igDock="http://infragistics.com/DockManager"
mc:Ignorable="d"
Title="ComposeUI Shell"
Height="450"
Width="600"
Background="{DynamicResource {x:Static SystemColors.AppWorkspaceBrushKey}}"
WindowStartupLocation="CenterScreen"
Initialized="RibbonWindow_Initialized">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Ribbon x:Name="Ribbon" SelectedIndex="0" Height="Auto" >
<!-- Hide the Application Menu -->
Expand All @@ -48,5 +50,18 @@ See the License for the specific language governing permissions and limitations
</RibbonGroup>
</RibbonTab>
</Ribbon>

<Grid Grid.Row="1"
x:Name="layoutRoot">
<igDock:XamDockManager x:Name="_xamDockManager"
AllowMaximizeFloatingWindows="True"
AllowMinimizeFloatingWindows="True"
ShowFloatingWindowsInTaskbar="True"
LayoutMode="FillContainer">
<igDock:XamDockManager.Panes>
<igDock:SplitPane x:Name="_verticalSplit" SplitterOrientation="Vertical" igDock:XamDockManager.InitialLocation="DockedLeft" />
</igDock:XamDockManager.Panes>
</igDock:XamDockManager>
</Grid>
</Grid>
</RibbonWindow>
7 changes: 6 additions & 1 deletion src/shell/dotnet/Shell/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
using System.Windows;
using System.Windows.Controls.Ribbon;
using CommunityToolkit.Mvvm.ComponentModel;
using Infragistics.Windows.DockManager;
using MorganStanley.ComposeUI.ModuleLoader;
using MorganStanley.ComposeUI.Shell.ImageSource;
using MorganStanley.ComposeUI.Shell.Utilities;
using IconUtilities = MorganStanley.ComposeUI.Shell.Utilities.IconUtilities;

namespace MorganStanley.ComposeUI.Shell;

Expand Down Expand Up @@ -61,6 +61,11 @@ private async void RibbonWindow_Initialized(object sender, System.EventArgs e)
};
}

public void AddDockableFloatingContent(WebContent webContent)
{
_verticalSplit.Panes.Add(new WebContentPane(webContent, _moduleLoader));
}

internal MainWindowViewModel ViewModel
{
get => (MainWindowViewModel) DataContext;
Expand Down
4 changes: 1 addition & 3 deletions src/shell/dotnet/Shell/Modules/ModuleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,13 @@ private async void OnWebModuleStarted(LifetimeEvent.Started e)
await _application.Dispatcher.InvokeAsync(
() =>
{
var window = _application.CreateWindow<WebWindow>(
var window = _application.CreateWebContent(
e.Instance,
webWindowOptions ?? new WebWindowOptions
{
Url = properties.Url.ToString(),
IconUrl = properties.IconUrl?.ToString()
});

window.Show();
});
}
catch (Exception ex)
Expand Down
7 changes: 5 additions & 2 deletions src/shell/dotnet/Shell/Shell.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<ItemGroup>
<None Remove="MainWindow.xaml" />
<None Remove="WebWindow.xaml" />
<None Remove="WebContent.xaml" />
</ItemGroup>

<ItemGroup>
Expand All @@ -22,10 +22,13 @@
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Microsoft.Web.WebView2" />
<PackageReference Include="System.Drawing.Common" />
<PackageReference Include="Infragistics.WPF.DockManager.Trial" />
<PackageReference Include="Infragistics.WPF.Trial" />
<PackageReference Include="Infragistics.WPF.Ribbon.Trial" />
</ItemGroup>

<ItemGroup>
<Page Include="WebWindow.xaml">
<Page Include="WebContent.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow.xaml" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
<Window x:Class="MorganStanley.ComposeUI.Shell.WebWindow"
<ContentPresenter x:Class="MorganStanley.ComposeUI.Shell.WebContent"
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"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
xmlns:local="clr-namespace:MorganStanley.ComposeUI.Shell"
mc:Ignorable="d"
Title="Compose" Height="450" Width="800"
>
<Grid>
<DockPanel>
<wv2:WebView2 Name = "WebView" Source = ""/>
</DockPanel>
</Grid>
</Window>
mc:Ignorable="d">
<ContentPresenter.Content>
<wv2:WebView2 Name = "WebView" Source = "" />
</ContentPresenter.Content>
</ContentPresenter>
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,37 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Web.WebView2.Core;
using MorganStanley.ComposeUI.ModuleLoader;
using MorganStanley.ComposeUI.Shell.ImageSource;
using MorganStanley.ComposeUI.Shell.Utilities;

namespace MorganStanley.ComposeUI.Shell;

/// <summary>
/// Interaction logic for WebWindow.xaml
/// Interaction logic for WebContent.xaml
/// </summary>

public partial class WebWindow : Window
public partial class WebContent : ContentPresenter, IDisposable
{
public WebWindow(
public WebContent(
WebWindowOptions options,
IModuleLoader moduleLoader,
IModuleInstance? moduleInstance = null,
ILogger<WebWindow>? logger = null,
ILogger<WebContent>? logger = null,
IImageSourcePolicy? imageSourcePolicy = null)
{
_moduleLoader = moduleLoader;
_moduleInstance = moduleInstance;
_iconProvider = new ImageSourceProvider(imageSourcePolicy ?? new DefaultImageSourcePolicy());
_options = options;
_logger = logger ?? NullLogger<WebWindow>.Instance;
_logger = logger ?? NullLogger<WebContent>.Instance;
InitializeComponent();

// TODO: When no title is set from options, we should show the HTML document's title instead
Expand All @@ -69,65 +66,33 @@ public WebWindow(
(LifetimeEvent e) =>
{
_lifetimeEvent = e.EventType;
Close();
})));
}

_ = InitializeAsync();
}

public IModuleInstance? ModuleInstance => _moduleInstance;
public LifetimeEventType LifetimeEvent => _lifetimeEvent;

protected override void OnClosing(CancelEventArgs args)
{
// TODO: Send the closing event to the page, allow it to cancel
public event EventHandler CloseRequested;

if (_moduleInstance == null)
return;
public string Title { get; private set; }

switch (_lifetimeEvent)
{
case LifetimeEventType.Stopped:
return;

case LifetimeEventType.Stopping:
args.Cancel = true;
Hide();
return;

default:
args.Cancel = true;
Hide();
Task.Run(() => _moduleLoader.StopModule(new StopRequest(_moduleInstance.InstanceId)));
return;
}
}

protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
RemoveLogicalChild(WebView);
WebView.Dispose();

var disposables = _disposables.AsEnumerable().Reverse().ToArray();
_disposables.Clear();

foreach (var disposable in disposables)
{
disposable.Dispose();
}
}
public System.Windows.Media.ImageSource? Icon { get; private set; }

private readonly IModuleLoader _moduleLoader;
private readonly IModuleInstance? _moduleInstance;
private readonly WebWindowOptions _options;
private readonly ILogger<WebWindow> _logger;
private readonly ILogger<WebContent> _logger;
private readonly ImageSourceProvider _iconProvider;
private bool _scriptsInjected;
private LifetimeEventType _lifetimeEvent = LifetimeEventType.Started;
private readonly TaskCompletionSource _scriptInjectionCompleted = new();
private readonly List<IDisposable> _disposables = new();

public WebWindowOptions Options => _options;

private async Task InitializeAsync()
{
await WebView.EnsureCoreWebView2Async();
Expand All @@ -154,15 +119,15 @@ private void TrySetIconUrl(WebWindowOptions webWindowOptions)

if (iconUrl != null)
{
Icon = _iconProvider.GetImageSource(iconUrl, appUrl);
Icon = _iconProvider.GetImageSource(iconUrl, appUrl, new(16, 16));
}
}

private Task InitializeCoreWebView2(CoreWebView2 coreWebView)
{
coreWebView.NewWindowRequested += (sender, args) => OnNewWindowRequested(args);
coreWebView.WindowCloseRequested += (sender, args) => OnWindowCloseRequested(args);
coreWebView.NavigationStarting += (sender, args) => OnNavigationStarting(args);
coreWebView.NavigationStarting += (sender, args) => OnNavigationStarting(args);
coreWebView.DocumentTitleChanged += (sender, args) => OnDocumentTitleChanged(args);

return Task.CompletedTask;
Expand Down Expand Up @@ -244,14 +209,27 @@ private async void OnNewWindowRequested(CoreWebView2NewWindowRequestedEventArgs
constructorArgs.Add(_moduleInstance);
}

var window = App.Current.CreateWindow<WebWindow>(constructorArgs.ToArray());
window.Show();
var window = App.Current.CreateWebContent(constructorArgs.ToArray());
await window.WebView.EnsureCoreWebView2Async();
e.NewWindow = window.WebView.CoreWebView2;
}

private void OnWindowCloseRequested(object args)
{
Close();
CloseRequested?.Invoke(this, EventArgs.Empty);
}

public void Dispose()
{
RemoveLogicalChild(WebView);
WebView.Dispose();

var disposables = _disposables.AsEnumerable().Reverse().ToArray();
_disposables.Clear();

foreach (var disposable in disposables)
{
disposable.Dispose();
}
}
}
Loading