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/accessibility/ax_view_state.h" 12 #include "ui/base/cursor/cursor.h" 13 #include "ui/events/event.h" 14 #include "ui/events/keycodes/keyboard_codes.h" 15 #include "ui/gfx/canvas.h" 16 #include "ui/gfx/color_utils.h" 17 #include "ui/gfx/font_list.h" 18 #include "ui/views/controls/link_listener.h" 19 #include "ui/views/native_cursor.h" 20 21 namespace views { 22 23 const char Link::kViewClassName[] = "Link"; 24 25 Link::Link() : Label(base::string16()) { 26 Init(); 27 } 28 29 Link::Link(const base::string16& title) : Label(title) { 30 Init(); 31 } 32 33 Link::~Link() { 34 } 35 36 SkColor Link::GetDefaultEnabledColor() { 37 #if defined(OS_WIN) 38 return color_utils::GetSysSkColor(COLOR_HOTLIGHT); 39 #else 40 return SkColorSetRGB(0, 51, 153); 41 #endif 42 } 43 44 const char* Link::GetClassName() const { 45 return kViewClassName; 46 } 47 48 gfx::NativeCursor Link::GetCursor(const ui::MouseEvent& event) { 49 if (!enabled()) 50 return gfx::kNullCursor; 51 return GetNativeHandCursor(); 52 } 53 54 bool Link::CanProcessEventsWithinSubtree() const { 55 // Links need to be able to accept events (e.g., clicking) even though 56 // in general Labels do not. 57 return View::CanProcessEventsWithinSubtree(); 58 } 59 60 bool Link::OnMousePressed(const ui::MouseEvent& event) { 61 if (!enabled() || 62 (!event.IsLeftMouseButton() && !event.IsMiddleMouseButton())) 63 return false; 64 SetPressed(true); 65 return true; 66 } 67 68 bool Link::OnMouseDragged(const ui::MouseEvent& event) { 69 SetPressed(enabled() && 70 (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) && 71 HitTestPoint(event.location())); 72 return true; 73 } 74 75 void Link::OnMouseReleased(const ui::MouseEvent& event) { 76 // Change the highlight first just in case this instance is deleted 77 // while calling the controller 78 OnMouseCaptureLost(); 79 if (enabled() && 80 (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) && 81 HitTestPoint(event.location())) { 82 // Focus the link on click. 83 RequestFocus(); 84 85 if (listener_) 86 listener_->LinkClicked(this, event.flags()); 87 } 88 } 89 90 void Link::OnMouseCaptureLost() { 91 SetPressed(false); 92 } 93 94 bool Link::OnKeyPressed(const ui::KeyEvent& event) { 95 bool activate = ((event.key_code() == ui::VKEY_SPACE) || 96 (event.key_code() == ui::VKEY_RETURN)); 97 if (!activate) 98 return false; 99 100 SetPressed(false); 101 102 // Focus the link on key pressed. 103 RequestFocus(); 104 105 if (listener_) 106 listener_->LinkClicked(this, event.flags()); 107 108 return true; 109 } 110 111 void Link::OnGestureEvent(ui::GestureEvent* event) { 112 if (!enabled()) 113 return; 114 115 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { 116 SetPressed(true); 117 } else if (event->type() == ui::ET_GESTURE_TAP) { 118 RequestFocus(); 119 if (listener_) 120 listener_->LinkClicked(this, event->flags()); 121 } else { 122 SetPressed(false); 123 return; 124 } 125 event->SetHandled(); 126 } 127 128 bool Link::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { 129 // Make sure we don't process space or enter as accelerators. 130 return (event.key_code() == ui::VKEY_SPACE) || 131 (event.key_code() == ui::VKEY_RETURN); 132 } 133 134 void Link::GetAccessibleState(ui::AXViewState* state) { 135 Label::GetAccessibleState(state); 136 state->role = ui::AX_ROLE_LINK; 137 } 138 139 void Link::OnEnabledChanged() { 140 RecalculateFont(); 141 View::OnEnabledChanged(); 142 } 143 144 void Link::OnFocus() { 145 Label::OnFocus(); 146 // We render differently focused. 147 SchedulePaint(); 148 } 149 150 void Link::OnBlur() { 151 Label::OnBlur(); 152 // We render differently focused. 153 SchedulePaint(); 154 } 155 156 void Link::SetFontList(const gfx::FontList& font_list) { 157 Label::SetFontList(font_list); 158 RecalculateFont(); 159 } 160 161 void Link::SetText(const base::string16& text) { 162 Label::SetText(text); 163 // Disable focusability for empty links. Otherwise Label::GetInsets() will 164 // give them an unconditional 1-px. inset on every side to allow for a focus 165 // border, when in this case we probably wanted zero width. 166 SetFocusable(!text.empty()); 167 } 168 169 void Link::SetEnabledColor(SkColor color) { 170 requested_enabled_color_ = color; 171 if (!pressed_) 172 Label::SetEnabledColor(requested_enabled_color_); 173 } 174 175 void Link::SetPressedColor(SkColor color) { 176 requested_pressed_color_ = color; 177 if (pressed_) 178 Label::SetEnabledColor(requested_pressed_color_); 179 } 180 181 void Link::SetUnderline(bool underline) { 182 if (underline_ == underline) 183 return; 184 underline_ = underline; 185 RecalculateFont(); 186 } 187 188 void Link::Init() { 189 listener_ = NULL; 190 pressed_ = false; 191 underline_ = true; 192 SetEnabledColor(GetDefaultEnabledColor()); 193 #if defined(OS_WIN) 194 SetDisabledColor(color_utils::GetSysSkColor(COLOR_WINDOWTEXT)); 195 SetPressedColor(SkColorSetRGB(200, 0, 0)); 196 #else 197 // TODO(beng): source from theme provider. 198 SetDisabledColor(SK_ColorBLACK); 199 SetPressedColor(SK_ColorRED); 200 #endif 201 RecalculateFont(); 202 203 // Label::Init() calls SetText(), but if that's being called from Label(), our 204 // SetText() override will not be reached (because the constructed class is 205 // only a Label at the moment, not yet a Link). So so the set_focusable() 206 // call explicitly here. 207 SetFocusable(!text().empty()); 208 } 209 210 void Link::SetPressed(bool pressed) { 211 if (pressed_ != pressed) { 212 pressed_ = pressed; 213 Label::SetEnabledColor(pressed_ ? 214 requested_pressed_color_ : requested_enabled_color_); 215 RecalculateFont(); 216 SchedulePaint(); 217 } 218 } 219 220 void Link::RecalculateFont() { 221 // Underline the link iff it is enabled and |underline_| is true. 222 const int style = font_list().GetFontStyle(); 223 const int intended_style = (enabled() && underline_) ? 224 (style | gfx::Font::UNDERLINE) : (style & ~gfx::Font::UNDERLINE); 225 if (style != intended_style) 226 Label::SetFontList(font_list().DeriveWithStyle(intended_style)); 227 } 228 229 } // namespace views 230