File: Preview\PreviewPane.cs
Web Access
Project: ..\..\..\src\EditorFeatures\Core.Cocoa\Microsoft.CodeAnalysis.EditorFeatures.Cocoa.csproj (Microsoft.CodeAnalysis.EditorFeatures.Cocoa)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using AppKit;
using Foundation;
using ObjCRuntime;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.VisualStudio.Text.Differencing;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.Preview
{
    internal class PreviewPane : NSView
    {
        private DifferenceViewerPreview? _differenceViewerPreview;
        private readonly NSTextField? titleField;
 
        public PreviewPane(string? id, string? title, Uri? helpLink, string? helpLinkToolTipText, IReadOnlyList<object> previewContent)
        {
            _differenceViewerPreview = (DifferenceViewerPreview)previewContent[0];
            var view = ((ICocoaDifferenceViewer)_differenceViewerPreview.Viewer).VisualElement;
 
            var originalSize = view.Frame.Size;
 
            this.TranslatesAutoresizingMaskIntoConstraints = false;
 
            // === Title ===
            // Title is in a stack view to help with padding
            var titlePlaceholder = new NSStackView()
            {
                Orientation = NSUserInterfaceLayoutOrientation.Vertical,
                EdgeInsets = new NSEdgeInsets(5, 0, 5, 0),
                Alignment = NSLayoutAttribute.Leading,
                TranslatesAutoresizingMaskIntoConstraints = false
            };
 
            // TODO: missing icon
            this.titleField = new NSTextField()
            {
                Editable = false,
                Bordered = false,
                BackgroundColor = NSColor.ControlBackground,
                DrawsBackground = false,
            };
 
            titlePlaceholder.AddArrangedSubview(titleField);
            AddSubview(titlePlaceholder);
 
            // === Preview View ===
            // This is the actual view, that shows the diff
            view.TranslatesAutoresizingMaskIntoConstraints = false;
            NSLayoutConstraint.Create(view, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, 1, originalSize.Width).Active = true;
            NSLayoutConstraint.Create(view, NSLayoutAttribute.Height, NSLayoutRelation.GreaterThanOrEqual, 1, originalSize.Height).Active = true;
            view.Subviews[0].TranslatesAutoresizingMaskIntoConstraints = false;
            view.WantsLayer = true;
 
            AddSubview(view);
 
            // === Constraints ===
            var constraints = new NSLayoutConstraint[]
            {
                // Title
                NSLayoutConstraint.Create(titlePlaceholder, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1, 0),
                NSLayoutConstraint.Create(titlePlaceholder, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this, NSLayoutAttribute.Leading, 1, 0),
                NSLayoutConstraint.Create(titlePlaceholder, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, this, NSLayoutAttribute.Trailing, 1, 0),
 
                // Preview View
                NSLayoutConstraint.Create(view, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this, NSLayoutAttribute.Bottom, 1, 0),
                NSLayoutConstraint.Create(view, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, this, NSLayoutAttribute.Leading, 1, 0),
                NSLayoutConstraint.Create(view, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, this, NSLayoutAttribute.Trailing, 1, 0),
 
                // subviews
                NSLayoutConstraint.Create(view.Subviews[0], NSLayoutAttribute.Top, NSLayoutRelation.Equal, view, NSLayoutAttribute.Top, 1, 0),
                NSLayoutConstraint.Create(view.Subviews[0], NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, view, NSLayoutAttribute.Bottom, 1, 0),
                NSLayoutConstraint.Create(view.Subviews[0], NSLayoutAttribute.Left, NSLayoutRelation.Equal, view, NSLayoutAttribute.Left, 1, 0),
                NSLayoutConstraint.Create(view.Subviews[0], NSLayoutAttribute.Right, NSLayoutRelation.Equal, view, NSLayoutAttribute.Right, 1, 0),
            };
 
            if (GenerateAttributeString(id, title, helpLink, helpLinkToolTipText) is NSAttributedString attributedStringTitle)
            {
                this.titleField.AttributedStringValue = attributedStringTitle;
                // We do this separately, because the title sometimes isn't there (i.e. no diagnostics ID)
                // and we want the preview to stretch to the top
                NSLayoutConstraint.Create(view, NSLayoutAttribute.Top, NSLayoutRelation.Equal, titlePlaceholder, NSLayoutAttribute.Bottom, 1, 0).Active = true;
            }
            else
            {
                NSLayoutConstraint.Create(view, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1, 0).Active = true;
            }
 
            NSLayoutConstraint.ActivateConstraints(constraints);
 
            _differenceViewerPreview.Viewer.InlineView.TryMoveCaretToAndEnsureVisible(
                new Microsoft.VisualStudio.Text.SnapshotPoint(_differenceViewerPreview.Viewer.InlineView.TextSnapshot, 0));
        }
 
        public PreviewPane(NativeHandle ptr)
            : base(ptr)
        {
        }
 
        private static NSAttributedString? GenerateAttributeString(string? id, string? title, Uri? link, string? linkTooltip)
        {
            if (string.IsNullOrEmpty(title))
                return null;
 
            var attributedBuffer = new NSMutableAttributedString();
 
            attributedBuffer.BeginEditing();
 
            var normalText = new NSStringAttributes
            {
                ForegroundColor = NSColor.ControlText
            };
 
            if (!string.IsNullOrEmpty(id))
            {
                var linkAttributes = new NSStringAttributes
                {
                    LinkUrl = link,
                    ToolTip = linkTooltip
                };
 
                attributedBuffer.Append(new NSAttributedString(id, linkAttributes));
                attributedBuffer.Append(new NSAttributedString(": ", normalText));
            }
 
            attributedBuffer.Append(new NSAttributedString(title, normalText));
            attributedBuffer.EndEditing();
 
            return attributedBuffer;
        }
 
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _differenceViewerPreview?.Dispose();
                _differenceViewerPreview = null;
            }
 
            base.Dispose(disposing);
        }
    }
}