diff options
author | mohsen <mohsen@chromium.org> | 2015-05-28 21:12:40 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-29 04:13:09 +0000 |
commit | 1d0063dd4fcc1d4ec66af570c8e41d4416649159 (patch) | |
tree | 86d0dcff66f0a7ffefda3b23456992936b9330f8 /ui/views/touchui/touch_selection_menu_runner_views.cc | |
parent | 7a6acf6c7d45e24f07781c69249d90967013bbe4 (diff) | |
download | chromium_src-1d0063dd4fcc1d4ec66af570c8e41d4416649159.zip chromium_src-1d0063dd4fcc1d4ec66af570c8e41d4416649159.tar.gz chromium_src-1d0063dd4fcc1d4ec66af570c8e41d4416649159.tar.bz2 |
Refactor touch selection quick menu
To use the touch selection quick menu in the upcoming unified touch
selection, a new interface is added to ui/touch_selection with an
implementation for Views. The implementation mostly copied code from the
old implementation.
COLLABORATOR=mfomitchev
BUG=399721
Review URL: https://codereview.chromium.org/1019353003
Cr-Commit-Position: refs/heads/master@{#331924}
Diffstat (limited to 'ui/views/touchui/touch_selection_menu_runner_views.cc')
-rw-r--r-- | ui/views/touchui/touch_selection_menu_runner_views.cc | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/ui/views/touchui/touch_selection_menu_runner_views.cc b/ui/views/touchui/touch_selection_menu_runner_views.cc new file mode 100644 index 0000000..6f44e52 --- /dev/null +++ b/ui/views/touchui/touch_selection_menu_runner_views.cc @@ -0,0 +1,249 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/touchui/touch_selection_menu_runner_views.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/aura/window.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/text_utils.h" +#include "ui/strings/grit/ui_strings.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/layout/box_layout.h" + +namespace views { +namespace { + +const int kMenuCommands[] = {IDS_APP_CUT, IDS_APP_COPY, IDS_APP_PASTE}; +const int kSpacingBetweenButtons = 2; +const int kButtonSeparatorColor = SkColorSetARGB(13, 0, 0, 0); +const int kMenuButtonMinHeight = 38; +const int kMenuButtonMinWidth = 63; +const int kMenuMargin = 1; + +const char kEllipsesButtonText[] = "..."; +const int kEllipsesButtonTag = -1; + +} // namespace + +// A bubble that contains actions available for the selected text. An object of +// this type, as a BubbleDelegateView, manages its own lifetime. +class TouchSelectionMenuRunnerViews::Menu : public BubbleDelegateView, + public ButtonListener { + public: + // Closes the menu. This will eventually self-destroy the object. + void Close(); + + // Returns a new instance of Menu if there is any command available; + // otherwise, returns |nullptr|. + static Menu* Create(TouchSelectionMenuRunnerViews* owner, + ui::TouchSelectionMenuClient* client, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, + aura::Window* context); + + private: + Menu(TouchSelectionMenuRunnerViews* owner, + ui::TouchSelectionMenuClient* client, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, + aura::Window* context); + + ~Menu() override; + + // Queries the |client_| for what commands to show in the menu and sizes the + // menu appropriately. + void CreateButtons(); + + // Helper method to create a single button. + Button* CreateButton(const base::string16& title, int tag); + + // BubbleDelegateView: + void OnPaint(gfx::Canvas* canvas) override; + void WindowClosing() override; + + // ButtonListener: + void ButtonPressed(Button* sender, const ui::Event& event) override; + + TouchSelectionMenuRunnerViews* owner_; + ui::TouchSelectionMenuClient* const client_; + + DISALLOW_COPY_AND_ASSIGN(Menu); +}; + +TouchSelectionMenuRunnerViews::Menu* +TouchSelectionMenuRunnerViews::Menu::Create( + TouchSelectionMenuRunnerViews* owner, + ui::TouchSelectionMenuClient* client, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, + aura::Window* context) { + DCHECK(client); + + for (size_t i = 0; i < arraysize(kMenuCommands); i++) { + if (client->IsCommandIdEnabled(kMenuCommands[i])) + return new Menu(owner, client, anchor_rect, handle_image_size, context); + } + + // No command is available, so return |nullptr|. + return nullptr; +} + +TouchSelectionMenuRunnerViews::Menu::Menu(TouchSelectionMenuRunnerViews* owner, + ui::TouchSelectionMenuClient* client, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, + aura::Window* context) + : BubbleDelegateView(nullptr, BubbleBorder::BOTTOM_CENTER), + owner_(owner), + client_(client) { + DCHECK(owner_); + DCHECK(client_); + + set_shadow(BubbleBorder::SMALL_SHADOW); + set_parent_window(context); + set_margins(gfx::Insets(kMenuMargin, kMenuMargin, kMenuMargin, kMenuMargin)); + set_can_activate(false); + set_adjust_if_offscreen(true); + EnableCanvasFlippingForRTLUI(true); + + SetLayoutManager( + new BoxLayout(BoxLayout::kHorizontal, 0, 0, kSpacingBetweenButtons)); + CreateButtons(); + + // After buttons are created, check if there is enough room between handles to + // show the menu and adjust anchor rect properly if needed, just in case the + // menu is needed to be shown under the selection. + gfx::Rect adjusted_anchor_rect(anchor_rect); + int menu_width = GetPreferredSize().width(); + // TODO(mfomitchev): This assumes that the handles are center-aligned to the + // |achor_rect| edges, which is not true. We should fix this, perhaps by + // passing down the cumulative width occupied by the handles within + // |anchor_rect| plus the handle image height instead of |handle_image_size|. + // Perhaps we should also allow for some minimum padding. + if (menu_width > anchor_rect.width() - handle_image_size.width()) + adjusted_anchor_rect.Inset(0, 0, 0, -handle_image_size.height()); + SetAnchorRect(adjusted_anchor_rect); + + BubbleDelegateView::CreateBubble(this); + GetWidget()->Show(); +} + +TouchSelectionMenuRunnerViews::Menu::~Menu() { +} + +void TouchSelectionMenuRunnerViews::Menu::CreateButtons() { + for (size_t i = 0; i < arraysize(kMenuCommands); i++) { + int command_id = kMenuCommands[i]; + if (!client_->IsCommandIdEnabled(command_id)) + continue; + + Button* button = + CreateButton(l10n_util::GetStringUTF16(command_id), command_id); + AddChildView(button); + } + + // Finally, add ellipses button. + AddChildView( + CreateButton(base::UTF8ToUTF16(kEllipsesButtonText), kEllipsesButtonTag)); + Layout(); +} + +Button* TouchSelectionMenuRunnerViews::Menu::CreateButton( + const base::string16& title, + int tag) { + base::string16 label = + gfx::RemoveAcceleratorChar(title, '&', nullptr, nullptr); + LabelButton* button = new LabelButton(this, label); + button->SetMinSize(gfx::Size(kMenuButtonMinWidth, kMenuButtonMinHeight)); + button->SetFocusable(true); + button->set_request_focus_on_press(false); + const gfx::FontList& font_list = + ui::ResourceBundle::GetSharedInstance().GetFontList( + ui::ResourceBundle::SmallFont); + button->SetFontList(font_list); + button->SetHorizontalAlignment(gfx::ALIGN_CENTER); + button->set_tag(tag); + return button; +} + +void TouchSelectionMenuRunnerViews::Menu::Close() { + // Closing the widget will self-destroy this object. + Widget* widget = GetWidget(); + if (widget && !widget->IsClosed()) + widget->Close(); + owner_ = nullptr; +} + +void TouchSelectionMenuRunnerViews::Menu::OnPaint(gfx::Canvas* canvas) { + BubbleDelegateView::OnPaint(canvas); + + // Draw separator bars. + for (int i = 0; i < child_count() - 1; ++i) { + View* child = child_at(i); + int x = child->bounds().right() + kSpacingBetweenButtons / 2; + canvas->FillRect(gfx::Rect(x, 0, 1, child->height()), + kButtonSeparatorColor); + } +} + +void TouchSelectionMenuRunnerViews::Menu::WindowClosing() { + DCHECK_IMPLIES(owner_, owner_->menu_ == this); + BubbleDelegateView::WindowClosing(); + if (owner_) + owner_->menu_ = nullptr; +} + +void TouchSelectionMenuRunnerViews::Menu::ButtonPressed( + Button* sender, + const ui::Event& event) { + Close(); + if (sender->tag() != kEllipsesButtonTag) + client_->ExecuteCommand(sender->tag(), event.flags()); + else + client_->RunContextMenu(); +} + +TouchSelectionMenuRunnerViews::TouchSelectionMenuRunnerViews() + : menu_(nullptr) { +} + +TouchSelectionMenuRunnerViews::~TouchSelectionMenuRunnerViews() { + CloseMenu(); +} + +gfx::Rect TouchSelectionMenuRunnerViews::GetAnchorRectForTest() const { + return menu_ ? menu_->GetAnchorRect() : gfx::Rect(); +} + +void TouchSelectionMenuRunnerViews::OpenMenu( + ui::TouchSelectionMenuClient* client, + const gfx::Rect& anchor_rect, + const gfx::Size& handle_image_size, + aura::Window* context) { + CloseMenu(); + + menu_ = Menu::Create(this, client, anchor_rect, handle_image_size, context); +} + +void TouchSelectionMenuRunnerViews::CloseMenu() { + if (!menu_) + return; + + // Closing the menu will eventually delete the object. + menu_->Close(); + menu_ = nullptr; +} + +bool TouchSelectionMenuRunnerViews::IsRunning() const { + return menu_ != nullptr; +} + +} // namespace views |