Skip to content

Commit

Permalink
Add support for MAUI embedding
Browse files Browse the repository at this point in the history
  • Loading branch information
mattleibow committed Jul 18, 2024
1 parent 58fb075 commit 27f27b2
Show file tree
Hide file tree
Showing 20 changed files with 878 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ namespace Maui.Controls.Sample.Droid;
[Register("com.microsoft.maui.sample.emdedding." + nameof(FirstFragment))]
public class FirstFragment : Fragment
{
EmbeddingScenarios.IScenario? _scenario;
MyMauiContent? _mauiView;
Android.Views.View? _nativeView;

public override View? OnCreateView(LayoutInflater inflater, ViewGroup? container, Bundle? savedInstanceState) =>
inflater.Inflate(Resource.Layout.fragment_first, container, false);

Expand All @@ -26,14 +30,43 @@ public override void OnViewCreated(View view, Bundle? savedInstanceState)

var button3 = view.FindViewById<Button>(Resource.Id.button3)!;
button3.Click += OnMagicClicked;

// Uncomment the scenario to test:
//_scenario = new EmbeddingScenarios.Scenario1_Basic();
//_scenario = new EmbeddingScenarios.Scenario2_Scoped();
_scenario = new EmbeddingScenarios.Scenario3_Correct();

// Create the view and (maybe) the window
(_mauiView, _nativeView) = _scenario!.Embed(Activity!);

// Add the new view to the UI
var rootLayout = view.FindViewById<LinearLayout>(Resource.Id.layout_first)!;
rootLayout.AddView(_nativeView, 1, new LinearLayout.LayoutParams(MatchParent, WrapContent));
}

public override void OnDestroyView()
{
base.OnDestroyView();

// Remove the view from the UI
var rootLayout = View!.FindViewById<LinearLayout>(Resource.Id.layout_first)!;
rootLayout.RemoveView(_nativeView);

// If we used a window, then clean that up
if (_mauiView?.Window is IWindow window)
window.Destroying();

base.OnStop();
}

