1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/views/touchui/touch_editing_menu.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "ui/base/l10n/l10n_util.h" 9 #include "ui/base/resource/resource_bundle.h" 10 #include "ui/gfx/canvas.h" 11 #include "ui/gfx/font_list.h" 12 #include "ui/gfx/insets.h" 13 #include "ui/gfx/text_utils.h" 14 #include "ui/strings/grit/ui_strings.h" 15 #include "ui/views/bubble/bubble_border.h" 16 #include "ui/views/bubble/bubble_frame_view.h" 17 #include "ui/views/controls/button/custom_button.h" 18 #include "ui/views/controls/button/label_button.h" 19 #include "ui/views/controls/button/label_button_border.h" 20 #include "ui/views/layout/box_layout.h" 21 #include "ui/views/widget/widget.h" 22 23 namespace { 24 25 const int kMenuCommands[] = {IDS_APP_CUT, 26 IDS_APP_COPY, 27 IDS_APP_PASTE}; 28 const int kSpacingBetweenButtons = 2; 29 const int kButtonSeparatorColor = SkColorSetARGB(13, 0, 0, 0); 30 const int kMenuButtonHeight = 38; 31 const int kMenuButtonWidth = 63; 32 const int kMenuMargin = 1; 33 34 const char* kEllipsesButtonText = "..."; 35 const int kEllipsesButtonTag = -1; 36 } // namespace 37 38 namespace views { 39 40 TouchEditingMenuView::TouchEditingMenuView( 41 TouchEditingMenuController* controller, 42 const gfx::Rect& anchor_rect, 43 const gfx::Size& handle_image_size, 44 gfx::NativeView context) 45 : BubbleDelegateView(NULL, views::BubbleBorder::BOTTOM_CENTER), 46 controller_(controller) { 47 set_shadow(views::BubbleBorder::SMALL_SHADOW); 48 set_parent_window(context); 49 set_margins(gfx::Insets(kMenuMargin, kMenuMargin, kMenuMargin, kMenuMargin)); 50 set_can_activate(false); 51 set_adjust_if_offscreen(true); 52 53 SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, 0, 0, 54 kSpacingBetweenButtons)); 55 CreateButtons(); 56 57 // After buttons are created, check if there is enough room between handles to 58 // show the menu and adjust anchor rect properly if needed, just in case the 59 // menu is needed to be shown under the selection. 60 gfx::Rect adjusted_anchor_rect(anchor_rect); 61 int menu_width = GetPreferredSize().width(); 62 if (menu_width > anchor_rect.width() - handle_image_size.width()) 63 adjusted_anchor_rect.Inset(0, 0, 0, -handle_image_size.height()); 64 SetAnchorRect(adjusted_anchor_rect); 65 66 views::BubbleDelegateView::CreateBubble(this); 67 GetWidget()->Show(); 68 } 69 70 TouchEditingMenuView::~TouchEditingMenuView() { 71 } 72 73 // static 74 TouchEditingMenuView* TouchEditingMenuView::Create( 75 TouchEditingMenuController* controller, 76 const gfx::Rect& anchor_rect, 77 const gfx::Size& handle_image_size, 78 gfx::NativeView context) { 79 if (controller) { 80 for (size_t i = 0; i < arraysize(kMenuCommands); i++) { 81 if (controller->IsCommandIdEnabled(kMenuCommands[i])) { 82 return new TouchEditingMenuView(controller, anchor_rect, 83 handle_image_size, context); 84 } 85 } 86 } 87 return NULL; 88 } 89 90 void TouchEditingMenuView::Close() { 91 if (GetWidget()) { 92 controller_ = NULL; 93 GetWidget()->Close(); 94 } 95 } 96 97 void TouchEditingMenuView::WindowClosing() { 98 views::BubbleDelegateView::WindowClosing(); 99 if (controller_) 100 controller_->OnMenuClosed(this); 101 } 102 103 void TouchEditingMenuView::ButtonPressed(Button* sender, 104 const ui::Event& event) { 105 if (controller_) { 106 if (sender->tag() != kEllipsesButtonTag) 107 controller_->ExecuteCommand(sender->tag(), event.flags()); 108 else 109 controller_->OpenContextMenu(); 110 } 111 } 112 113 void TouchEditingMenuView::OnPaint(gfx::Canvas* canvas) { 114 BubbleDelegateView::OnPaint(canvas); 115 116 // Draw separator bars. 117 for (int i = 0; i < child_count() - 1; ++i) { 118 View* child = child_at(i); 119 int x = child->bounds().right() + kSpacingBetweenButtons / 2; 120 canvas->FillRect(gfx::Rect(x, 0, 1, child->height()), 121 kButtonSeparatorColor); 122 } 123 } 124 125 void TouchEditingMenuView::CreateButtons() { 126 RemoveAllChildViews(true); 127 for (size_t i = 0; i < arraysize(kMenuCommands); i++) { 128 int command_id = kMenuCommands[i]; 129 if (controller_ && controller_->IsCommandIdEnabled(command_id)) { 130 Button* button = CreateButton(l10n_util::GetStringUTF16(command_id), 131 command_id); 132 AddChildView(button); 133 } 134 } 135 136 // Finally, add ellipses button. 137 AddChildView(CreateButton( 138 base::UTF8ToUTF16(kEllipsesButtonText), kEllipsesButtonTag)); 139 Layout(); 140 } 141 142 Button* TouchEditingMenuView::CreateButton(const base::string16& title, 143 int tag) { 144 base::string16 label = gfx::RemoveAcceleratorChar(title, '&', NULL, NULL); 145 LabelButton* button = new LabelButton(this, label); 146 button->SetFocusable(true); 147 button->set_request_focus_on_press(false); 148 const gfx::FontList& font_list = 149 ui::ResourceBundle::GetSharedInstance().GetFontList( 150 ui::ResourceBundle::SmallFont); 151 scoped_ptr<LabelButtonBorder> button_border( 152 new LabelButtonBorder(button->style())); 153 int v_border = (kMenuButtonHeight - font_list.GetHeight()) / 2; 154 int h_border = (kMenuButtonWidth - gfx::GetStringWidth(label, font_list)) / 2; 155 button_border->set_insets( 156 gfx::Insets(v_border, h_border, v_border, h_border)); 157 button->SetBorder(button_border.PassAs<Border>()); 158 button->SetFontList(font_list); 159 button->set_tag(tag); 160 return button; 161 } 162 163 } // namespace views 164