1// Copyright (c) 2012 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/controls/link.h" 6 7#include "build/build_config.h" 8 9#include "base/logging.h" 10#include "base/strings/utf_string_conversions.h" 11#include "ui/base/accessibility/accessible_view_state.h" 12#include "ui/events/event.h" 13#include "ui/events/keycodes/keyboard_codes.h" 14#include "ui/gfx/canvas.h" 15#include "ui/gfx/color_utils.h" 16#include "ui/gfx/font.h" 17#include "ui/views/controls/link_listener.h" 18 19#if defined(USE_AURA) 20#include "ui/base/cursor/cursor.h" 21#endif 22 23namespace views { 24 25const char Link::kViewClassName[] = "Link"; 26 27Link::Link() : Label(string16()) { 28 Init(); 29} 30 31Link::Link(const string16& title) : Label(title) { 32 Init(); 33} 34 35Link::~Link() { 36} 37 38SkColor Link::GetDefaultEnabledColor() { 39#if defined(OS_WIN) 40 return color_utils::GetSysSkColor(COLOR_HOTLIGHT); 41#else 42 return SkColorSetRGB(0, 51, 153); 43#endif 44} 45 46void Link::OnEnabledChanged() { 47 RecalculateFont(); 48 View::OnEnabledChanged(); 49} 50 51const char* Link::GetClassName() const { 52 return kViewClassName; 53} 54 55gfx::NativeCursor Link::GetCursor(const ui::MouseEvent& event) { 56 if (!enabled()) 57 return gfx::kNullCursor; 58#if defined(USE_AURA) 59 return ui::kCursorHand; 60#elif defined(OS_WIN) 61 static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_HAND); 62 return g_hand_cursor; 63#endif 64} 65 66void Link::OnPaint(gfx::Canvas* canvas) { 67 Label::OnPaint(canvas); 68 69 if (HasFocus()) 70 canvas->DrawFocusRect(GetLocalBounds()); 71} 72 73void Link::OnFocus() { 74 Label::OnFocus(); 75 // We render differently focused. 76 SchedulePaint(); 77} 78 79void Link::OnBlur() { 80 Label::OnBlur(); 81 // We render differently focused. 82 SchedulePaint(); 83} 84 85bool Link::HitTestRect(const gfx::Rect& rect) const { 86 // We need to allow clicks on the link. So we override the implementation in 87 // Label and use the default implementation of View. 88 return View::HitTestRect(rect); 89} 90 91bool Link::OnMousePressed(const ui::MouseEvent& event) { 92 if (!enabled() || 93 (!event.IsLeftMouseButton() && !event.IsMiddleMouseButton())) 94 return false; 95 SetPressed(true); 96 return true; 97} 98 99bool Link::OnMouseDragged(const ui::MouseEvent& event) { 100 SetPressed(enabled() && 101 (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) && 102 HitTestPoint(event.location())); 103 return true; 104} 105 106void Link::OnMouseReleased(const ui::MouseEvent& event) { 107 // Change the highlight first just in case this instance is deleted 108 // while calling the controller 109 OnMouseCaptureLost(); 110 if (enabled() && 111 (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) && 112 HitTestPoint(event.location())) { 113 // Focus the link on click. 114 RequestFocus(); 115 116 if (listener_) 117 listener_->LinkClicked(this, event.flags()); 118 } 119} 120 121void Link::OnMouseCaptureLost() { 122 SetPressed(false); 123} 124 125bool Link::OnKeyPressed(const ui::KeyEvent& event) { 126 bool activate = ((event.key_code() == ui::VKEY_SPACE) || 127 (event.key_code() == ui::VKEY_RETURN)); 128 if (!activate) 129 return false; 130 131 SetPressed(false); 132 133 // Focus the link on key pressed. 134 RequestFocus(); 135 136 if (listener_) 137 listener_->LinkClicked(this, event.flags()); 138 139 return true; 140} 141 142bool Link::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { 143 // Make sure we don't process space or enter as accelerators. 144 return (event.key_code() == ui::VKEY_SPACE) || 145 (event.key_code() == ui::VKEY_RETURN); 146} 147 148void Link::GetAccessibleState(ui::AccessibleViewState* state) { 149 Label::GetAccessibleState(state); 150 state->role = ui::AccessibilityTypes::ROLE_LINK; 151} 152 153void Link::OnGestureEvent(ui::GestureEvent* event) { 154 if (!enabled()) 155 return; 156 157 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { 158 SetPressed(true); 159 } else if (event->type() == ui::ET_GESTURE_TAP) { 160 RequestFocus(); 161 if (listener_) 162 listener_->LinkClicked(this, event->flags()); 163 } else { 164 SetPressed(false); 165 return; 166 } 167 event->SetHandled(); 168} 169 170void Link::SetFont(const gfx::Font& font) { 171 Label::SetFont(font); 172 RecalculateFont(); 173} 174 175void Link::SetEnabledColor(SkColor color) { 176 requested_enabled_color_ = color; 177 if (!pressed_) 178 Label::SetEnabledColor(requested_enabled_color_); 179} 180 181void Link::SetPressedColor(SkColor color) { 182 requested_pressed_color_ = color; 183 if (pressed_) 184 Label::SetEnabledColor(requested_pressed_color_); 185} 186 187void Link::SetUnderline(bool underline) { 188 if (underline_ == underline) 189 return; 190 underline_ = underline; 191 RecalculateFont(); 192} 193 194void Link::Init() { 195 listener_ = NULL; 196 pressed_ = false; 197 underline_ = true; 198 SetEnabledColor(GetDefaultEnabledColor()); 199#if defined(OS_WIN) 200 SetDisabledColor(color_utils::GetSysSkColor(COLOR_WINDOWTEXT)); 201 SetPressedColor(SkColorSetRGB(200, 0, 0)); 202#else 203 // TODO(beng): source from theme provider. 204 SetDisabledColor(SK_ColorBLACK); 205 SetPressedColor(SK_ColorRED); 206#endif 207 RecalculateFont(); 208 SetFocusable(true); 209} 210 211void Link::SetPressed(bool pressed) { 212 if (pressed_ != pressed) { 213 pressed_ = pressed; 214 Label::SetEnabledColor(pressed_ ? 215 requested_pressed_color_ : requested_enabled_color_); 216 RecalculateFont(); 217 SchedulePaint(); 218 } 219} 220 221void Link::RecalculateFont() { 222 // Underline the link iff it is enabled and |underline_| is true. 223 const int style = font().GetStyle(); 224 const int intended_style = (enabled() && underline_) ? 225 (style | gfx::Font::UNDERLINE) : (style & ~gfx::Font::UNDERLINE); 226 if (style != intended_style) 227 Label::SetFont(font().DeriveFont(0, intended_style)); 228} 229 230} // namespace views 231