1 /* 2 * Copyright 2007, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "CachedPrefix.h" 27 #include "android_graphics.h" 28 #include "CachedRoot.h" 29 #include "IntRect.h" 30 #include "LayerAndroid.h" 31 #include "SkCanvas.h" 32 #include "SkCornerPathEffect.h" 33 #include "SkPath.h" 34 #include "SkRegion.h" 35 #include "WebViewCore.h" 36 37 namespace android { 38 39 /////////////////////////////////////////////////////////////////////////////// 40 41 const static SkColor cursorOuterColors[] = { 42 SkColorSetARGB(0xff, 0xB3, 0x3F, 0x08), // normal ring select 43 SkColorSetARGB(0xff, 0x46, 0xb0, 0x00), // fake ring select, for phone, email, text 44 SkColorSetARGB(0xff, 0xAD, 0x5C, 0x0A), // normal ring pressed 45 SkColorSetARGB(0xff, 0x36, 0xc0, 0x00) // fake ring pressed 46 }; 47 48 const static SkColor cursorInnerColors[] = { 49 SkColorSetARGB(0xff, 0xFE, 0x92, 0x30), // normal ring select 50 SkColorSetARGB(0xff, 0x8c, 0xd9, 0x00), // fake ring select, for phone, email, text 51 SkColorSetARGB(0xff, 0xFE, 0xBD, 0x3A), // normal ring pressed 52 SkColorSetARGB(0xff, 0x7c, 0xe9, 0x00) // fake ring pressed 53 }; 54 55 const static SkColor cursorPressedColors[] = { 56 SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B), // normal ring pressed 57 SkColorSetARGB(0x80, 0x7c, 0xe9, 0x00) // fake ring pressed 58 }; 59 60 #define CURSOR_RING_ROUNDEDNESS SkIntToScalar(5) // used to draw corners 61 #define CURSOR_RING_INNER_DIAMETER SkFixedToScalar(SkIntToFixed(3)>>1) // 3/2 == 1.5 62 #define CURSOR_RING_OUTER_OUTSET 2 // used to inflate rects added to region 63 64 void CursorRing::draw(SkCanvas* canvas, LayerAndroid* layer) 65 { 66 #if USE(ACCELERATED_COMPOSITING) 67 int layerId = m_node->isInLayer() ? m_frame->layer(m_node)->uniqueId() : -1; 68 if (layer->uniqueId() != layerId) 69 return; 70 #endif 71 if (canvas->quickReject(m_bounds, SkCanvas::kAA_EdgeType)) { 72 DBG_NAV_LOGD("canvas->quickReject cursorNode=%d (nodePointer=%p)" 73 " bounds=(%d,%d,w=%d,h=%d)", m_node->index(), m_node->nodePointer(), 74 m_bounds.x(), m_bounds.y(), m_bounds.width(), m_bounds.height()); 75 m_followedLink = false; 76 return; 77 } 78 unsigned rectCount = m_rings.size(); 79 SkRegion rgn; 80 SkPath path; 81 for (unsigned i = 0; i < rectCount; i++) 82 { 83 SkRect r(m_rings[i]); 84 SkIRect ir; 85 86 r.round(&ir); 87 ir.inset(-CURSOR_RING_OUTER_OUTSET, -CURSOR_RING_OUTER_OUTSET); 88 rgn.op(ir, SkRegion::kUnion_Op); 89 } 90 rgn.getBoundaryPath(&path); 91 92 SkPaint paint; 93 paint.setAntiAlias(true); 94 paint.setPathEffect(new SkCornerPathEffect(CURSOR_RING_ROUNDEDNESS))->unref(); 95 if (m_flavor >= NORMAL_ANIMATING) { // pressed 96 paint.setColor(cursorPressedColors[m_flavor - NORMAL_ANIMATING]); 97 canvas->drawPath(path, paint); 98 } 99 paint.setStyle(SkPaint::kStroke_Style); 100 paint.setStrokeWidth(CURSOR_RING_OUTER_DIAMETER); 101 paint.setColor(cursorOuterColors[m_flavor]); 102 canvas->drawPath(path, paint); 103 paint.setStrokeWidth(CURSOR_RING_INNER_DIAMETER); 104 paint.setColor(cursorInnerColors[m_flavor]); 105 canvas->drawPath(path, paint); 106 } 107 108 bool CursorRing::setup() 109 { 110 m_node->localCursorRings(m_frame, &m_rings); 111 if (!m_rings.size()) { 112 DBG_NAV_LOG("!rings.size()"); 113 m_viewImpl->m_hasCursorBounds = false; 114 return false; 115 } 116 m_isButton = false; 117 m_viewImpl->gButtonMutex.lock(); 118 // If this is a button drawn by us (rather than webkit) do not draw the 119 // cursor ring, since its cursor will be shown by a change in what we draw. 120 // Should be in sync with recordButtons, since that will be called 121 // before this. 122 if (m_viewImpl->m_buttons.size() > 0) { 123 WebCore::Node* cursorPointer = (WebCore::Node*) m_node->nodePointer(); 124 Container* end = m_viewImpl->m_buttons.end(); 125 for (Container* ptr = m_viewImpl->m_buttons.begin(); ptr != end; ptr++) { 126 if (ptr->matches(cursorPointer)) { 127 m_isButton = true; 128 break; 129 } 130 } 131 } 132 m_viewImpl->gButtonMutex.unlock(); 133 m_bounds = m_node->localBounds(m_frame); 134 m_viewImpl->updateCursorBounds(m_root, m_frame, m_node); 135 136 bool useHitBounds = m_node->useHitBounds(); 137 if (useHitBounds) 138 m_bounds = m_node->localHitBounds(m_frame); 139 if (useHitBounds || m_node->useBounds()) { 140 m_rings.clear(); 141 m_rings.append(m_bounds); 142 } 143 m_bounds.inflate(SkScalarCeil(CURSOR_RING_OUTER_DIAMETER)); 144 if (!m_node->hasCursorRing() || (m_node->isPlugin() && m_node->isFocus())) 145 return false; 146 m_flavor = NORMAL_FLAVOR; 147 if (!m_isButton) { 148 m_flavor = m_node->isSyntheticLink() ? FAKE_FLAVOR : NORMAL_FLAVOR; 149 if (m_followedLink) { 150 m_flavor = static_cast<Flavor>(m_flavor + NORMAL_ANIMATING); 151 } 152 #if DEBUG_NAV_UI 153 const WebCore::IntRect& ring = m_rings[0]; 154 DBG_NAV_LOGD("cursorNode=%d (nodePointer=%p) flavor=%s rings=%d" 155 " (%d, %d, %d, %d) isPlugin=%s", 156 m_node->index(), m_node->nodePointer(), 157 m_flavor == FAKE_FLAVOR ? "FAKE_FLAVOR" : 158 m_flavor == NORMAL_ANIMATING ? "NORMAL_ANIMATING" : 159 m_flavor == FAKE_ANIMATING ? "FAKE_ANIMATING" : "NORMAL_FLAVOR", 160 m_rings.size(), ring.x(), ring.y(), ring.width(), ring.height(), 161 m_node->isPlugin() ? "true" : "false"); 162 #endif 163 } 164 return true; 165 } 166 167 } 168