Jump to content
Sign in to follow this  
CHackbart

MacOS NSVisualEffectView implementation

Recommended Posts

Hi,

 

I wrote a small solution to add this fancy semi transparent background effect supported since a while on MacOS. What I did is to translate the NSVisualEffectView class to Delphi and to write some small code which adds this view to the ContentView. First I thought it works fine, but it does not resize automatically and technically it should be moved to the bottom of the rendered subview, otherwise the view used for the FireMonkey controls is not visible. 

 

634140491_Bildschirmfoto2021-11-15um21_16_43.thumb.png.0d03360f2e80cbe392e8fcb99986a236.png

 

Maybe somebody has an idea how to put this view at the bottom of the view list rendered without adding this code in to the FMX.Platform.Mac. I dislike the idea to mess with the regular FMX units around.

 

 

visualeffectview.zip

Edited by CHackbart

Share this post


Link to post

I managed to get it working. To do so I had to move the main view to the front and set the background color of the layer to transparent. This works also when UseGlobalMetal is enabled.

example.thumb.png.fb4f9214b9e6be78c60b1b3f76b9f2e1.png

Edited by CHackbart
  • Like 2

Share this post


Link to post

Yes, and it works surprisingly well. You just have to execute AppendToForm with your Formular and if you do not want to have a border - since FireMonkey still is unable to render the toolbar with a style under osx - you can set this also to borderless. 

 

function AppendToForm(const AForm: TForm;const AOpacity: single=1.0;const ABorderLess: Boolean=False): Boolean;
var
  LNSWin: NSWindow;
  LBlurView: NSVisualEffectView;
  LView: NSView;
  LContext: NSView;
begin
  if not TOSVersion.Check(10,10) then
  begin
    result := false;
    exit;
  end;

  AForm.Fill.Kind := TBrushKind.Solid;
  AForm.Fill.Color := 0;

  LNSWin := WindowHandleToPlatform(AForm.Handle).Wnd;
  LContext := WindowHandleToPlatform(AForm.Handle).View;

  LNSWin.setOpaque(false);
  LNSWin.setAlphaValue(AOpacity);

  LBlurView := TNSVisualEffectView.Wrap(
   TNSVisualEffectView.Alloc.initWithFrame(
    MakeNSRect(0,0, AForm.Width, AForm.Height)));
  LBlurView.setWantsLayer(true);
  LBlurView.setBlendingMode(NSVisualEffectBlendingModeBehindWindow);
  LBlurView.setMaterial(NSVisualEffectViewMaterialFullScreenUI);
  LBlurView.setState(NSVisualEffectStateActive);
  LBlurView.setAutoresizingMask(NSViewWidthSizable or NSViewHeightSizable);

  LView := TNSView.Wrap(LNSWin.contentView);

  if ABorderLess then
  begin
   LNSWin.setStyleMask(NSBorderlessWindowMask or NSResizableWindowMask);
   LNSWin.setBackgroundColor(TNSColor.Wrap(TNSColor.OCClass.clearColor));

   LView.setWantsLayer(true);
   LView.layer.setMasksToBounds(true);
   LView.layer.SetCornerRadius(10.0);
  end;

  LContext.removeFromSuperview;
  LView.addSubview(LBlurView);
  LView.addSubview(LContext);

  if GlobalUseMetal then
   WindowHandleToPlatform(AForm.Handle).MTView.layer.setOpaque(false);

  result := true;
end;

 

  • Like 1
  • Thanks 1

Share this post


Link to post

I will create a small GitHub repo with those helpers. Especially the Z-Order handling is something I barely miss under MacOS. By removing the contentView and adding it after the other views have been added to the MainView you can easily put the FMX layer to the top.  

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×