Browse Source

refactor: NativeWindowViews should not be a View (#12750)

Cheng Zhao 7 years ago
parent
commit
c67d1b62e3

+ 63 - 194
atom/browser/native_window_views.cc

@@ -7,12 +7,12 @@
 #if defined(OS_WIN)
 #include <objbase.h>
 #endif
-#include <string>
+
 #include <vector>
 
 #include "atom/browser/api/atom_api_web_contents.h"
 #include "atom/browser/native_browser_view_views.h"
-#include "atom/browser/ui/views/menu_bar.h"
+#include "atom/browser/ui/views/root_view.h"
 #include "atom/browser/web_contents_preferences.h"
 #include "atom/browser/web_view_manager.h"
 #include "atom/browser/window_list.h"
@@ -23,7 +23,6 @@
 #include "brightray/browser/inspectable_web_contents.h"
 #include "brightray/browser/inspectable_web_contents_view.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/native_web_keyboard_event.h"
 #include "native_mate/dictionary.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/hit_test.h"
@@ -67,13 +66,6 @@ namespace atom {
 
 namespace {
 
-// The menu bar height in pixels.
-#if defined(OS_WIN)
-const int kMenuBarHeight = 20;
-#else
-const int kMenuBarHeight = 25;
-#endif
-
 #if defined(OS_WIN)
 void FlipWindowStyle(HWND handle, bool on, DWORD flag) {
   DWORD style = ::GetWindowLong(handle, GWL_STYLE);
@@ -85,33 +77,22 @@ void FlipWindowStyle(HWND handle, bool on, DWORD flag) {
 }
 #endif
 
-bool IsAltKey(const content::NativeWebKeyboardEvent& event) {
-  return event.windows_key_code == ui::VKEY_MENU;
-}
-bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
-  typedef content::NativeWebKeyboardEvent::Modifiers Modifiers;
-  int modifiers = event.GetModifiers();
-  modifiers &= ~Modifiers::kNumLockOn;
-  modifiers &= ~Modifiers::kCapsLockOn;
-  return (modifiers == Modifiers::kAltKey) ||
-         (modifiers == (Modifiers::kAltKey | Modifiers::kIsLeft)) ||
-         (modifiers == (Modifiers::kAltKey | Modifiers::kIsRight));
-}
-
 class NativeWindowClientView : public views::ClientView {
  public:
   NativeWindowClientView(views::Widget* widget,
-                         NativeWindowViews* contents_view)
-      : views::ClientView(widget, contents_view) {}
+                         views::View* root_view,
+                         NativeWindowViews* window)
+      : views::ClientView(widget, root_view), window_(window) {}
   virtual ~NativeWindowClientView() {}
 
   bool CanClose() override {
-    static_cast<NativeWindowViews*>(contents_view())
-        ->NotifyWindowCloseButtonClicked();
+    window_->NotifyWindowCloseButtonClicked();
     return false;
   }
 
  private:
+  NativeWindowViews* window_;
+
   DISALLOW_COPY_AND_ASSIGN(NativeWindowClientView);
 };
 
@@ -120,11 +101,9 @@ class NativeWindowClientView : public views::ClientView {
 NativeWindowViews::NativeWindowViews(const mate::Dictionary& options,
                                      NativeWindow* parent)
     : NativeWindow(options, parent),
+      root_view_(new RootView(this)),
       content_view_(nullptr),
       focused_view_(nullptr),
-      menu_bar_autohide_(false),
-      menu_bar_visible_(false),
-      menu_bar_alt_pressed_(false),
 #if defined(OS_WIN)
       checked_for_a11y_support_(false),
       thick_frame_(true),
@@ -137,11 +116,11 @@ NativeWindowViews::NativeWindowViews(const mate::Dictionary& options,
       maximizable_(true),
       minimizable_(true),
       fullscreenable_(true) {
-  // The root view is this class, it is managed by us.
-  set_owned_by_client();
-
   options.Get(options::kTitle, &title_);
-  options.Get(options::kAutoHideMenuBar, &menu_bar_autohide_);
+
+  bool menu_bar_autohide;
+  if (options.Get(options::kAutoHideMenuBar, &menu_bar_autohide))
+    root_view_->SetAutoHideMenuBar(menu_bar_autohide);
 
 #if defined(OS_WIN)
   // On Windows we rely on the CanResize() to indicate whether window can be
@@ -328,7 +307,7 @@ NativeWindowViews::~NativeWindowViews() {
 void NativeWindowViews::SetContentView(
     brightray::InspectableWebContents* web_contents) {
   if (content_view_) {
-    RemoveChildView(content_view_);
+    root_view_->RemoveChildView(content_view_);
     if (browser_view()) {
       content_view_->RemoveChildView(
           browser_view()->GetInspectableWebContentsView()->GetView());
@@ -337,8 +316,8 @@ void NativeWindowViews::SetContentView(
   }
   content_view_ = web_contents->GetView()->GetView();
   focused_view_ = web_contents->GetView()->GetWebView();
-  AddChildView(content_view_);
-  Layout();
+  root_view_->AddChildView(content_view_);
+  root_view_->Layout();
 }
 
 void NativeWindowViews::Close() {
@@ -550,7 +529,7 @@ void NativeWindowViews::SetFullScreen(bool fullscreen) {
   if (fullscreen)
     SetMenuBarVisibility(false);
   else
-    SetMenuBarVisibility(!menu_bar_autohide_);
+    SetMenuBarVisibility(!IsMenuBarAutoHide());
 #endif
 }
 
@@ -809,7 +788,7 @@ bool NativeWindowViews::IsKiosk() {
 
 void NativeWindowViews::SetBackgroundColor(SkColor background_color) {
   // web views' background color.
-  SetBackground(views::CreateSolidBackground(background_color));
+  root_view_->SetBackground(views::CreateSolidBackground(background_color));
 
 #if defined(OS_WIN)
   // Set the background color of native window.
@@ -901,22 +880,10 @@ void NativeWindowViews::SetFocusable(bool focusable) {
 }
 
 void NativeWindowViews::SetMenu(AtomMenuModel* menu_model) {
-  if (menu_model == nullptr) {
-    // Remove accelerators
-    accelerator_table_.clear();
-    GetFocusManager()->UnregisterAccelerators(this);
-    // and menu bar.
 #if defined(USE_X11)
+  if (menu_model == nullptr)
     global_menu_bar_.reset();
-#endif
-    SetMenuBarVisibility(false);
-    menu_bar_.reset();
-    return;
-  }
-
-  RegisterAccelerators(menu_model);
 
-#if defined(USE_X11)
   if (!global_menu_bar_ && ShouldUseGlobalMenuBar())
     global_menu_bar_.reset(new GlobalMenuBarX11(this));
 
@@ -927,40 +894,33 @@ void NativeWindowViews::SetMenu(AtomMenuModel* menu_model) {
   }
 #endif
 
-  // Do not show menu bar in frameless window.
-  if (!has_frame())
-    return;
-
-  if (!menu_bar_) {
-    gfx::Size content_size = GetContentSize();
-    menu_bar_.reset(new MenuBar(this));
-    menu_bar_->set_owned_by_client();
-
-    if (!menu_bar_autohide_) {
-      SetMenuBarVisibility(true);
-      if (use_content_size_) {
-        // Enlarge the size constraints for the menu.
-        extensions::SizeConstraints constraints = GetContentSizeConstraints();
-        if (constraints.HasMinimumSize()) {
-          gfx::Size min_size = constraints.GetMinimumSize();
-          min_size.set_height(min_size.height() + kMenuBarHeight);
-          constraints.set_minimum_size(min_size);
-        }
-        if (constraints.HasMaximumSize()) {
-          gfx::Size max_size = constraints.GetMaximumSize();
-          max_size.set_height(max_size.height() + kMenuBarHeight);
-          constraints.set_maximum_size(max_size);
-        }
-        SetContentSizeConstraints(constraints);
-
-        // Resize the window to make sure content size is not changed.
-        SetContentSize(content_size);
-      }
+  // Should reset content size when setting menu.
+  gfx::Size content_size = GetContentSize();
+  bool should_reset_size = use_content_size_ && has_frame() &&
+                           !IsMenuBarAutoHide() &&
+                           ((!!menu_model) != root_view_->HasMenu());
+
+  root_view_->SetMenu(menu_model);
+
+  if (should_reset_size) {
+    // Enlarge the size constraints for the menu.
+    int menu_bar_height = root_view_->GetMenuBarHeight();
+    extensions::SizeConstraints constraints = GetContentSizeConstraints();
+    if (constraints.HasMinimumSize()) {
+      gfx::Size min_size = constraints.GetMinimumSize();
+      min_size.set_height(min_size.height() + menu_bar_height);
+      constraints.set_minimum_size(min_size);
     }
-  }
+    if (constraints.HasMaximumSize()) {
+      gfx::Size max_size = constraints.GetMaximumSize();
+      max_size.set_height(max_size.height() + menu_bar_height);
+      constraints.set_maximum_size(max_size);
+    }
+    SetContentSizeConstraints(constraints);
 
-  menu_bar_->SetMenu(menu_model);
-  Layout();
+    // Resize the window to make sure content size is not changed.
+    SetContentSize(content_size);
+  }
 }
 
 void NativeWindowViews::SetBrowserView(NativeBrowserView* view) {
@@ -1036,35 +996,19 @@ void NativeWindowViews::SetOverlayIcon(const gfx::Image& overlay,
 }
 
 void NativeWindowViews::SetAutoHideMenuBar(bool auto_hide) {
-  menu_bar_autohide_ = auto_hide;
+  root_view_->SetAutoHideMenuBar(auto_hide);
 }
 
 bool NativeWindowViews::IsMenuBarAutoHide() {
-  return menu_bar_autohide_;
+  return root_view_->IsMenuBarAutoHide();
 }
 
 void NativeWindowViews::SetMenuBarVisibility(bool visible) {
-  if (!menu_bar_ || menu_bar_visible_ == visible)
-    return;
-
-  // Always show the accelerator when the auto-hide menu bar shows.
-  if (menu_bar_autohide_)
-    menu_bar_->SetAcceleratorVisibility(visible);
-
-  menu_bar_visible_ = visible;
-  if (visible) {
-    DCHECK_EQ(child_count(), 1);
-    AddChildView(menu_bar_.get());
-  } else {
-    DCHECK_EQ(child_count(), 2);
-    RemoveChildView(menu_bar_.get());
-  }
-
-  Layout();
+  root_view_->SetMenuBarVisibility(visible);
 }
 
 bool NativeWindowViews::IsMenuBarVisible() {
-  return menu_bar_visible_;
+  return root_view_->IsMenuBarVisible();
 }
 
 void NativeWindowViews::SetVisibleOnAllWorkspaces(bool visible) {
@@ -1102,9 +1046,10 @@ gfx::Rect NativeWindowViews::ContentBoundsToWindowBounds(
       widget()->non_client_view()->GetWindowBoundsForClientBounds(dpi_bounds));
 #endif
 
-  if (menu_bar_ && menu_bar_visible_) {
-    window_bounds.set_y(window_bounds.y() - kMenuBarHeight);
-    window_bounds.set_height(window_bounds.height() + kMenuBarHeight);
+  if (root_view_->HasMenu() && root_view_->IsMenuBarVisible()) {
+    int menu_bar_height = root_view_->GetMenuBarHeight();
+    window_bounds.set_y(window_bounds.y() - menu_bar_height);
+    window_bounds.set_height(window_bounds.height() + menu_bar_height);
   }
   return window_bounds;
 }
@@ -1130,9 +1075,10 @@ gfx::Rect NativeWindowViews::WindowBoundsToContentBounds(
       display::win::ScreenWin::ScreenToDIPSize(hwnd, content_bounds.size()));
 #endif
 
-  if (menu_bar_ && menu_bar_visible_) {
-    content_bounds.set_y(content_bounds.y() + kMenuBarHeight);
-    content_bounds.set_height(content_bounds.height() - kMenuBarHeight);
+  if (root_view_->HasMenu() && root_view_->IsMenuBarVisible()) {
+    int menu_bar_height = root_view_->GetMenuBarHeight();
+    content_bounds.set_y(content_bounds.y() + menu_bar_height);
+    content_bounds.set_height(content_bounds.height() - menu_bar_height);
   }
   return content_bounds;
 }
@@ -1175,10 +1121,10 @@ void NativeWindowViews::OnWidgetActivationChanged(views::Widget* changed_widget,
                  GetWeakPtr()));
 
   // Hide menu bar when window is blured.
-  if (!active && menu_bar_autohide_ && menu_bar_visible_)
+  if (!active && IsMenuBarAutoHide() && IsMenuBarVisible())
     SetMenuBarVisibility(false);
 
-  menu_bar_alt_pressed_ = false;
+  root_view_->ResetAltState();
 }
 
 void NativeWindowViews::OnWidgetBoundsChanged(views::Widget* changed_widget,
@@ -1255,7 +1201,7 @@ bool NativeWindowViews::ShouldHandleSystemCommands() const {
 }
 
 views::View* NativeWindowViews::GetContentsView() {
-  return this;
+  return root_view_.get();
 }
 
 bool NativeWindowViews::ShouldDescendIntoChildForEventHandling(
@@ -1277,7 +1223,7 @@ bool NativeWindowViews::ShouldDescendIntoChildForEventHandling(
 }
 
 views::ClientView* NativeWindowViews::CreateClientView(views::Widget* widget) {
-  return new NativeWindowClientView(widget, this);
+  return new NativeWindowClientView(widget, root_view_.get(), this);
 }
 
 views::NonClientFrameView* NativeWindowViews::CreateNonClientFrameView(
@@ -1304,86 +1250,9 @@ void NativeWindowViews::OnWidgetMove() {
 void NativeWindowViews::HandleKeyboardEvent(
     content::WebContents*,
     const content::NativeWebKeyboardEvent& event) {
-  keyboard_event_handler_->HandleKeyboardEvent(event, GetFocusManager());
-
-  if (!menu_bar_)
-    return;
-
-  // Show accelerator when "Alt" is pressed.
-  if (menu_bar_visible_ && IsAltKey(event))
-    menu_bar_->SetAcceleratorVisibility(event.GetType() ==
-                                        blink::WebInputEvent::kRawKeyDown);
-
-  // Show the submenu when "Alt+Key" is pressed.
-  if (event.GetType() == blink::WebInputEvent::kRawKeyDown &&
-      !IsAltKey(event) && IsAltModifier(event)) {
-    if (!menu_bar_visible_ &&
-        (menu_bar_->HasAccelerator(event.windows_key_code)))
-      SetMenuBarVisibility(true);
-    menu_bar_->ActivateAccelerator(event.windows_key_code);
-    return;
-  }
-
-  if (!menu_bar_autohide_)
-    return;
-
-  // Toggle the menu bar only when a single Alt is released.
-  if (event.GetType() == blink::WebInputEvent::kRawKeyDown && IsAltKey(event)) {
-    // When a single Alt is pressed:
-    menu_bar_alt_pressed_ = true;
-  } else if (event.GetType() == blink::WebInputEvent::kKeyUp &&
-             IsAltKey(event) && menu_bar_alt_pressed_) {
-    // When a single Alt is released right after a Alt is pressed:
-    menu_bar_alt_pressed_ = false;
-    SetMenuBarVisibility(!menu_bar_visible_);
-  } else {
-    // When any other keys except single Alt have been pressed/released:
-    menu_bar_alt_pressed_ = false;
-  }
-}
-
-void NativeWindowViews::Layout() {
-  if (!content_view_)  // Not ready yet.
-    return;
-
-  const auto size = GetContentsBounds().size();
-  const auto menu_bar_bounds =
-      menu_bar_visible_ ? gfx::Rect(0, 0, size.width(), kMenuBarHeight)
-                        : gfx::Rect();
-  if (menu_bar_) {
-    menu_bar_->SetBoundsRect(menu_bar_bounds);
-  }
-
-  content_view_->SetBoundsRect(
-      gfx::Rect(0, menu_bar_bounds.height(), size.width(),
-                size.height() - menu_bar_bounds.height()));
-}
-
-gfx::Size NativeWindowViews::GetMinimumSize() const {
-  return NativeWindow::GetMinimumSize();
-}
-
-gfx::Size NativeWindowViews::GetMaximumSize() const {
-  return NativeWindow::GetMaximumSize();
-}
-
-bool NativeWindowViews::AcceleratorPressed(const ui::Accelerator& accelerator) {
-  return accelerator_util::TriggerAcceleratorTableCommand(&accelerator_table_,
-                                                          accelerator);
-}
-
-void NativeWindowViews::RegisterAccelerators(AtomMenuModel* menu_model) {
-  // Clear previous accelerators.
-  views::FocusManager* focus_manager = GetFocusManager();
-  accelerator_table_.clear();
-  focus_manager->UnregisterAccelerators(this);
-
-  // Register accelerators with focus manager.
-  accelerator_util::GenerateAcceleratorTable(&accelerator_table_, menu_model);
-  for (const auto& iter : accelerator_table_) {
-    focus_manager->RegisterAccelerator(
-        iter.first, ui::AcceleratorManager::kNormalPriority, this);
-  }
+  keyboard_event_handler_->HandleKeyboardEvent(event,
+                                               root_view_->GetFocusManager());
+  root_view_->HandleKeyEvent(event);
 }
 
 ui::WindowShowState NativeWindowViews::GetRestoredState() {

+ 3 - 21
atom/browser/native_window_views.h

@@ -9,9 +9,7 @@
 
 #include <set>
 #include <string>
-#include <vector>
 
-#include "atom/browser/ui/accelerator_util.h"
 #include "ui/views/widget/widget_observer.h"
 
 #if defined(OS_WIN)
@@ -27,7 +25,7 @@ class UnhandledKeyboardEventHandler;
 namespace atom {
 
 class GlobalMenuBarX11;
-class MenuBar;
+class RootView;
 class WindowStateWatcher;
 
 #if defined(OS_WIN)
@@ -40,7 +38,6 @@ class NativeWindowViews : public NativeWindow,
 #if defined(OS_WIN)
                           public MessageHandlerDelegate,
 #endif
-                          public views::View,
                           public views::WidgetObserver {
  public:
   NativeWindowViews(const mate::Dictionary& options, NativeWindow* parent);
@@ -195,26 +192,14 @@ class NativeWindowViews : public NativeWindow,
       content::WebContents*,
       const content::NativeWebKeyboardEvent& event) override;
 
-  // views::View:
-  void Layout() override;
-  gfx::Size GetMinimumSize() const override;
-  gfx::Size GetMaximumSize() const override;
-  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
-
-  // Register accelerators supported by the menu model.
-  void RegisterAccelerators(AtomMenuModel* menu_model);
-
   // Returns the restore state for the window.
   ui::WindowShowState GetRestoredState();
 
+  std::unique_ptr<RootView> root_view_;
+
   views::View* content_view_;  // Weak ref.
   views::View* focused_view_;  // The view should be focused by default.
 
-  std::unique_ptr<MenuBar> menu_bar_;
-  bool menu_bar_autohide_;
-  bool menu_bar_visible_;
-  bool menu_bar_alt_pressed_;
-
   // The "resizable" flag on Linux is implemented by setting size constraints,
   // we need to make sure size constraints are restored when window becomes
   // resizable again. This is also used on Windows, to keep taskbar resize
@@ -281,9 +266,6 @@ class NativeWindowViews : public NativeWindow,
   // Handles unhandled keyboard messages coming back from the renderer process.
   std::unique_ptr<views::UnhandledKeyboardEventHandler> keyboard_event_handler_;
 
-  // Map from accelerator to menu item's command id.
-  accelerator_util::AcceleratorTable accelerator_table_;
-
   // For custom drag, the whole window is non-draggable and the draggable region
   // has to been explicitly provided.
   std::unique_ptr<SkRegion> draggable_region_;  // used in custom drag.

+ 197 - 0
atom/browser/ui/views/root_view.cc

@@ -0,0 +1,197 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "atom/browser/ui/views/root_view.h"
+
+#include "atom/browser/native_window_views.h"
+#include "atom/browser/ui/views/menu_bar.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+
+namespace atom {
+
+namespace {
+
+// The menu bar height in pixels.
+#if defined(OS_WIN)
+const int kMenuBarHeight = 20;
+#else
+const int kMenuBarHeight = 25;
+#endif
+
+bool IsAltKey(const content::NativeWebKeyboardEvent& event) {
+  return event.windows_key_code == ui::VKEY_MENU;
+}
+
+bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
+  typedef content::NativeWebKeyboardEvent::Modifiers Modifiers;
+  int modifiers = event.GetModifiers();
+  modifiers &= ~Modifiers::kNumLockOn;
+  modifiers &= ~Modifiers::kCapsLockOn;
+  return (modifiers == Modifiers::kAltKey) ||
+         (modifiers == (Modifiers::kAltKey | Modifiers::kIsLeft)) ||
+         (modifiers == (Modifiers::kAltKey | Modifiers::kIsRight));
+}
+
+}  // namespace
+
+RootView::RootView(NativeWindow* window) : window_(window) {
+  set_owned_by_client();
+}
+
+RootView::~RootView() {}
+
+void RootView::SetMenu(AtomMenuModel* menu_model) {
+  if (menu_model == nullptr) {
+    // Remove accelerators
+    accelerator_table_.clear();
+    GetFocusManager()->UnregisterAccelerators(this);
+    // and menu bar.
+    SetMenuBarVisibility(false);
+    menu_bar_.reset();
+    return;
+  }
+
+  RegisterAccelerators(menu_model);
+
+  // Do not show menu bar in frameless window.
+  if (!window_->has_frame())
+    return;
+
+  if (!menu_bar_) {
+    menu_bar_.reset(new MenuBar(this));
+    menu_bar_->set_owned_by_client();
+    if (!menu_bar_autohide_)
+      SetMenuBarVisibility(true);
+  }
+
+  menu_bar_->SetMenu(menu_model);
+  Layout();
+}
+
+bool RootView::HasMenu() const {
+  return !!menu_bar_;
+}
+
+int RootView::GetMenuBarHeight() const {
+  return kMenuBarHeight;
+}
+
+void RootView::SetAutoHideMenuBar(bool auto_hide) {
+  menu_bar_autohide_ = auto_hide;
+}
+
+bool RootView::IsMenuBarAutoHide() const {
+  return menu_bar_autohide_;
+}
+
+void RootView::SetMenuBarVisibility(bool visible) {
+  if (!menu_bar_ || menu_bar_visible_ == visible)
+    return;
+
+  // Always show the accelerator when the auto-hide menu bar shows.
+  if (menu_bar_autohide_)
+    menu_bar_->SetAcceleratorVisibility(visible);
+
+  menu_bar_visible_ = visible;
+  if (visible) {
+    DCHECK_EQ(child_count(), 1);
+    AddChildView(menu_bar_.get());
+  } else {
+    DCHECK_EQ(child_count(), 2);
+    RemoveChildView(menu_bar_.get());
+  }
+
+  Layout();
+}
+
+bool RootView::IsMenuBarVisible() const {
+  return menu_bar_visible_;
+}
+
+void RootView::HandleKeyEvent(const content::NativeWebKeyboardEvent& event) {
+  if (!menu_bar_)
+    return;
+
+  // Show accelerator when "Alt" is pressed.
+  if (menu_bar_visible_ && IsAltKey(event))
+    menu_bar_->SetAcceleratorVisibility(event.GetType() ==
+                                        blink::WebInputEvent::kRawKeyDown);
+
+  // Show the submenu when "Alt+Key" is pressed.
+  if (event.GetType() == blink::WebInputEvent::kRawKeyDown &&
+      !IsAltKey(event) && IsAltModifier(event)) {
+    if (!menu_bar_visible_ &&
+        (menu_bar_->HasAccelerator(event.windows_key_code)))
+      SetMenuBarVisibility(true);
+    menu_bar_->ActivateAccelerator(event.windows_key_code);
+    return;
+  }
+
+  if (!menu_bar_autohide_)
+    return;
+
+  // Toggle the menu bar only when a single Alt is released.
+  if (event.GetType() == blink::WebInputEvent::kRawKeyDown && IsAltKey(event)) {
+    // When a single Alt is pressed:
+    menu_bar_alt_pressed_ = true;
+  } else if (event.GetType() == blink::WebInputEvent::kKeyUp &&
+             IsAltKey(event) && menu_bar_alt_pressed_) {
+    // When a single Alt is released right after a Alt is pressed:
+    menu_bar_alt_pressed_ = false;
+    SetMenuBarVisibility(!menu_bar_visible_);
+  } else {
+    // When any other keys except single Alt have been pressed/released:
+    menu_bar_alt_pressed_ = false;
+  }
+}
+
+void RootView::ResetAltState() {
+  menu_bar_alt_pressed_ = false;
+}
+
+void RootView::Layout() {
+  views::View* content_view =
+      static_cast<NativeWindowViews*>(window_)->content_view();
+  if (!content_view)  // Not ready yet.
+    return;
+
+  const auto menu_bar_bounds =
+      menu_bar_visible_ ? gfx::Rect(0, 0, size().width(), kMenuBarHeight)
+                        : gfx::Rect();
+  if (menu_bar_)
+    menu_bar_->SetBoundsRect(menu_bar_bounds);
+
+  content_view->SetBoundsRect(
+      gfx::Rect(0, menu_bar_bounds.height(), size().width(),
+                size().height() - menu_bar_bounds.height()));
+}
+
+gfx::Size RootView::GetMinimumSize() const {
+  return window_->GetMinimumSize();
+}
+
+gfx::Size RootView::GetMaximumSize() const {
+  return window_->GetMaximumSize();
+}
+
+bool RootView::AcceleratorPressed(const ui::Accelerator& accelerator) {
+  return accelerator_util::TriggerAcceleratorTableCommand(&accelerator_table_,
+                                                          accelerator);
+}
+
+void RootView::RegisterAccelerators(AtomMenuModel* menu_model) {
+  // Clear previous accelerators.
+  views::FocusManager* focus_manager = GetFocusManager();
+  accelerator_table_.clear();
+  focus_manager->UnregisterAccelerators(this);
+
+  // Register accelerators with focus manager.
+  accelerator_util::GenerateAcceleratorTable(&accelerator_table_, menu_model);
+  for (const auto& iter : accelerator_table_) {
+    focus_manager->RegisterAccelerator(
+        iter.first, ui::AcceleratorManager::kNormalPriority, this);
+  }
+}
+
+}  // namespace atom

+ 65 - 0
atom/browser/ui/views/root_view.h

@@ -0,0 +1,65 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ATOM_BROWSER_UI_VIEWS_ROOT_VIEW_H_
+#define ATOM_BROWSER_UI_VIEWS_ROOT_VIEW_H_
+
+#include <memory>
+
+#include "atom/browser/ui/accelerator_util.h"
+#include "ui/views/view.h"
+
+namespace content {
+struct NativeWebKeyboardEvent;
+}
+
+namespace atom {
+
+class AtomMenuModel;
+class MenuBar;
+class NativeWindow;
+
+class RootView : public views::View {
+ public:
+  explicit RootView(NativeWindow* window);
+  ~RootView() override;
+
+  void SetMenu(AtomMenuModel* menu_model);
+  bool HasMenu() const;
+  int GetMenuBarHeight() const;
+  void SetAutoHideMenuBar(bool auto_hide);
+  bool IsMenuBarAutoHide() const;
+  void SetMenuBarVisibility(bool visible);
+  bool IsMenuBarVisible() const;
+  void HandleKeyEvent(const content::NativeWebKeyboardEvent& event);
+  void ResetAltState();
+
+  // views::View:
+  void Layout() override;
+  gfx::Size GetMinimumSize() const override;
+  gfx::Size GetMaximumSize() const override;
+  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
+
+ private:
+  // Register accelerators supported by the menu model.
+  void RegisterAccelerators(AtomMenuModel* menu_model);
+
+  // Parent window, weak ref.
+  NativeWindow* window_;
+
+  // Menu bar.
+  std::unique_ptr<MenuBar> menu_bar_;
+  bool menu_bar_autohide_ = false;
+  bool menu_bar_visible_ = false;
+  bool menu_bar_alt_pressed_ = false;
+
+  // Map from accelerator to menu item's command id.
+  accelerator_util::AcceleratorTable accelerator_table_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootView);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_UI_VIEWS_ROOT_VIEW_H_

+ 2 - 0
filenames.gypi

@@ -368,6 +368,8 @@
       'atom/browser/ui/views/menu_model_adapter.h',
       'atom/browser/ui/views/native_frame_view.cc',
       'atom/browser/ui/views/native_frame_view.h',
+      'atom/browser/ui/views/root_view.cc',
+      'atom/browser/ui/views/root_view.h',
       'atom/browser/ui/views/submenu_button.cc',
       'atom/browser/ui/views/submenu_button.h',
       'atom/browser/ui/views/win_frame_view.cc',