private void OnMagicClicked(object? sender, EventArgs e)
private async void OnMagicClicked(object? sender, EventArgs e)
{
if (_mauiView?.DotNetBot is not Image bot)
return;

await bot.RotateTo(360, 1000);
bot.Rotation = 0;

bot.HeightRequest = 90;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ namespace Maui.Controls.Sample.Droid;
[Register("com.microsoft.maui.sample.emdedding." + nameof(SettingsFragment))]
public class SettingsFragment : Fragment
{
EmbeddingScenarios.IScenario? _scenario;
MyMauiContent? _mauiView;
Android.Views.View? _nativeView;

public override View? OnCreateView(LayoutInflater inflater, ViewGroup? container, Bundle? savedInstanceState) =>
inflater.Inflate(Resource.Layout.fragment_settings, container, false);

Expand All @@ -26,14 +30,43 @@ public override void OnViewCreated(View view, Bundle? savedInstanceState)

var button3 = view.FindViewById<Button>(Resource.Id.button3)!;
button3.Click += OnMagicClicked;

// Uncomment the scenario to test:
//_scenario = new EmbeddingScenarios.Scenario1_Basic();
//_scenario = new EmbeddingScenarios.Scenario2_Scoped();
_scenario = new EmbeddingScenarios.Scenario3_Correct();

// Create the view and (maybe) the window
(_mauiView, _nativeView) = _scenario!.Embed(Activity!);

// Add the new view to the UI
var rootLayout = view.FindViewById<LinearLayout>(Resource.Id.layout_settings)!;
rootLayout.AddView(_nativeView, 1, new LinearLayout.LayoutParams(MatchParent, WrapContent));
}

public override void OnDestroyView()
{
base.OnDestroyView();

// Remove the view from the UI
var rootLayout = View!.FindViewById<LinearLayout>(Resource.Id.layout_settings)!;
rootLayout.RemoveView(_nativeView);

// If we used a window, then clean that up
if (_mauiView?.Window is IWindow window)
window.Destroying();

base.OnStop();
}

private void OnMagicClicked(object? sender, EventArgs e)
private async void OnMagicClicked(object? sender, EventArgs e)
{
if (_mauiView?.DotNetBot is not Image bot)
return;

await bot.RotateTo(360, 1000);
bot.Rotation = 0;

bot.HeightRequest = 90;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

public class MainViewController : UIViewController
{
EmbeddingScenarios.IScenario? _scenario;
MyMauiContent? _mauiView;
UIView? _nativeView;

public override void ViewDidLoad()
{
base.ViewDidLoad();
Expand Down Expand Up @@ -34,7 +38,16 @@ public override void ViewDidLoad()
firstButton.SetTitle("UIKit Button Above MAUI", UIControlState.Normal);
stackView.AddArrangedSubview(firstButton);

// TODO: The MAUI content will go here.
// Uncomment the scenario to test:
//_scenario = new EmbeddingScenarios.Scenario1_Basic();
//_scenario = new EmbeddingScenarios.Scenario2_Scoped();
_scenario = new EmbeddingScenarios.Scenario3_Correct();

// create the view and (maybe) the window
(_mauiView, _nativeView) = _scenario.Embed(ParentViewController!.View!.Window);

// add the new view to the UI
stackView.AddArrangedSubview(new ContainerView(_nativeView));

// Create UIKit button
var secondButton = new UIButton(UIButtonType.System);
Expand All @@ -44,11 +57,23 @@ public override void ViewDidLoad()
// Create UIKit button
var thirdButton = new UIButton(UIButtonType.System);
thirdButton.SetTitle("UIKit Button Magic", UIControlState.Normal);
thirdButton.TouchUpInside += OnMagicClicked;
stackView.AddArrangedSubview(thirdButton);

AddNavBarButtons();
}

private async void OnMagicClicked(object? sender, EventArgs e)
{
if (_mauiView?.DotNetBot is not Image bot)
return;

await bot.RotateTo(360, 1000);
bot.Rotation = 0;

bot.HeightRequest = 90;
}

private void AddNavBarButtons()
{
var addNewWindowButton = new UIBarButtonItem(
Expand Down Expand Up @@ -87,4 +112,33 @@ private void RequestSession(string? activityType = null)
});
}
}

// UIStackView uses IntrinsicContentSize instead of SizeThatFits
// so we need to create a container view to wrap the Maui view and
// redirect the IntrinsicContentSize to the Maui view's SizeThatFits.
class ContainerView : UIView
{
public ContainerView(UIView view)
{
AddSubview(view);
}

public override CGSize IntrinsicContentSize =>
SizeThatFits(new CGSize(nfloat.MaxValue, nfloat.MaxValue));

public override void LayoutSubviews()
{
if (Subviews?.FirstOrDefault() is {} view)
view.Frame = Bounds;
}

public override void SetNeedsLayout()
{
base.SetNeedsLayout();
InvalidateIntrinsicContentSize();
}

public override CGSize SizeThatFits(CGSize size) =>
Subviews?.FirstOrDefault()?.SizeThatFits(size) ?? CGSize.Empty;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,54 @@ namespace Maui.Controls.Sample.WinUI;

public sealed partial class MainWindow : Microsoft.UI.Xaml.Window
{
EmbeddingScenarios.IScenario? _scenario;
MyMauiContent? _mauiView;
FrameworkElement? _nativeView;

public MainWindow()
{
InitializeComponent();
}

private void OnRootLayoutLoaded(object? sender, RoutedEventArgs e)
private async void OnRootLayoutLoaded(object? sender, RoutedEventArgs e)
{
// Sometimes Loaded fires twice...
if (_nativeView is not null)
return;

await Task.Yield();

// Uncomment the scenario to test:
//_scenario = new EmbeddingScenarios.Scenario1_Basic();
//_scenario = new EmbeddingScenarios.Scenario2_Scoped();
_scenario = new EmbeddingScenarios.Scenario3_Correct();

// create the view and (maybe) the window
(_mauiView, _nativeView) = _scenario.Embed(this);

// add the new view to the UI
RootLayout.Children.Insert(1, _nativeView);
}

private void OnWindowClosed(object? sender, WindowEventArgs args)
{
// Remove the view from the UI
RootLayout.Children.Remove(_nativeView);

// If we used a window, then clean that up
if (_mauiView?.Window is IWindow window)
window.Destroying();
}

private void OnMagicClicked(object? sender, RoutedEventArgs e)
private async void OnMagicClicked(object? sender, RoutedEventArgs e)
{
if (_mauiView?.DotNetBot is not Image bot)
return;

await bot.RotateTo(360, 1000);
bot.Rotation = 0;

bot.HeightRequest = 90;
}

private void OnNewWindowClicked(object? sender, RoutedEventArgs e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

public class MainViewController : UIViewController
{
EmbeddingScenarios.IScenario? _scenario;
MyMauiContent? _mauiView;
UIView? _nativeView;

public override void ViewDidLoad()
{
base.ViewDidLoad();
Expand Down Expand Up @@ -34,7 +38,16 @@ public override void ViewDidLoad()
firstButton.SetTitle("UIKit Button Above MAUI", UIControlState.Normal);
stackView.AddArrangedSubview(firstButton);

// TODO: The MAUI content will go here.
// Uncomment the scenario to test:
// _scenario = new EmbeddingScenarios.Scenario1_Basic();
//_scenario = new EmbeddingScenarios.Scenario2_Scoped();
_scenario = new EmbeddingScenarios.Scenario3_Correct();

// create the view and (maybe) the window
(_mauiView, _nativeView) = _scenario.Embed(ParentViewController!.View!.Window);

// add the new view to the UI
stackView.AddArrangedSubview(new ContainerView(_nativeView));

// Create UIKit button
var secondButton = new UIButton(UIButtonType.System);
Expand All @@ -44,12 +57,24 @@ public override void ViewDidLoad()
// Create UIKit button
var thirdButton = new UIButton(UIButtonType.System);
thirdButton.SetTitle("UIKit Button Magic", UIControlState.Normal);
thirdButton.TouchUpInside += OnMagicClicked;
stackView.AddArrangedSubview(thirdButton);

if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad)
AddNavBarButtons();
}

private async void OnMagicClicked(object? sender, EventArgs e)
{
if (_mauiView?.DotNetBot is not Image bot)
return;

await bot.RotateTo(360, 1000);
bot.Rotation = 0;

bot.HeightRequest = 90;
}

private void AddNavBarButtons()
{
var addNewWindowButton = new UIBarButtonItem(
Expand Down Expand Up @@ -88,4 +113,33 @@ private void RequestSession(string? activityType = null)
});
}
}

// UIStackView uses IntrinsicContentSize instead of SizeThatFits
// so we need to create a container view to wrap the Maui view and
// redirect the IntrinsicContentSize to the Maui view's SizeThatFits.
class ContainerView : UIView
{
public ContainerView(UIView view)
{
AddSubview(view);
}

public override CGSize IntrinsicContentSize =>
SizeThatFits(new CGSize(nfloat.MaxValue, nfloat.MaxValue));

public override void LayoutSubviews()
{
if (Subviews?.FirstOrDefault() is {} view)
view.Frame = Bounds;
}

public override void SetNeedsLayout()
{
base.SetNeedsLayout();
InvalidateIntrinsicContentSize();
}

public override CGSize SizeThatFits(CGSize size) =>
Subviews?.FirstOrDefault()?.SizeThatFits(size) ?? CGSize.Empty;
}
}
Loading

0 comments on commit 27f27b2

Please sign in to comment.