chromium/remoting/host/continue_window_mac.mm

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/continue_window.h"

#import <Cocoa/Cocoa.h>

#include <memory>

#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/sys_string_conversions.h"
#include "remoting/base/string_resources.h"
#include "ui/base/l10n/l10n_util_mac.h"

// Handles the ContinueWindow.
@interface ContinueWindowMacController : NSObject {
 @private
  NSMutableArray<NSWindow*>* __strong _shades;
  NSAlert* __strong _continue_alert;
  raw_ptr<remoting::ContinueWindow> _continue_window;
}

- (instancetype)initWithWindow:(remoting::ContinueWindow*)continue_window;
- (void)show;
- (void)hide;
- (void)onCancel:(id)sender;
- (void)onContinue:(id)sender;
@end

namespace remoting {

// A bridge between C++ and ObjC implementations of ContinueWindow.
// Everything important occurs in ContinueWindowMacController.
class ContinueWindowMac : public ContinueWindow {
 public:
  ContinueWindowMac();

  ContinueWindowMac(const ContinueWindowMac&) = delete;
  ContinueWindowMac& operator=(const ContinueWindowMac&) = delete;

  ~ContinueWindowMac() override;

 protected:
  // ContinueWindow overrides.
  void ShowUi() override;
  void HideUi() override;

 private:
  ContinueWindowMacController* __strong controller_;
};

ContinueWindowMac::ContinueWindowMac() = default;

ContinueWindowMac::~ContinueWindowMac() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (controller_) {
    HideUi();
  }
}

void ContinueWindowMac::ShowUi() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  @autoreleasepool {
    controller_ = [[ContinueWindowMacController alloc] initWithWindow:this];
    [controller_ show];
  }
}

void ContinueWindowMac::HideUi() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  @autoreleasepool {
    [controller_ hide];
    controller_ = nil;
  }
}

// static
std::unique_ptr<HostWindow> HostWindow::CreateContinueWindow() {
  return std::make_unique<ContinueWindowMac>();
}

}  // namespace remoting

@implementation ContinueWindowMacController

- (instancetype)initWithWindow:(remoting::ContinueWindow*)continue_window {
  if ((self = [super init])) {
    _continue_window = continue_window;
  }
  return self;
}

- (void)show {
  // Generate window shade
  NSArray* screens = NSScreen.screens;
  _shades = [[NSMutableArray alloc] initWithCapacity:screens.count];
  for (NSScreen* screen in screens) {
    NSWindow* shade =
        [[NSWindow alloc] initWithContentRect:screen.frame
                                    styleMask:NSWindowStyleMaskBorderless
                                      backing:NSBackingStoreBuffered
                                        defer:NO
                                       screen:screen];
    shade.releasedWhenClosed = NO;
    shade.alphaValue = 0.8;
    shade.opaque = NO;
    shade.backgroundColor = NSColor.blackColor;
    // Raise the window shade above just about everything else.
    // Leave the dock and menu bar exposed so the user has some basic level
    // of control (like they can quit Chromium).
    shade.level = NSModalPanelWindowLevel - 1;
    [shade orderFront:nil];
    [_shades addObject:shade];
  }

  // Create alert.
  _continue_alert = [[NSAlert alloc] init];
  _continue_alert.messageText = l10n_util::GetNSString(IDS_CONTINUE_PROMPT);

  NSButton* continue_button = [_continue_alert
      addButtonWithTitle:l10n_util::GetNSString(IDS_CONTINUE_BUTTON)];
  continue_button.action = @selector(onContinue:);
  continue_button.target = self;

  NSButton* cancel_button = [_continue_alert
      addButtonWithTitle:l10n_util::GetNSString(IDS_STOP_SHARING_BUTTON)];
  cancel_button.action = @selector(onCancel:);
  cancel_button.target = self;

  NSBundle* bundle = [NSBundle bundleForClass:[self class]];
  NSString* imagePath = [bundle pathForResource:@"chromoting128" ofType:@"png"];
  NSImage* image = [[NSImage alloc] initByReferencingFile:imagePath];
  _continue_alert.icon = image;
  [_continue_alert layout];

  // Force alert to be at the proper level and location.
  NSWindow* continue_window = _continue_alert.window;
  [continue_window center];
  continue_window.level = NSModalPanelWindowLevel;
  [continue_window orderWindow:NSWindowAbove
                    relativeTo:_shades.lastObject.windowNumber];
  [continue_window makeKeyWindow];
}

- (void)hide {
  // Remove window shade.
  for (NSWindow* window in _shades) {
    [window close];
  }
  _shades = nil;
  if (_continue_alert) {
    [_continue_alert.window close];
    _continue_alert = nil;
  }
}

- (void)onCancel:(id)sender {
  [self hide];
  _continue_window->DisconnectSession();
}

- (void)onContinue:(id)sender {
  [self hide];
  _continue_window->ContinueSession();
}

@end