chromium/chrome/browser/ui/views/frame/immersive_mode_controller_mac.h

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

#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_MODE_CONTROLLER_MAC_H_
#define CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_MODE_CONTROLLER_MAC_H_

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
#include "components/remote_cocoa/common/native_widget_ns_window.mojom.h"
#include "ui/views/cocoa/immersive_mode_reveal_client.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/view_observer.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"

std::unique_ptr<ImmersiveModeController> CreateImmersiveModeControllerMac(
    const BrowserView* browser_view);

class ImmersiveModeControllerMac;

// This class notifies the browser view to refresh layout whenever the overlay
// widget moves. This is necessary for positioning web dialogs.
class ImmersiveModeOverlayWidgetObserver : public views::WidgetObserver {
 public:
  explicit ImmersiveModeOverlayWidgetObserver(
      ImmersiveModeControllerMac* controller);

  ImmersiveModeOverlayWidgetObserver(
      const ImmersiveModeOverlayWidgetObserver&) = delete;
  ImmersiveModeOverlayWidgetObserver& operator=(
      const ImmersiveModeOverlayWidgetObserver&) = delete;
  ~ImmersiveModeOverlayWidgetObserver() override;

  // views::WidgetObserver:
  void OnWidgetBoundsChanged(views::Widget* widget,
                             const gfx::Rect& new_bounds) override;

 private:
  raw_ptr<ImmersiveModeControllerMac> controller_;
};

class ImmersiveModeControllerMac : public ImmersiveModeController,
                                   public views::FocusChangeListener,
                                   public views::ViewObserver,
                                   public views::WidgetObserver,
                                   public views::FocusTraversable,
                                   public views::ImmersiveModeRevealClient {
 public:
  class RevealedLock : public ImmersiveRevealedLock {
   public:
    explicit RevealedLock(base::WeakPtr<ImmersiveModeControllerMac> controller);

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

    ~RevealedLock() override;

   private:
    base::WeakPtr<ImmersiveModeControllerMac> controller_;
  };

  // If `separate_tab_strip` is true, the tab strip is split out into its own
  // widget separate from the overlay view so that it can live in the title bar.
  explicit ImmersiveModeControllerMac(bool separate_tab_strip);

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

  ~ImmersiveModeControllerMac() override;

  // ImmersiveModeController overrides:
  void Init(BrowserView* browser_view) override;
  void SetEnabled(bool enabled) override;
  bool IsEnabled() const override;
  bool ShouldHideTopViews() const override;
  bool IsRevealed() const override;
  int GetTopContainerVerticalOffset(
      const gfx::Size& top_container_size) const override;
  std::unique_ptr<ImmersiveRevealedLock> GetRevealedLock(
      AnimateReveal animate_reveal) override;
  void OnFindBarVisibleBoundsChanged(
      const gfx::Rect& new_visible_bounds_in_screen) override;
  bool ShouldStayImmersiveAfterExitingFullscreen() override;
  void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
  int GetMinimumContentOffset() const override;
  int GetExtraInfobarOffset() const override;
  void OnContentFullscreenChanged(bool is_content_fullscreen) override;

  // Set the widget id of the tab hosting widget. Set before calling SetEnabled.
  void SetTabNativeWidgetID(uint64_t widget_id);

  // views::FocusChangeListener implementation.
  void OnWillChangeFocus(views::View* focused_before,
                         views::View* focused_now) override;
  void OnDidChangeFocus(views::View* focused_before,
                        views::View* focused_now) override;

  // views::ViewObserver implementation
  void OnViewBoundsChanged(views::View* observed_view) override;

  // views::WidgetObserver implementation
  void OnWidgetDestroying(views::Widget* widget) override;

  // views::Traversable:
  views::FocusSearch* GetFocusSearch() override;
  views::FocusTraversable* GetFocusTraversableParent() override;
  views::View* GetFocusTraversableParentView() override;

  // views::ImmersiveModeRevealClient:
  void OnImmersiveModeToolbarRevealChanged(bool is_revealed) override;
  void OnImmersiveModeMenuBarRevealChanged(float reveal_amount) override;
  void OnAutohidingMenuBarHeightChanged(int menu_bar_height) override;

  BrowserView* browser_view() { return browser_view_; }

 private:
  friend class RevealedLock;

  void LockDestroyed();

  // Move children from `from_widget` to `to_widget`. Certain child widgets will
  // be held back from the move, see `ShouldMoveChild` for details.
  void MoveChildren(views::Widget* from_widget, views::Widget* to_widget);

  // Returns true if the child should be moved.
  bool ShouldMoveChild(views::Widget* child);

  raw_ptr<BrowserView> browser_view_ = nullptr;  // weak
  std::unique_ptr<ImmersiveRevealedLock> focus_lock_;
  bool enabled_ = false;
  base::ScopedObservation<views::View, views::ViewObserver>
      top_container_observation_{this};
  base::ScopedObservation<views::Widget, views::WidgetObserver>
      browser_frame_observation_{this};
  ImmersiveModeOverlayWidgetObserver overlay_widget_observer_{this};
  base::ScopedObservation<views::Widget, views::WidgetObserver>
      overlay_widget_observation_{&overlay_widget_observer_};
  remote_cocoa::mojom::NativeWidgetNSWindow* GetNSWindowMojo();
  std::unique_ptr<views::FocusSearch> focus_search_;

  // Used to hold the widget id for the tab hosting widget. This will be passed
  // to the remote_cocoa immersive mode controller where the tab strip will be
  // placed in the titlebar.
  uint64_t tab_native_widget_id_ = 0;

  // Whether the tab strip should be a separate widget.
  bool separate_tab_strip_ = false;
  // Height of the tab widget, used when resizing. Only non-zero if
  // `separate_tab_strip_` is true.
  int tab_widget_height_ = 0;
  // Total height of the overlay (including the separate tab strip if relevant).
  int overlay_height_ = 0;
  // Whether the find bar is currently visible.
  bool find_bar_visible_ = false;
  // Whether the toolbar is currently visible.
  bool is_revealed_ = false;
  // The proportion of the menubar/topchrome that has been revealed as a result
  // of the user mousing to the top of the screen.
  float reveal_amount_ = 0;
  // The height of the menubar, if the menubar should be accounted for when
  // compensating for reveal animations, otherwise 0. Situations where it is
  // not accounted for include screens with notches (where there is always
  // space reserved for it) and the "Always Show Menu Bar" system setting.
  int menu_bar_height_ = 0;

  base::WeakPtrFactory<ImmersiveModeControllerMac> weak_ptr_factory_;
};

#endif  // CHROME_BROWSER_UI_VIEWS_FRAME_IMMERSIVE_MODE_CONTROLLER_MAC_H_