Browse Source

views: Implement the window menu bar.

Cheng Zhao 10 years ago
parent
commit
0f18d63f7f

+ 2 - 0
atom.gyp

@@ -143,6 +143,8 @@
       'atom/browser/ui/views/linux_frame_view.h',
       'atom/browser/ui/views/menu_bar.cc',
       'atom/browser/ui/views/menu_bar.h',
+      'atom/browser/ui/views/menu_delegate.cc',
+      'atom/browser/ui/views/menu_delegate.h',
       'atom/browser/ui/views/menu_layout.cc',
       'atom/browser/ui/views/menu_layout.h',
       'atom/browser/ui/views/win_frame_view.cc',

+ 3 - 0
atom/browser/native_window_views.cc

@@ -304,6 +304,9 @@ void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
     AddChildViewAt(menu_bar_, 0);
     SetContentSize(content_size);
   }
+
+  menu_bar_->SetMenu(menu_model);
+  Layout();
 }
 
 gfx::NativeWindow NativeWindowViews::GetNativeWindow() {

+ 65 - 4
atom/browser/ui/views/menu_bar.cc

@@ -4,7 +4,11 @@
 
 #include "atom/browser/ui/views/menu_bar.h"
 
-#include "ui/gfx/canvas.h"
+#include "atom/browser/ui/views/menu_delegate.h"
+#include "ui/base/models/menu_model.h"
+#include "ui/views/background.h"
+#include "ui/views/controls/button/menu_button.h"
+#include "ui/views/layout/box_layout.h"
 
 namespace atom {
 
@@ -17,18 +21,75 @@ const SkColor kDefaultColor = SkColorSetARGB(255, 233, 233, 233);
 
 }  // namespace
 
-MenuBar::MenuBar() {
+MenuBar::MenuBar()
+    : menu_model_(NULL) {
+  set_background(views::Background::CreateSolidBackground(kDefaultColor));
+  SetLayoutManager(new views::BoxLayout(
+      views::BoxLayout::kHorizontal, 0, 0, 0));
 }
 
 MenuBar::~MenuBar() {
 }
 
-void MenuBar::Paint(gfx::Canvas* canvas) {
-  canvas->FillRect(bounds(), kDefaultColor);
+void MenuBar::SetMenu(ui::MenuModel* model) {
+  menu_model_ = model;
+  RemoveAllChildViews(true);
+
+  for (int i = 0; i < model->GetItemCount(); ++i) {
+    views::MenuButton* button =
+        new views::MenuButton(this, model->GetLabelAt(i), this, false);
+    button->set_tag(i);
+    AddChildView(button);
+  }
+}
+
+int MenuBar::GetItemCount() const {
+  return menu_model_->GetItemCount();
+}
+
+bool MenuBar::GetMenuButtonFromScreenPoint(const gfx::Point& point,
+                                           ui::MenuModel** menu_model,
+                                           views::MenuButton** button) {
+  gfx::Point location(point);
+  views::View::ConvertPointFromScreen(this, &location);
+
+  if (location.x() < 0 || location.x() >= width() || location.y() < 0 ||
+      location.y() >= height())
+    return false;
+
+  for (int i = 0; i < child_count(); ++i) {
+    views::View* view = child_at(i);
+    if (view->bounds().Contains(location) &&
+        (menu_model_->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU)) {
+      *menu_model = menu_model_->GetSubmenuModelAt(i);
+      *button = static_cast<views::MenuButton*>(view);
+      return true;
+    }
+  }
+
+  return false;
 }
 
 const char* MenuBar::GetClassName() const {
   return kViewClassName;
 }
 
+void MenuBar::ButtonPressed(views::Button* sender, const ui::Event& event) {
+}
+
+void MenuBar::OnMenuButtonClicked(views::View* source,
+                                  const gfx::Point& point) {
+  if (!menu_model_)
+    return;
+
+  views::MenuButton* button = static_cast<views::MenuButton*>(source);
+  int id = button->tag();
+  ui::MenuModel::ItemType type = menu_model_->GetTypeAt(id);
+  if (type != ui::MenuModel::TYPE_SUBMENU)
+    return;
+
+  menu_delegate_.reset(new MenuDelegate(this));
+  menu_delegate_->RunMenu(menu_model_->GetSubmenuModelAt(id), button);
+}
+
 }  // namespace atom

+ 37 - 2
atom/browser/ui/views/menu_bar.h

@@ -5,21 +5,56 @@
 #ifndef ATOM_BROWSER_UI_VIEWS_MENU_BAR_H_
 #define ATOM_BROWSER_UI_VIEWS_MENU_BAR_H_
 
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/menu_button_listener.h"
 #include "ui/views/view.h"
 
+namespace ui {
+class MenuModel;
+}
+
+namespace views {
+class MenuButton;
+}
+
 namespace atom {
 
-class MenuBar : public views::View {
+class MenuDelegate;
+
+class MenuBar : public views::View,
+                public views::ButtonListener,
+                public views::MenuButtonListener {
  public:
   MenuBar();
   virtual ~MenuBar();
 
+  // Replaces current menu with a new one.
+  void SetMenu(ui::MenuModel* menu_model);
+
+  // Returns there are how many items in the root menu.
+  int GetItemCount() const;
+
+  // Get the menu under specified screen point.
+  bool GetMenuButtonFromScreenPoint(const gfx::Point& point,
+                                    ui::MenuModel** menu_model,
+                                    views::MenuButton** button);
+
  protected:
   // views::View:
-  virtual void Paint(gfx::Canvas* canvas) OVERRIDE;
   virtual const char* GetClassName() const OVERRIDE;
 
+  // views::ButtonListener:
+  virtual void ButtonPressed(views::Button* sender,
+                             const ui::Event& event) OVERRIDE;
+
+  // views::MenuButtonListener:
+  virtual void OnMenuButtonClicked(views::View* source,
+                                   const gfx::Point& point) OVERRIDE;
+
  private:
+  ui::MenuModel* menu_model_;
+  scoped_ptr<MenuDelegate> menu_delegate_;
+
   DISALLOW_COPY_AND_ASSIGN(MenuBar);
 };
 

+ 127 - 0
atom/browser/ui/views/menu_delegate.cc

@@ -0,0 +1,127 @@
+// Copyright (c) 2014 GitHub, Inc. All rights reserved.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "atom/browser/ui/views/menu_delegate.h"
+
+#include "atom/browser/ui/views/menu_bar.h"
+#include "base/stl_util.h"
+#include "ui/views/controls/button/menu_button.h"
+#include "ui/views/controls/menu/menu_model_adapter.h"
+#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/widget/widget.h"
+
+namespace atom {
+
+MenuDelegate::MenuDelegate(MenuBar* menu_bar)
+    : menu_bar_(menu_bar),
+      id_(-1),
+      items_(menu_bar_->GetItemCount()),
+      delegates_(menu_bar_->GetItemCount()) {
+}
+
+MenuDelegate::~MenuDelegate() {
+  STLDeleteElements(&delegates_);
+}
+
+void MenuDelegate::RunMenu(ui::MenuModel* model, views::MenuButton* button) {
+  gfx::Point screen_loc;
+  views::View::ConvertPointToScreen(button, &screen_loc);
+  // Subtract 1 from the height to make the popup flush with the button border.
+  gfx::Rect bounds(screen_loc.x(), screen_loc.y(), button->width(),
+                   button->height() - 1);
+
+  id_ = button->tag();
+  views::MenuItemView* item = BuildMenu(model);
+
+  menu_runner_.reset(new views::MenuRunner(item));
+  views::MenuRunner::RunResult result = menu_runner_->RunMenuAt(
+      button->GetWidget()->GetTopLevelWidget(),
+      button,
+      bounds,
+      views::MenuItemView::TOPRIGHT,
+      ui::MENU_SOURCE_MOUSE,
+      views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU);
+  if (result == views::MenuRunner::MENU_DELETED)
+    LOG(ERROR) << "Menu deleted when running";
+}
+
+views::MenuItemView* MenuDelegate::BuildMenu(ui::MenuModel* model) {
+  DCHECK_GE(id_, 0);
+  DCHECK_LT(id_, items_.size());
+
+  if (!items_[id_]) {
+    views::MenuModelAdapter* delegate = new views::MenuModelAdapter(model);
+    delegates_[id_] = delegate;
+
+    views::MenuItemView* item = new views::MenuItemView(this);
+    delegate->BuildMenu(item);
+    items_[id_] = item;
+  }
+
+  return items_[id_];
+}
+
+void MenuDelegate::ExecuteCommand(int id) {
+  delegate()->ExecuteCommand(id);
+}
+
+void MenuDelegate::ExecuteCommand(int id, int mouse_event_flags) {
+  delegate()->ExecuteCommand(id, mouse_event_flags);
+}
+
+bool MenuDelegate::IsTriggerableEvent(views::MenuItemView* source,
+                                      const ui::Event& e) {
+  return delegate()->IsTriggerableEvent(source, e);
+}
+
+bool MenuDelegate::GetAccelerator(int id, ui::Accelerator* accelerator) {
+  return delegate()->GetAccelerator(id, accelerator);
+}
+
+base::string16 MenuDelegate::GetLabel(int id) const {
+  return delegate()->GetLabel(id);
+}
+
+const gfx::FontList* MenuDelegate::GetLabelFontList(int id) const {
+  return delegate()->GetLabelFontList(id);
+}
+
+bool MenuDelegate::IsCommandEnabled(int id) const {
+  return delegate()->IsCommandEnabled(id);
+}
+
+bool MenuDelegate::IsItemChecked(int id) const {
+  return delegate()->IsItemChecked(id);
+}
+
+void MenuDelegate::SelectionChanged(views::MenuItemView* menu) {
+  delegate()->SelectionChanged(menu);
+}
+
+void MenuDelegate::WillShowMenu(views::MenuItemView* menu) {
+  delegate()->WillShowMenu(menu);
+}
+
+void MenuDelegate::WillHideMenu(views::MenuItemView* menu) {
+  delegate()->WillHideMenu(menu);
+}
+
+views::MenuItemView* MenuDelegate::GetSiblingMenu(
+    views::MenuItemView* menu,
+    const gfx::Point& screen_point,
+    views::MenuItemView::AnchorPosition* anchor,
+    bool* has_mnemonics,
+    views::MenuButton** button) {
+  ui::MenuModel* model;
+  if (!menu_bar_->GetMenuButtonFromScreenPoint(screen_point, &model, button))
+    return NULL;
+
+  *anchor = views::MenuItemView::TOPLEFT;
+  *has_mnemonics = true;
+
+  id_ = (*button)->tag();
+  return BuildMenu(model);
+}
+
+}  // namespace atom

+ 76 - 0
atom/browser/ui/views/menu_delegate.h

@@ -0,0 +1,76 @@
+// Copyright (c) 2014 GitHub, Inc. All rights reserved.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ATOM_BROWSER_UI_VIEWS_MENU_DELEGATE_H_
+#define ATOM_BROWSER_UI_VIEWS_MENU_DELEGATE_H_
+
+#include <vector>
+
+#include "ui/views/controls/menu/menu_delegate.h"
+
+namespace views {
+class MenuModelAdapter;
+class MenuRunner;
+}
+
+namespace ui {
+class MenuModel;
+}
+
+namespace atom {
+
+class MenuBar;
+
+class MenuDelegate : public views::MenuDelegate {
+ public:
+  MenuDelegate(MenuBar* menu_bar);
+  virtual ~MenuDelegate();
+
+  void RunMenu(ui::MenuModel* model, views::MenuButton* button);
+
+ protected:
+  // views::MenuDelegate:
+  virtual void ExecuteCommand(int id) OVERRIDE;
+  virtual void ExecuteCommand(int id, int mouse_event_flags) OVERRIDE;
+  virtual bool IsTriggerableEvent(views::MenuItemView* source,
+                                  const ui::Event& e) OVERRIDE;
+  virtual bool GetAccelerator(int id,
+                              ui::Accelerator* accelerator) OVERRIDE;
+  virtual base::string16 GetLabel(int id) const OVERRIDE;
+  virtual const gfx::FontList* GetLabelFontList(int id) const OVERRIDE;
+  virtual bool IsCommandEnabled(int id) const OVERRIDE;
+  virtual bool IsItemChecked(int id) const OVERRIDE;
+  virtual void SelectionChanged(views::MenuItemView* menu) OVERRIDE;
+  virtual void WillShowMenu(views::MenuItemView* menu) OVERRIDE;
+  virtual void WillHideMenu(views::MenuItemView* menu) OVERRIDE;
+  virtual views::MenuItemView* GetSiblingMenu(
+      views::MenuItemView* menu,
+      const gfx::Point& screen_point,
+      views::MenuItemView::AnchorPosition* anchor,
+      bool* has_mnemonics,
+      views::MenuButton** button);
+
+ private:
+  // Gets the cached menu item view from the model.
+  views::MenuItemView* BuildMenu(ui::MenuModel* model);
+
+  // Returns delegate for current item.
+  views::MenuDelegate* delegate() const { return delegates_[id_]; }
+
+  MenuBar* menu_bar_;
+  scoped_ptr<views::MenuRunner> menu_runner_;
+
+  // Current item's id.
+  int id_;
+  // Cached menu items, managed by MenuRunner.
+  std::vector<views::MenuItemView*> items_;
+  // Cached menu delegates for each menu item, managed by us.
+  std::vector<views::MenuDelegate*> delegates_;
+
+  DISALLOW_COPY_AND_ASSIGN(MenuDelegate);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_BROWSER_UI_VIEWS_MENU_DELEGATE_H_