refactor: Improve accessibility of menus (#15302)
* refactor: improve menubar keyboard accessibility * fix: create a temporary widget for tray icon context menu * fix: focus menu bar with Alt when autohide is off * fix: make menu bar focus work more like the native menus * fix: only focus menu bar if it's not already focused * fix: track accelerator registration to avoid duplicates * docs: add docs for & notation in app menu item names * fix: only try to activate accelerator if it's registered * fix: add friend to monitor window focus change * style: add <memory> include
This commit is contained in:
parent
00daff6ac8
commit
894ae1b3f5
13 changed files with 405 additions and 140 deletions
|
@ -5,13 +5,16 @@
|
||||||
#include "atom/browser/ui/views/menu_bar.h"
|
#include "atom/browser/ui/views/menu_bar.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "atom/browser/ui/views/menu_delegate.h"
|
|
||||||
#include "atom/browser/ui/views/submenu_button.h"
|
#include "atom/browser/ui/views/submenu_button.h"
|
||||||
|
#include "atom/common/keyboard_util.h"
|
||||||
#include "ui/base/models/menu_model.h"
|
#include "ui/base/models/menu_model.h"
|
||||||
#include "ui/views/background.h"
|
#include "ui/views/background.h"
|
||||||
#include "ui/views/layout/box_layout.h"
|
#include "ui/views/layout/box_layout.h"
|
||||||
|
#include "ui/views/widget/widget.h"
|
||||||
|
|
||||||
#if defined(USE_X11)
|
#if defined(USE_X11)
|
||||||
#include "chrome/browser/ui/libgtkui/gtk_util.h"
|
#include "chrome/browser/ui/libgtkui/gtk_util.h"
|
||||||
|
@ -32,17 +35,36 @@ const SkColor kDefaultColor = SkColorSetARGB(255, 233, 233, 233);
|
||||||
|
|
||||||
const char MenuBar::kViewClassName[] = "ElectronMenuBar";
|
const char MenuBar::kViewClassName[] = "ElectronMenuBar";
|
||||||
|
|
||||||
MenuBar::MenuBar(views::View* window)
|
MenuBarColorUpdater::MenuBarColorUpdater(MenuBar* menu_bar)
|
||||||
: background_color_(kDefaultColor), window_(window) {
|
: menu_bar_(menu_bar) {}
|
||||||
|
|
||||||
|
MenuBarColorUpdater::~MenuBarColorUpdater() {}
|
||||||
|
|
||||||
|
void MenuBarColorUpdater::OnDidChangeFocus(views::View* focused_before,
|
||||||
|
views::View* focused_now) {
|
||||||
|
if (menu_bar_) {
|
||||||
|
// if we've changed window focus, update menu bar colors
|
||||||
|
const auto had_focus = menu_bar_->has_focus_;
|
||||||
|
menu_bar_->has_focus_ = focused_now != nullptr;
|
||||||
|
if (menu_bar_->has_focus_ != had_focus)
|
||||||
|
menu_bar_->UpdateViewColors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuBar::MenuBar(RootView* window)
|
||||||
|
: background_color_(kDefaultColor),
|
||||||
|
window_(window),
|
||||||
|
color_updater_(new MenuBarColorUpdater(this)) {
|
||||||
RefreshColorCache();
|
RefreshColorCache();
|
||||||
UpdateViewColors();
|
UpdateViewColors();
|
||||||
|
SetFocusBehavior(FocusBehavior::ALWAYS);
|
||||||
SetLayoutManager(
|
SetLayoutManager(
|
||||||
std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal));
|
std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal));
|
||||||
window_->GetFocusManager()->AddFocusChangeListener(this);
|
window_->GetFocusManager()->AddFocusChangeListener(color_updater_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuBar::~MenuBar() {
|
MenuBar::~MenuBar() {
|
||||||
window_->GetFocusManager()->RemoveFocusChangeListener(this);
|
window_->GetFocusManager()->RemoveFocusChangeListener(color_updater_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuBar::SetMenu(AtomMenuModel* model) {
|
void MenuBar::SetMenu(AtomMenuModel* model) {
|
||||||
|
@ -96,6 +118,116 @@ bool MenuBar::GetMenuButtonFromScreenPoint(const gfx::Point& screenPoint,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MenuBar::OnBeforeExecuteCommand() {
|
||||||
|
RemovePaneFocus();
|
||||||
|
window_->RestoreFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuBar::OnMenuClosed() {
|
||||||
|
SetAcceleratorVisibility(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuBar::AcceleratorPressed(const ui::Accelerator& accelerator) {
|
||||||
|
views::View* focused_view = GetFocusManager()->GetFocusedView();
|
||||||
|
if (!ContainsForFocusSearch(this, focused_view))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (accelerator.key_code()) {
|
||||||
|
case ui::VKEY_MENU:
|
||||||
|
case ui::VKEY_ESCAPE: {
|
||||||
|
RemovePaneFocus();
|
||||||
|
window_->RestoreFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case ui::VKEY_LEFT:
|
||||||
|
GetFocusManager()->AdvanceFocus(true);
|
||||||
|
return true;
|
||||||
|
case ui::VKEY_RIGHT:
|
||||||
|
GetFocusManager()->AdvanceFocus(false);
|
||||||
|
return true;
|
||||||
|
case ui::VKEY_HOME:
|
||||||
|
GetFocusManager()->SetFocusedViewWithReason(
|
||||||
|
GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal);
|
||||||
|
return true;
|
||||||
|
case ui::VKEY_END:
|
||||||
|
GetFocusManager()->SetFocusedViewWithReason(
|
||||||
|
GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal);
|
||||||
|
return true;
|
||||||
|
default: {
|
||||||
|
auto children = GetChildrenInZOrder();
|
||||||
|
for (int i = 0, n = children.size(); i < n; ++i) {
|
||||||
|
auto* button = static_cast<SubmenuButton*>(children[i]);
|
||||||
|
bool shifted = false;
|
||||||
|
auto keycode =
|
||||||
|
atom::KeyboardCodeFromCharCode(button->accelerator(), &shifted);
|
||||||
|
|
||||||
|
if (keycode == accelerator.key_code()) {
|
||||||
|
const gfx::Point p(0, 0);
|
||||||
|
auto event = accelerator.ToKeyEvent();
|
||||||
|
OnMenuButtonClicked(button, p, &event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuBar::SetPaneFocus(views::View* initial_focus) {
|
||||||
|
bool result = views::AccessiblePaneView::SetPaneFocus(initial_focus);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
auto children = GetChildrenInZOrder();
|
||||||
|
std::set<ui::KeyboardCode> reg;
|
||||||
|
for (int i = 0, n = children.size(); i < n; ++i) {
|
||||||
|
auto* button = static_cast<SubmenuButton*>(children[i]);
|
||||||
|
bool shifted = false;
|
||||||
|
auto keycode =
|
||||||
|
atom::KeyboardCodeFromCharCode(button->accelerator(), &shifted);
|
||||||
|
|
||||||
|
// We want the menu items to activate if the user presses the accelerator
|
||||||
|
// key, even without alt, since we are now focused on the menu bar
|
||||||
|
if (keycode != ui::VKEY_UNKNOWN && reg.find(keycode) != reg.end()) {
|
||||||
|
reg.insert(keycode);
|
||||||
|
focus_manager()->RegisterAccelerator(
|
||||||
|
ui::Accelerator(keycode, ui::EF_NONE),
|
||||||
|
ui::AcceleratorManager::kNormalPriority, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to remove focus / hide menu bar when alt is pressed again
|
||||||
|
focus_manager()->RegisterAccelerator(
|
||||||
|
ui::Accelerator(ui::VKEY_MENU, ui::EF_ALT_DOWN),
|
||||||
|
ui::AcceleratorManager::kNormalPriority, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MenuBar::RemovePaneFocus() {
|
||||||
|
views::AccessiblePaneView::RemovePaneFocus();
|
||||||
|
SetAcceleratorVisibility(false);
|
||||||
|
|
||||||
|
auto children = GetChildrenInZOrder();
|
||||||
|
std::set<ui::KeyboardCode> unreg;
|
||||||
|
for (int i = 0, n = children.size(); i < n; ++i) {
|
||||||
|
auto* button = static_cast<SubmenuButton*>(children[i]);
|
||||||
|
bool shifted = false;
|
||||||
|
auto keycode =
|
||||||
|
atom::KeyboardCodeFromCharCode(button->accelerator(), &shifted);
|
||||||
|
|
||||||
|
if (keycode != ui::VKEY_UNKNOWN && unreg.find(keycode) != unreg.end()) {
|
||||||
|
unreg.insert(keycode);
|
||||||
|
focus_manager()->UnregisterAccelerator(
|
||||||
|
ui::Accelerator(keycode, ui::EF_NONE), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
focus_manager()->UnregisterAccelerator(
|
||||||
|
ui::Accelerator(ui::VKEY_MENU, ui::EF_ALT_DOWN), this);
|
||||||
|
}
|
||||||
|
|
||||||
const char* MenuBar::GetClassName() const {
|
const char* MenuBar::GetClassName() const {
|
||||||
return kViewClassName;
|
return kViewClassName;
|
||||||
}
|
}
|
||||||
|
@ -119,9 +251,16 @@ void MenuBar::OnMenuButtonClicked(views::MenuButton* source,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetFocusManager()->SetFocusedViewWithReason(
|
||||||
|
source, views::FocusManager::kReasonFocusTraversal);
|
||||||
|
|
||||||
// Deleted in MenuDelegate::OnMenuClosed
|
// Deleted in MenuDelegate::OnMenuClosed
|
||||||
MenuDelegate* menu_delegate = new MenuDelegate(this);
|
MenuDelegate* menu_delegate = new MenuDelegate(this);
|
||||||
menu_delegate->RunMenu(menu_model_->GetSubmenuModelAt(id), source);
|
menu_delegate->RunMenu(menu_model_->GetSubmenuModelAt(id), source,
|
||||||
|
event != nullptr && event->IsKeyEvent()
|
||||||
|
? ui::MENU_SOURCE_KEYBOARD
|
||||||
|
: ui::MENU_SOURCE_MOUSE);
|
||||||
|
menu_delegate->AddObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuBar::RefreshColorCache(const ui::NativeTheme* theme) {
|
void MenuBar::RefreshColorCache(const ui::NativeTheme* theme) {
|
||||||
|
@ -151,14 +290,6 @@ void MenuBar::OnNativeThemeChanged(const ui::NativeTheme* theme) {
|
||||||
UpdateViewColors();
|
UpdateViewColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuBar::OnDidChangeFocus(View* focused_before, View* focused_now) {
|
|
||||||
// if we've changed focus, update our view
|
|
||||||
const auto had_focus = has_focus_;
|
|
||||||
has_focus_ = focused_now != nullptr;
|
|
||||||
if (has_focus_ != had_focus)
|
|
||||||
UpdateViewColors();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MenuBar::RebuildChildren() {
|
void MenuBar::RebuildChildren() {
|
||||||
RemoveAllChildViews(true);
|
RemoveAllChildViews(true);
|
||||||
for (int i = 0, n = GetItemCount(); i < n; ++i) {
|
for (int i = 0, n = GetItemCount(); i < n; ++i) {
|
||||||
|
|
|
@ -5,7 +5,12 @@
|
||||||
#ifndef ATOM_BROWSER_UI_VIEWS_MENU_BAR_H_
|
#ifndef ATOM_BROWSER_UI_VIEWS_MENU_BAR_H_
|
||||||
#define ATOM_BROWSER_UI_VIEWS_MENU_BAR_H_
|
#define ATOM_BROWSER_UI_VIEWS_MENU_BAR_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "atom/browser/ui/atom_menu_model.h"
|
#include "atom/browser/ui/atom_menu_model.h"
|
||||||
|
#include "atom/browser/ui/views/menu_delegate.h"
|
||||||
|
#include "atom/browser/ui/views/root_view.h"
|
||||||
|
#include "ui/views/accessible_pane_view.h"
|
||||||
#include "ui/views/controls/button/menu_button_listener.h"
|
#include "ui/views/controls/button/menu_button_listener.h"
|
||||||
#include "ui/views/focus/focus_manager.h"
|
#include "ui/views/focus/focus_manager.h"
|
||||||
#include "ui/views/view.h"
|
#include "ui/views/view.h"
|
||||||
|
@ -16,15 +21,27 @@ class MenuButton;
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
class MenuDelegate;
|
class MenuBarColorUpdater : public views::FocusChangeListener {
|
||||||
|
public:
|
||||||
|
explicit MenuBarColorUpdater(MenuBar* menu_bar);
|
||||||
|
~MenuBarColorUpdater() override;
|
||||||
|
|
||||||
class MenuBar : public views::View,
|
void OnDidChangeFocus(views::View* focused_before,
|
||||||
|
views::View* focused_now) override;
|
||||||
|
void OnWillChangeFocus(views::View* focused_before,
|
||||||
|
views::View* focused_now) override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MenuBar* menu_bar_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuBar : public views::AccessiblePaneView,
|
||||||
public views::MenuButtonListener,
|
public views::MenuButtonListener,
|
||||||
public views::FocusChangeListener {
|
public atom::MenuDelegate::Observer {
|
||||||
public:
|
public:
|
||||||
static const char kViewClassName[];
|
static const char kViewClassName[];
|
||||||
|
|
||||||
explicit MenuBar(views::View* window);
|
explicit MenuBar(RootView* window);
|
||||||
~MenuBar() override;
|
~MenuBar() override;
|
||||||
|
|
||||||
// Replaces current menu with a new one.
|
// Replaces current menu with a new one.
|
||||||
|
@ -47,6 +64,15 @@ class MenuBar : public views::View,
|
||||||
AtomMenuModel** menu_model,
|
AtomMenuModel** menu_model,
|
||||||
views::MenuButton** button);
|
views::MenuButton** button);
|
||||||
|
|
||||||
|
// atom::MenuDelegate::Observer:
|
||||||
|
void OnBeforeExecuteCommand() override;
|
||||||
|
void OnMenuClosed() override;
|
||||||
|
|
||||||
|
// views::AccessiblePaneView:
|
||||||
|
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
|
||||||
|
bool SetPaneFocus(views::View* initial_focus) override;
|
||||||
|
void RemovePaneFocus() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// views::View:
|
// views::View:
|
||||||
const char* GetClassName() const override;
|
const char* GetClassName() const override;
|
||||||
|
@ -57,11 +83,9 @@ class MenuBar : public views::View,
|
||||||
const ui::Event* event) override;
|
const ui::Event* event) override;
|
||||||
void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
|
void OnNativeThemeChanged(const ui::NativeTheme* theme) override;
|
||||||
|
|
||||||
// views::FocusChangeListener:
|
|
||||||
void OnDidChangeFocus(View* focused_before, View* focused_now) override;
|
|
||||||
void OnWillChangeFocus(View* focused_before, View* focused_now) override {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class MenuBarColorUpdater;
|
||||||
|
|
||||||
void RebuildChildren();
|
void RebuildChildren();
|
||||||
void UpdateViewColors();
|
void UpdateViewColors();
|
||||||
|
|
||||||
|
@ -72,13 +96,15 @@ class MenuBar : public views::View,
|
||||||
SkColor disabled_color_;
|
SkColor disabled_color_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
views::View* window_ = nullptr;
|
RootView* window_ = nullptr;
|
||||||
AtomMenuModel* menu_model_ = nullptr;
|
AtomMenuModel* menu_model_ = nullptr;
|
||||||
|
|
||||||
View* FindAccelChild(base::char16 key);
|
View* FindAccelChild(base::char16 key);
|
||||||
|
|
||||||
bool has_focus_ = true;
|
bool has_focus_ = true;
|
||||||
|
|
||||||
|
std::unique_ptr<MenuBarColorUpdater> color_updater_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(MenuBar);
|
DISALLOW_COPY_AND_ASSIGN(MenuBar);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,17 +14,24 @@
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
MenuDelegate::MenuDelegate(MenuBar* menu_bar) : menu_bar_(menu_bar), id_(-1) {}
|
MenuDelegate::MenuDelegate(MenuBar* menu_bar)
|
||||||
|
: menu_bar_(menu_bar), id_(-1), hold_first_switch_(false) {}
|
||||||
|
|
||||||
MenuDelegate::~MenuDelegate() {}
|
MenuDelegate::~MenuDelegate() {}
|
||||||
|
|
||||||
void MenuDelegate::RunMenu(AtomMenuModel* model, views::MenuButton* button) {
|
void MenuDelegate::RunMenu(AtomMenuModel* model,
|
||||||
|
views::MenuButton* button,
|
||||||
|
ui::MenuSourceType source_type) {
|
||||||
gfx::Point screen_loc;
|
gfx::Point screen_loc;
|
||||||
views::View::ConvertPointToScreen(button, &screen_loc);
|
views::View::ConvertPointToScreen(button, &screen_loc);
|
||||||
// Subtract 1 from the height to make the popup flush with the button border.
|
// 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(),
|
gfx::Rect bounds(screen_loc.x(), screen_loc.y(), button->width(),
|
||||||
button->height() - 1);
|
button->height() - 1);
|
||||||
|
|
||||||
|
if (source_type == ui::MENU_SOURCE_KEYBOARD) {
|
||||||
|
hold_first_switch_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
id_ = button->tag();
|
id_ = button->tag();
|
||||||
adapter_.reset(new MenuModelAdapter(model));
|
adapter_.reset(new MenuModelAdapter(model));
|
||||||
|
|
||||||
|
@ -35,15 +42,18 @@ void MenuDelegate::RunMenu(AtomMenuModel* model, views::MenuButton* button) {
|
||||||
item,
|
item,
|
||||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS));
|
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS));
|
||||||
menu_runner_->RunMenuAt(button->GetWidget()->GetTopLevelWidget(), button,
|
menu_runner_->RunMenuAt(button->GetWidget()->GetTopLevelWidget(), button,
|
||||||
bounds, views::MENU_ANCHOR_TOPRIGHT,
|
bounds, views::MENU_ANCHOR_TOPRIGHT, source_type);
|
||||||
ui::MENU_SOURCE_MOUSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuDelegate::ExecuteCommand(int id) {
|
void MenuDelegate::ExecuteCommand(int id) {
|
||||||
|
for (Observer& obs : observers_)
|
||||||
|
obs.OnBeforeExecuteCommand();
|
||||||
adapter_->ExecuteCommand(id);
|
adapter_->ExecuteCommand(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuDelegate::ExecuteCommand(int id, int mouse_event_flags) {
|
void MenuDelegate::ExecuteCommand(int id, int mouse_event_flags) {
|
||||||
|
for (Observer& obs : observers_)
|
||||||
|
obs.OnBeforeExecuteCommand();
|
||||||
adapter_->ExecuteCommand(id, mouse_event_flags);
|
adapter_->ExecuteCommand(id, mouse_event_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +99,9 @@ void MenuDelegate::WillHideMenu(views::MenuItemView* menu) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuDelegate::OnMenuClosed(views::MenuItemView* menu) {
|
void MenuDelegate::OnMenuClosed(views::MenuItemView* menu) {
|
||||||
|
for (Observer& obs : observers_)
|
||||||
|
obs.OnMenuClosed();
|
||||||
|
|
||||||
// Only switch to new menu when current menu is closed.
|
// Only switch to new menu when current menu is closed.
|
||||||
if (button_to_open_)
|
if (button_to_open_)
|
||||||
button_to_open_->Activate(nullptr);
|
button_to_open_->Activate(nullptr);
|
||||||
|
@ -101,6 +114,11 @@ views::MenuItemView* MenuDelegate::GetSiblingMenu(
|
||||||
views::MenuAnchorPosition* anchor,
|
views::MenuAnchorPosition* anchor,
|
||||||
bool* has_mnemonics,
|
bool* has_mnemonics,
|
||||||
views::MenuButton**) {
|
views::MenuButton**) {
|
||||||
|
if (hold_first_switch_) {
|
||||||
|
hold_first_switch_ = false;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(zcbenz): We should follow Chromium's logics on implementing the
|
// TODO(zcbenz): We should follow Chromium's logics on implementing the
|
||||||
// sibling menu switches, this code is almost a hack.
|
// sibling menu switches, this code is almost a hack.
|
||||||
views::MenuButton* button;
|
views::MenuButton* button;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "atom/browser/ui/atom_menu_model.h"
|
#include "atom/browser/ui/atom_menu_model.h"
|
||||||
|
#include "base/observer_list.h"
|
||||||
#include "ui/views/controls/menu/menu_delegate.h"
|
#include "ui/views/controls/menu/menu_delegate.h"
|
||||||
|
|
||||||
namespace views {
|
namespace views {
|
||||||
|
@ -23,7 +24,19 @@ class MenuDelegate : public views::MenuDelegate {
|
||||||
explicit MenuDelegate(MenuBar* menu_bar);
|
explicit MenuDelegate(MenuBar* menu_bar);
|
||||||
~MenuDelegate() override;
|
~MenuDelegate() override;
|
||||||
|
|
||||||
void RunMenu(AtomMenuModel* model, views::MenuButton* button);
|
void RunMenu(AtomMenuModel* model,
|
||||||
|
views::MenuButton* button,
|
||||||
|
ui::MenuSourceType source_type);
|
||||||
|
|
||||||
|
class Observer {
|
||||||
|
public:
|
||||||
|
virtual void OnBeforeExecuteCommand() = 0;
|
||||||
|
virtual void OnMenuClosed() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddObserver(Observer* obs) { observers_.AddObserver(obs); }
|
||||||
|
|
||||||
|
void RemoveObserver(const Observer* obs) { observers_.RemoveObserver(obs); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// views::MenuDelegate:
|
// views::MenuDelegate:
|
||||||
|
@ -55,6 +68,9 @@ class MenuDelegate : public views::MenuDelegate {
|
||||||
|
|
||||||
// The menu button to switch to.
|
// The menu button to switch to.
|
||||||
views::MenuButton* button_to_open_ = nullptr;
|
views::MenuButton* button_to_open_ = nullptr;
|
||||||
|
bool hold_first_switch_;
|
||||||
|
|
||||||
|
base::ObserverList<Observer> observers_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(MenuDelegate);
|
DISALLOW_COPY_AND_ASSIGN(MenuDelegate);
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,7 +35,9 @@ bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
RootView::RootView(NativeWindow* window) : window_(window) {
|
RootView::RootView(NativeWindow* window)
|
||||||
|
: window_(window),
|
||||||
|
last_focused_view_tracker_(std::make_unique<views::ViewTracker>()) {
|
||||||
set_owned_by_client();
|
set_owned_by_client();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,10 +91,6 @@ void RootView::SetMenuBarVisibility(bool visible) {
|
||||||
if (!window_->content_view() || !menu_bar_ || menu_bar_visible_ == visible)
|
if (!window_->content_view() || !menu_bar_ || menu_bar_visible_ == visible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Always show the accelerator when the auto-hide menu bar shows.
|
|
||||||
if (menu_bar_autohide_)
|
|
||||||
menu_bar_->SetAcceleratorVisibility(visible);
|
|
||||||
|
|
||||||
menu_bar_visible_ = visible;
|
menu_bar_visible_ = visible;
|
||||||
if (visible) {
|
if (visible) {
|
||||||
DCHECK_EQ(child_count(), 1);
|
DCHECK_EQ(child_count(), 1);
|
||||||
|
@ -121,16 +119,20 @@ void RootView::HandleKeyEvent(const content::NativeWebKeyboardEvent& event) {
|
||||||
// Show the submenu when "Alt+Key" is pressed.
|
// Show the submenu when "Alt+Key" is pressed.
|
||||||
if (event.GetType() == blink::WebInputEvent::kRawKeyDown &&
|
if (event.GetType() == blink::WebInputEvent::kRawKeyDown &&
|
||||||
!IsAltKey(event) && IsAltModifier(event)) {
|
!IsAltKey(event) && IsAltModifier(event)) {
|
||||||
if (!menu_bar_visible_ &&
|
if (menu_bar_->HasAccelerator(event.windows_key_code)) {
|
||||||
(menu_bar_->HasAccelerator(event.windows_key_code)))
|
if (!menu_bar_visible_) {
|
||||||
SetMenuBarVisibility(true);
|
SetMenuBarVisibility(true);
|
||||||
menu_bar_->ActivateAccelerator(event.windows_key_code);
|
|
||||||
|
View* focused_view = GetFocusManager()->GetFocusedView();
|
||||||
|
last_focused_view_tracker_->SetView(focused_view);
|
||||||
|
menu_bar_->RequestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
menu_bar_->ActivateAccelerator(event.windows_key_code);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!menu_bar_autohide_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Toggle the menu bar only when a single Alt is released.
|
// Toggle the menu bar only when a single Alt is released.
|
||||||
if (event.GetType() == blink::WebInputEvent::kRawKeyDown && IsAltKey(event)) {
|
if (event.GetType() == blink::WebInputEvent::kRawKeyDown && IsAltKey(event)) {
|
||||||
// When a single Alt is pressed:
|
// When a single Alt is pressed:
|
||||||
|
@ -139,13 +141,30 @@ void RootView::HandleKeyEvent(const content::NativeWebKeyboardEvent& event) {
|
||||||
IsAltKey(event) && menu_bar_alt_pressed_) {
|
IsAltKey(event) && menu_bar_alt_pressed_) {
|
||||||
// When a single Alt is released right after a Alt is pressed:
|
// When a single Alt is released right after a Alt is pressed:
|
||||||
menu_bar_alt_pressed_ = false;
|
menu_bar_alt_pressed_ = false;
|
||||||
SetMenuBarVisibility(!menu_bar_visible_);
|
if (menu_bar_autohide_)
|
||||||
|
SetMenuBarVisibility(!menu_bar_visible_);
|
||||||
|
|
||||||
|
View* focused_view = GetFocusManager()->GetFocusedView();
|
||||||
|
last_focused_view_tracker_->SetView(focused_view);
|
||||||
|
menu_bar_->RequestFocus();
|
||||||
|
// Show accelerators when menu bar is focused
|
||||||
|
menu_bar_->SetAcceleratorVisibility(true);
|
||||||
} else {
|
} else {
|
||||||
// When any other keys except single Alt have been pressed/released:
|
// When any other keys except single Alt have been pressed/released:
|
||||||
menu_bar_alt_pressed_ = false;
|
menu_bar_alt_pressed_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RootView::RestoreFocus() {
|
||||||
|
View* last_focused_view = last_focused_view_tracker_->view();
|
||||||
|
if (last_focused_view) {
|
||||||
|
GetFocusManager()->SetFocusedViewWithReason(
|
||||||
|
last_focused_view, views::FocusManager::kReasonFocusRestore);
|
||||||
|
}
|
||||||
|
if (menu_bar_autohide_)
|
||||||
|
SetMenuBarVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
void RootView::ResetAltState() {
|
void RootView::ResetAltState() {
|
||||||
menu_bar_alt_pressed_ = false;
|
menu_bar_alt_pressed_ = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "atom/browser/ui/accelerator_util.h"
|
#include "atom/browser/ui/accelerator_util.h"
|
||||||
#include "ui/views/view.h"
|
#include "ui/views/view.h"
|
||||||
|
#include "ui/views/view_tracker.h"
|
||||||
|
|
||||||
namespace content {
|
namespace content {
|
||||||
struct NativeWebKeyboardEvent;
|
struct NativeWebKeyboardEvent;
|
||||||
|
@ -34,6 +35,7 @@ class RootView : public views::View {
|
||||||
bool IsMenuBarVisible() const;
|
bool IsMenuBarVisible() const;
|
||||||
void HandleKeyEvent(const content::NativeWebKeyboardEvent& event);
|
void HandleKeyEvent(const content::NativeWebKeyboardEvent& event);
|
||||||
void ResetAltState();
|
void ResetAltState();
|
||||||
|
void RestoreFocus();
|
||||||
|
|
||||||
// views::View:
|
// views::View:
|
||||||
void Layout() override;
|
void Layout() override;
|
||||||
|
@ -57,6 +59,8 @@ class RootView : public views::View {
|
||||||
// Map from accelerator to menu item's command id.
|
// Map from accelerator to menu item's command id.
|
||||||
accelerator_util::AcceleratorTable accelerator_table_;
|
accelerator_util::AcceleratorTable accelerator_table_;
|
||||||
|
|
||||||
|
std::unique_ptr<views::ViewTracker> last_focused_view_tracker_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(RootView);
|
DISALLOW_COPY_AND_ASSIGN(RootView);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -71,15 +71,21 @@ void SubmenuButton::SetUnderlineColor(SkColor color) {
|
||||||
underline_color_ = color;
|
underline_color_ = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SubmenuButton::GetAccessibleNodeData(ui::AXNodeData* node_data) {
|
||||||
|
node_data->SetName(accessible_name());
|
||||||
|
node_data->role = ax::mojom::Role::kPopUpButton;
|
||||||
|
}
|
||||||
|
|
||||||
void SubmenuButton::PaintButtonContents(gfx::Canvas* canvas) {
|
void SubmenuButton::PaintButtonContents(gfx::Canvas* canvas) {
|
||||||
views::MenuButton::PaintButtonContents(canvas);
|
views::MenuButton::PaintButtonContents(canvas);
|
||||||
|
|
||||||
if (show_underline_ && (underline_start_ != underline_end_)) {
|
if (show_underline_ && (underline_start_ != underline_end_)) {
|
||||||
int padding = (width() - text_width_) / 2;
|
float padding = (width() - text_width_) / 2;
|
||||||
int underline_height = (height() + text_height_) / 2 - 2;
|
float underline_height = (height() + text_height_) / 2 - 2;
|
||||||
canvas->DrawLine(gfx::Point(underline_start_ + padding, underline_height),
|
canvas->DrawSharpLine(
|
||||||
gfx::Point(underline_end_ + padding, underline_height),
|
gfx::PointF(underline_start_ + padding, underline_height),
|
||||||
underline_color_);
|
gfx::PointF(underline_end_ + padding, underline_height),
|
||||||
|
underline_color_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "ui/accessibility/ax_node_data.h"
|
||||||
#include "ui/views/animation/ink_drop_highlight.h"
|
#include "ui/views/animation/ink_drop_highlight.h"
|
||||||
#include "ui/views/controls/button/menu_button.h"
|
#include "ui/views/controls/button/menu_button.h"
|
||||||
|
|
||||||
|
@ -25,6 +26,8 @@ class SubmenuButton : public views::MenuButton {
|
||||||
|
|
||||||
base::char16 accelerator() const { return accelerator_; }
|
base::char16 accelerator() const { return accelerator_; }
|
||||||
|
|
||||||
|
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
|
||||||
|
|
||||||
// views::MenuButton:
|
// views::MenuButton:
|
||||||
void PaintButtonContents(gfx::Canvas* canvas) override;
|
void PaintButtonContents(gfx::Canvas* canvas) override;
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,16 @@
|
||||||
#include "ui/gfx/geometry/rect.h"
|
#include "ui/gfx/geometry/rect.h"
|
||||||
#include "ui/gfx/image/image.h"
|
#include "ui/gfx/image/image.h"
|
||||||
#include "ui/views/controls/menu/menu_runner.h"
|
#include "ui/views/controls/menu/menu_runner.h"
|
||||||
|
#include "ui/views/widget/widget.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
NotifyIcon::NotifyIcon(NotifyIconHost* host, UINT id, HWND window, UINT message)
|
NotifyIcon::NotifyIcon(NotifyIconHost* host, UINT id, HWND window, UINT message)
|
||||||
: host_(host), icon_id_(id), window_(window), message_id_(message) {
|
: host_(host),
|
||||||
|
icon_id_(id),
|
||||||
|
window_(window),
|
||||||
|
message_id_(message),
|
||||||
|
weak_factory_(this) {
|
||||||
NOTIFYICONDATA icon_data;
|
NOTIFYICONDATA icon_data;
|
||||||
InitIconData(&icon_data);
|
InitIconData(&icon_data);
|
||||||
icon_data.uFlags |= NIF_MESSAGE;
|
icon_data.uFlags |= NIF_MESSAGE;
|
||||||
|
@ -142,10 +147,25 @@ void NotifyIcon::PopUpContextMenu(const gfx::Point& pos,
|
||||||
if (pos.IsOrigin())
|
if (pos.IsOrigin())
|
||||||
rect.set_origin(display::Screen::GetScreen()->GetCursorScreenPoint());
|
rect.set_origin(display::Screen::GetScreen()->GetCursorScreenPoint());
|
||||||
|
|
||||||
|
// Create a widget for the menu, otherwise we get no keyboard events, which
|
||||||
|
// is required for accessibility.
|
||||||
|
widget_.reset(new views::Widget());
|
||||||
|
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
|
||||||
|
params.ownership =
|
||||||
|
views::Widget::InitParams::Ownership::WIDGET_OWNS_NATIVE_WIDGET;
|
||||||
|
params.bounds = gfx::Rect(0, 0, 0, 0);
|
||||||
|
params.force_software_compositing = true;
|
||||||
|
|
||||||
|
widget_->Init(params);
|
||||||
|
|
||||||
|
widget_->Show();
|
||||||
|
widget_->Activate();
|
||||||
menu_runner_.reset(new views::MenuRunner(
|
menu_runner_.reset(new views::MenuRunner(
|
||||||
menu_model != nullptr ? menu_model : menu_model_,
|
menu_model != nullptr ? menu_model : menu_model_,
|
||||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS));
|
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS,
|
||||||
menu_runner_->RunMenuAt(NULL, NULL, rect, views::MENU_ANCHOR_TOPLEFT,
|
base::Bind(&NotifyIcon::OnContextMenuClosed,
|
||||||
|
weak_factory_.GetWeakPtr())));
|
||||||
|
menu_runner_->RunMenuAt(widget_.get(), NULL, rect, views::MENU_ANCHOR_TOPLEFT,
|
||||||
ui::MENU_SOURCE_MOUSE);
|
ui::MENU_SOURCE_MOUSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,4 +192,8 @@ void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) {
|
||||||
icon_data->uID = icon_id_;
|
icon_data->uID = icon_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotifyIcon::OnContextMenuClosed() {
|
||||||
|
widget_->Close();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace atom
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "atom/browser/ui/tray_icon.h"
|
#include "atom/browser/ui/tray_icon.h"
|
||||||
#include "base/compiler_specific.h"
|
#include "base/compiler_specific.h"
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
#include "base/memory/weak_ptr.h"
|
||||||
#include "base/win/scoped_gdi_object.h"
|
#include "base/win/scoped_gdi_object.h"
|
||||||
|
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
@ -23,7 +24,8 @@ class Point;
|
||||||
|
|
||||||
namespace views {
|
namespace views {
|
||||||
class MenuRunner;
|
class MenuRunner;
|
||||||
}
|
class Widget;
|
||||||
|
} // namespace views
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
|
@ -63,6 +65,7 @@ class NotifyIcon : public TrayIcon {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitIconData(NOTIFYICONDATA* icon_data);
|
void InitIconData(NOTIFYICONDATA* icon_data);
|
||||||
|
void OnContextMenuClosed();
|
||||||
|
|
||||||
// The tray that owns us. Weak.
|
// The tray that owns us. Weak.
|
||||||
NotifyIconHost* host_;
|
NotifyIconHost* host_;
|
||||||
|
@ -85,6 +88,12 @@ class NotifyIcon : public TrayIcon {
|
||||||
// Context menu associated with this icon (if any).
|
// Context menu associated with this icon (if any).
|
||||||
std::unique_ptr<views::MenuRunner> menu_runner_;
|
std::unique_ptr<views::MenuRunner> menu_runner_;
|
||||||
|
|
||||||
|
// Temporary widget for the context menu, needed for keyboard event capture.
|
||||||
|
std::unique_ptr<views::Widget> widget_;
|
||||||
|
|
||||||
|
// WeakPtrFactory for CloseClosure safety.
|
||||||
|
base::WeakPtrFactory<NotifyIcon> weak_factory_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(NotifyIcon);
|
DISALLOW_COPY_AND_ASSIGN(NotifyIcon);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,94 @@ namespace atom {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Return key code of the char, and also determine whether the SHIFT key is
|
// Return key code represented by |str|.
|
||||||
// pressed.
|
ui::KeyboardCode KeyboardCodeFromKeyIdentifier(const std::string& s,
|
||||||
|
bool* shifted) {
|
||||||
|
std::string str = base::ToLowerASCII(s);
|
||||||
|
if (str == "ctrl" || str == "control") {
|
||||||
|
return ui::VKEY_CONTROL;
|
||||||
|
} else if (str == "super" || str == "cmd" || str == "command" ||
|
||||||
|
str == "meta") {
|
||||||
|
return ui::VKEY_COMMAND;
|
||||||
|
} else if (str == "commandorcontrol" || str == "cmdorctrl") {
|
||||||
|
#if defined(OS_MACOSX)
|
||||||
|
return ui::VKEY_COMMAND;
|
||||||
|
#else
|
||||||
|
return ui::VKEY_CONTROL;
|
||||||
|
#endif
|
||||||
|
} else if (str == "alt" || str == "option") {
|
||||||
|
return ui::VKEY_MENU;
|
||||||
|
} else if (str == "shift") {
|
||||||
|
return ui::VKEY_SHIFT;
|
||||||
|
} else if (str == "altgr") {
|
||||||
|
return ui::VKEY_ALTGR;
|
||||||
|
} else if (str == "plus") {
|
||||||
|
*shifted = true;
|
||||||
|
return ui::VKEY_OEM_PLUS;
|
||||||
|
} else if (str == "tab") {
|
||||||
|
return ui::VKEY_TAB;
|
||||||
|
} else if (str == "space") {
|
||||||
|
return ui::VKEY_SPACE;
|
||||||
|
} else if (str == "backspace") {
|
||||||
|
return ui::VKEY_BACK;
|
||||||
|
} else if (str == "delete") {
|
||||||
|
return ui::VKEY_DELETE;
|
||||||
|
} else if (str == "insert") {
|
||||||
|
return ui::VKEY_INSERT;
|
||||||
|
} else if (str == "enter" || str == "return") {
|
||||||
|
return ui::VKEY_RETURN;
|
||||||
|
} else if (str == "up") {
|
||||||
|
return ui::VKEY_UP;
|
||||||
|
} else if (str == "down") {
|
||||||
|
return ui::VKEY_DOWN;
|
||||||
|
} else if (str == "left") {
|
||||||
|
return ui::VKEY_LEFT;
|
||||||
|
} else if (str == "right") {
|
||||||
|
return ui::VKEY_RIGHT;
|
||||||
|
} else if (str == "home") {
|
||||||
|
return ui::VKEY_HOME;
|
||||||
|
} else if (str == "end") {
|
||||||
|
return ui::VKEY_END;
|
||||||
|
} else if (str == "pageup") {
|
||||||
|
return ui::VKEY_PRIOR;
|
||||||
|
} else if (str == "pagedown") {
|
||||||
|
return ui::VKEY_NEXT;
|
||||||
|
} else if (str == "esc" || str == "escape") {
|
||||||
|
return ui::VKEY_ESCAPE;
|
||||||
|
} else if (str == "volumemute") {
|
||||||
|
return ui::VKEY_VOLUME_MUTE;
|
||||||
|
} else if (str == "volumeup") {
|
||||||
|
return ui::VKEY_VOLUME_UP;
|
||||||
|
} else if (str == "volumedown") {
|
||||||
|
return ui::VKEY_VOLUME_DOWN;
|
||||||
|
} else if (str == "medianexttrack") {
|
||||||
|
return ui::VKEY_MEDIA_NEXT_TRACK;
|
||||||
|
} else if (str == "mediaprevioustrack") {
|
||||||
|
return ui::VKEY_MEDIA_PREV_TRACK;
|
||||||
|
} else if (str == "mediastop") {
|
||||||
|
return ui::VKEY_MEDIA_STOP;
|
||||||
|
} else if (str == "mediaplaypause") {
|
||||||
|
return ui::VKEY_MEDIA_PLAY_PAUSE;
|
||||||
|
} else if (str == "printscreen") {
|
||||||
|
return ui::VKEY_SNAPSHOT;
|
||||||
|
} else if (str.size() > 1 && str[0] == 'f') {
|
||||||
|
// F1 - F24.
|
||||||
|
int n;
|
||||||
|
if (base::StringToInt(str.c_str() + 1, &n) && n > 0 && n < 25) {
|
||||||
|
return static_cast<ui::KeyboardCode>(ui::VKEY_F1 + n - 1);
|
||||||
|
} else {
|
||||||
|
LOG(WARNING) << str << "is not available on keyboard";
|
||||||
|
return ui::VKEY_UNKNOWN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (str.size() > 2)
|
||||||
|
LOG(WARNING) << "Invalid accelerator token: " << str;
|
||||||
|
return ui::VKEY_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
ui::KeyboardCode KeyboardCodeFromCharCode(base::char16 c, bool* shifted) {
|
ui::KeyboardCode KeyboardCodeFromCharCode(base::char16 c, bool* shifted) {
|
||||||
c = base::ToLowerASCII(c);
|
c = base::ToLowerASCII(c);
|
||||||
*shifted = false;
|
*shifted = false;
|
||||||
|
@ -198,94 +284,6 @@ ui::KeyboardCode KeyboardCodeFromCharCode(base::char16 c, bool* shifted) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return key code represented by |str|.
|
|
||||||
ui::KeyboardCode KeyboardCodeFromKeyIdentifier(const std::string& s,
|
|
||||||
bool* shifted) {
|
|
||||||
std::string str = base::ToLowerASCII(s);
|
|
||||||
if (str == "ctrl" || str == "control") {
|
|
||||||
return ui::VKEY_CONTROL;
|
|
||||||
} else if (str == "super" || str == "cmd" || str == "command" ||
|
|
||||||
str == "meta") {
|
|
||||||
return ui::VKEY_COMMAND;
|
|
||||||
} else if (str == "commandorcontrol" || str == "cmdorctrl") {
|
|
||||||
#if defined(OS_MACOSX)
|
|
||||||
return ui::VKEY_COMMAND;
|
|
||||||
#else
|
|
||||||
return ui::VKEY_CONTROL;
|
|
||||||
#endif
|
|
||||||
} else if (str == "alt" || str == "option") {
|
|
||||||
return ui::VKEY_MENU;
|
|
||||||
} else if (str == "shift") {
|
|
||||||
return ui::VKEY_SHIFT;
|
|
||||||
} else if (str == "altgr") {
|
|
||||||
return ui::VKEY_ALTGR;
|
|
||||||
} else if (str == "plus") {
|
|
||||||
*shifted = true;
|
|
||||||
return ui::VKEY_OEM_PLUS;
|
|
||||||
} else if (str == "tab") {
|
|
||||||
return ui::VKEY_TAB;
|
|
||||||
} else if (str == "space") {
|
|
||||||
return ui::VKEY_SPACE;
|
|
||||||
} else if (str == "backspace") {
|
|
||||||
return ui::VKEY_BACK;
|
|
||||||
} else if (str == "delete") {
|
|
||||||
return ui::VKEY_DELETE;
|
|
||||||
} else if (str == "insert") {
|
|
||||||
return ui::VKEY_INSERT;
|
|
||||||
} else if (str == "enter" || str == "return") {
|
|
||||||
return ui::VKEY_RETURN;
|
|
||||||
} else if (str == "up") {
|
|
||||||
return ui::VKEY_UP;
|
|
||||||
} else if (str == "down") {
|
|
||||||
return ui::VKEY_DOWN;
|
|
||||||
} else if (str == "left") {
|
|
||||||
return ui::VKEY_LEFT;
|
|
||||||
} else if (str == "right") {
|
|
||||||
return ui::VKEY_RIGHT;
|
|
||||||
} else if (str == "home") {
|
|
||||||
return ui::VKEY_HOME;
|
|
||||||
} else if (str == "end") {
|
|
||||||
return ui::VKEY_END;
|
|
||||||
} else if (str == "pageup") {
|
|
||||||
return ui::VKEY_PRIOR;
|
|
||||||
} else if (str == "pagedown") {
|
|
||||||
return ui::VKEY_NEXT;
|
|
||||||
} else if (str == "esc" || str == "escape") {
|
|
||||||
return ui::VKEY_ESCAPE;
|
|
||||||
} else if (str == "volumemute") {
|
|
||||||
return ui::VKEY_VOLUME_MUTE;
|
|
||||||
} else if (str == "volumeup") {
|
|
||||||
return ui::VKEY_VOLUME_UP;
|
|
||||||
} else if (str == "volumedown") {
|
|
||||||
return ui::VKEY_VOLUME_DOWN;
|
|
||||||
} else if (str == "medianexttrack") {
|
|
||||||
return ui::VKEY_MEDIA_NEXT_TRACK;
|
|
||||||
} else if (str == "mediaprevioustrack") {
|
|
||||||
return ui::VKEY_MEDIA_PREV_TRACK;
|
|
||||||
} else if (str == "mediastop") {
|
|
||||||
return ui::VKEY_MEDIA_STOP;
|
|
||||||
} else if (str == "mediaplaypause") {
|
|
||||||
return ui::VKEY_MEDIA_PLAY_PAUSE;
|
|
||||||
} else if (str == "printscreen") {
|
|
||||||
return ui::VKEY_SNAPSHOT;
|
|
||||||
} else if (str.size() > 1 && str[0] == 'f') {
|
|
||||||
// F1 - F24.
|
|
||||||
int n;
|
|
||||||
if (base::StringToInt(str.c_str() + 1, &n) && n > 0 && n < 25) {
|
|
||||||
return static_cast<ui::KeyboardCode>(ui::VKEY_F1 + n - 1);
|
|
||||||
} else {
|
|
||||||
LOG(WARNING) << str << "is not available on keyboard";
|
|
||||||
return ui::VKEY_UNKNOWN;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (str.size() > 2)
|
|
||||||
LOG(WARNING) << "Invalid accelerator token: " << str;
|
|
||||||
return ui::VKEY_UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted) {
|
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted) {
|
||||||
if (str.size() == 1)
|
if (str.size() == 1)
|
||||||
return KeyboardCodeFromCharCode(str[0], shifted);
|
return KeyboardCodeFromCharCode(str[0], shifted);
|
||||||
|
|
|
@ -7,10 +7,15 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/strings/string16.h"
|
||||||
#include "ui/events/keycodes/keyboard_codes.h"
|
#include "ui/events/keycodes/keyboard_codes.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
|
// Return key code of the char, and also determine whether the SHIFT key is
|
||||||
|
// pressed.
|
||||||
|
ui::KeyboardCode KeyboardCodeFromCharCode(base::char16 c, bool* shifted);
|
||||||
|
|
||||||
// Return key code of the |str|, and also determine whether the SHIFT key is
|
// Return key code of the |str|, and also determine whether the SHIFT key is
|
||||||
// pressed.
|
// pressed.
|
||||||
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted);
|
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted);
|
||||||
|
|
|
@ -19,6 +19,12 @@ The `menu` class has the following static methods:
|
||||||
Sets `menu` as the application menu on macOS. On Windows and Linux, the
|
Sets `menu` as the application menu on macOS. On Windows and Linux, the
|
||||||
`menu` will be set as each window's top menu.
|
`menu` will be set as each window's top menu.
|
||||||
|
|
||||||
|
Also on Windows and Linux, you can use a `&` in the top-level item name to
|
||||||
|
indicate which letter should get a generated accelerator. For example, using
|
||||||
|
`&File` for the file menu would result in a generated `Alt-F` accelerator that
|
||||||
|
opens the associated menu. The indicated character in the button label gets an
|
||||||
|
underline. The `&` character is not displayed on the button label.
|
||||||
|
|
||||||
Passing `null` will remove the menu bar on Windows and Linux but has no
|
Passing `null` will remove the menu bar on Windows and Linux but has no
|
||||||
effect on macOS.
|
effect on macOS.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue