15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/button/menu_button.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/ui_resources.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/ui_strings.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/accessibility/accessible_view_state.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/dragdrop/drag_drop_types.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
14d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h"
15d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event_constants.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/button/button.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/button/menu_button_listener.h"
21424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "ui/views/mouse_constants.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/root_view.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using base::TimeTicks;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::TimeDelta;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace views {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Default menu offset.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kDefaultMenuOffsetX = -2;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kDefaultMenuOffsetY = -4;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
35a3f7b4e666c476898878fa745f637129375cd889Ben Murdochconst char MenuButton::kViewClassName[] = "MenuButton";
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int MenuButton::kMenuMarkerPaddingLeft = 3;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int MenuButton::kMenuMarkerPaddingRight = -1;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MenuButton - constructors, destructors, initialization
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MenuButton::MenuButton(ButtonListener* listener,
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const string16& text,
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       MenuButtonListener* menu_button_listener,
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       bool show_menu_marker)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : TextButton(listener, text),
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu_visible_(false),
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu_offset_(kDefaultMenuOffsetX, kDefaultMenuOffsetY),
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      listener_(menu_button_listener),
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      show_menu_marker_(show_menu_marker),
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu_marker_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          IDR_MENU_DROPARROW).ToImageSkia()),
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      destroyed_flag_(NULL) {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set_alignment(TextButton::ALIGN_LEFT);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MenuButton::~MenuButton() {
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (destroyed_flag_)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *destroyed_flag_ = true;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MenuButton - Public APIs
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MenuButton::Activate() {
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetState(STATE_PRESSED);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (listener_) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect lb = GetLocalBounds();
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The position of the menu depends on whether or not the locale is
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // right-to-left.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Point menu_position(lb.right(), lb.bottom());
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (base::i18n::IsRTL())
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu_position.set_x(lb.x());
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    View::ConvertPointToScreen(this, &menu_position);
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (base::i18n::IsRTL())
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu_position.Offset(-menu_offset_.x(), menu_offset_.y());
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu_position.Offset(menu_offset_.x(), menu_offset_.y());
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int max_x_coordinate = GetMaximumScreenXCoordinate();
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (max_x_coordinate && max_x_coordinate <= menu_position.x())
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu_position.set_x(max_x_coordinate - 1);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We're about to show the menu from a mouse press. By showing from the
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // mouse press event we block RootView in mouse dispatching. This also
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // appears to cause RootView to get a mouse pressed BEFORE the mouse
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // release is seen, which means RootView sends us another mouse press no
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // matter where the user pressed. To force RootView to recalculate the
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // mouse target during the mouse press we explicitly set the mouse handler
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to NULL.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static_cast<internal::RootView*>(GetWidget()->GetRootView())->
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetMouseHandler(NULL);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    menu_visible_ = true;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool destroyed = false;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    destroyed_flag_ = &destroyed;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    listener_->OnMenuButtonClicked(this, menu_position);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (destroyed) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The menu was deleted while showing. Don't attempt any processing.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    destroyed_flag_ = NULL;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    menu_visible_ = false;
117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    menu_closed_time_ = TimeTicks::Now();
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Now that the menu has closed, we need to manually reset state to
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // "normal" since the menu modal loop will have prevented normal
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // mouse move messages from getting to this View. We set "normal"
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // and not "hot" because the likelihood is that the mouse is now
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // somewhere else (user clicked elsewhere on screen to close the menu
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // or selected an item) and we will inevitably refresh the hot state
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // in the event the mouse _is_ over the view.
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SetState(STATE_NORMAL);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We must return false here so that the RootView does not get stuck
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // sending all mouse pressed events to us instead of the appropriate
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // target.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MenuButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TextButton::PaintButton(canvas, mode);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (show_menu_marker_)
1404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    PaintMenuMarker(canvas);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MenuButton - Events
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Size MenuButton::GetPreferredSize() {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Size prefsize = TextButton::GetPreferredSize();
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (show_menu_marker_) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefsize.Enlarge(menu_marker_->width() + kMenuMarkerPaddingLeft +
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         kMenuMarkerPaddingRight,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     0);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return prefsize;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
159b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char* MenuButton::GetClassName() const {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return kViewClassName;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MenuButton::OnMousePressed(const ui::MouseEvent& event) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequestFocus();
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (state() != STATE_DISABLED) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we're draggable (GetDragOperations returns a non-zero value), then
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // don't pop on press, instead wait for release.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (event.IsOnlyLeftMouseButton() &&
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        HitTestPoint(event.location()) &&
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetDragOperations(event.location()) == ui::DragDropTypes::DRAG_NONE) {
171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      TimeDelta delta = TimeTicks::Now() - menu_closed_time_;
172424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      if (delta.InMilliseconds() > kMinimumMsBetweenButtonClicks)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Activate();
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MenuButton::OnMouseReleased(const ui::MouseEvent& event) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Explicitly test for left mouse button to show the menu. If we tested for
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // !IsTriggerableEvent it could lead to a situation where we end up showing
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the menu and context menu (this would happen if the right button is not
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // triggerable and there's a context menu).
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetDragOperations(event.location()) != ui::DragDropTypes::DRAG_NONE &&
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state() != STATE_DISABLED && !InDrag() && event.IsOnlyLeftMouseButton() &&
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HitTestPoint(event.location())) {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Activate();
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TextButton::OnMouseReleased(event);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The reason we override View::OnMouseExited is because we get this event when
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// we display the menu. If we don't override this method then
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// BaseButton::OnMouseExited will get the event and will set the button's state
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// to STATE_NORMAL instead of keeping the state BM_PUSHED. This, in turn, will
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// cause the button to appear depressed while the menu is displayed.
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MenuButton::OnMouseExited(const ui::MouseEvent& event) {
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if ((state_ != STATE_DISABLED) && (!menu_visible_) && (!InDrag())) {
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SetState(STATE_NORMAL);
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void MenuButton::OnGestureEvent(ui::GestureEvent* event) {
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (state() != STATE_DISABLED && event->type() == ui::ET_GESTURE_TAP) {
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (Activate())
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      event->StopPropagation();
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TextButton::OnGestureEvent(event);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MenuButton::OnKeyPressed(const ui::KeyEvent& event) {
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (event.key_code()) {
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::VKEY_SPACE:
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Alt-space on windows should show the window menu.
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (event.IsAltDown())
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::VKEY_RETURN:
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::VKEY_UP:
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::VKEY_DOWN: {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // WARNING: we may have been deleted by the time Activate returns.
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bool ret = Activate();
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(USE_AURA)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // This is to prevent the keyboard event from being dispatched twice.
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // The Activate function returns false in most cases. In AURA if the
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // keyboard event is not handled, we pass it to the default handler
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // which dispatches the event back to us causing the menu to get
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // displayed again.
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ret = true;
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ret;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MenuButton::OnKeyReleased(const ui::KeyEvent& event) {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Override CustomButton's implementation, which presses the button when
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // you press space and clicks it when you release space.  For a MenuButton
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we always activate the menu on key press.
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MenuButton::GetAccessibleState(ui::AccessibleViewState* state) {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CustomButton::GetAccessibleState(state);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state->role = ui::AccessibilityTypes::ROLE_BUTTONMENU;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state->default_action = l10n_util::GetStringUTF16(IDS_APP_ACCACTION_PRESS);
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state->state = ui::AccessibilityTypes::STATE_HASPOPUP;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void MenuButton::PaintMenuMarker(gfx::Canvas* canvas) {
2554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gfx::Insets insets = GetInsets();
2564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // We can not use the views' mirroring infrastructure for mirroring a
2584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // MenuButton control (see TextButton::OnPaint() for a detailed explanation
2594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // regarding why we can not flip the canvas). Therefore, we need to
2604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // manually mirror the position of the down arrow.
2614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gfx::Rect arrow_bounds(width() - insets.right() -
2624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                         menu_marker_->width() - kMenuMarkerPaddingRight,
2634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                         height() / 2 - menu_marker_->height() / 2,
2644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                         menu_marker_->width(),
2654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                         menu_marker_->height());
2664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  arrow_bounds.set_x(GetMirroredXForRect(arrow_bounds));
2674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  canvas->DrawImageInt(*menu_marker_, arrow_bounds.x(), arrow_bounds.y());
2684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int MenuButton::GetMaximumScreenXCoordinate() {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetWidget()) {
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect monitor_bounds = GetWidget()->GetWorkAreaBoundsInScreen();
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return monitor_bounds.right() - 1;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace views
281