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 "CachedColor.h"
29 #include "CachedRoot.h"
30 #include "IntRect.h"
31 #include "LayerAndroid.h"
32 #include "SkCanvas.h"
33 #include "SkCornerPathEffect.h"
34 #include "SkPath.h"
35 #include "SkRegion.h"
36 #include "WebViewCore.h"
37
38 namespace android {
39
40 // The CSS values for the inner and outer widths may be specified as fractions
41 #define WIDTH_SCALE 0.0625f // 1/16, to offset the scale in CSSStyleSelector
42
draw(SkCanvas * canvas,LayerAndroid * layer,IntRect * inval)43 void CursorRing::draw(SkCanvas* canvas, LayerAndroid* layer, IntRect* inval)
44 {
45 if (!m_lastBounds.isEmpty()) {
46 *inval = m_lastBounds;
47 m_lastBounds = IntRect(0, 0, 0, 0);
48 }
49 #if USE(ACCELERATED_COMPOSITING)
50 int layerId = m_node->isInLayer() ? m_frame->layer(m_node)->uniqueId() : -1;
51 if (layer->uniqueId() != layerId)
52 return;
53 #endif
54 if (canvas->quickReject(m_bounds, SkCanvas::kAA_EdgeType)) {
55 DBG_NAV_LOGD("canvas->quickReject cursorNode=%d (nodePointer=%p)"
56 " bounds=(%d,%d,w=%d,h=%d)", m_node->index(), m_node->nodePointer(),
57 m_bounds.x(), m_bounds.y(), m_bounds.width(), m_bounds.height());
58 return;
59 }
60 const CachedColor& colors = m_frame->color(m_node);
61 unsigned rectCount = m_rings.size();
62 SkRegion rgn;
63 SkPath path;
64 for (unsigned i = 0; i < rectCount; i++)
65 {
66 SkRect r(m_rings[i]);
67 SkIRect ir;
68
69 r.round(&ir);
70 ir.inset(-colors.outset(), -colors.outset());
71 rgn.op(ir, SkRegion::kUnion_Op);
72 }
73 rgn.getBoundaryPath(&path);
74
75 SkPaint paint;
76 paint.setAntiAlias(true);
77 paint.setPathEffect(new SkCornerPathEffect(
78 SkIntToScalar(colors.radius())))->unref();
79 SkColor outer;
80 SkColor inner;
81 if (m_isPressed) {
82 SkColor pressed;
83 pressed = colors.fillColor();
84 paint.setColor(pressed);
85 canvas->drawPath(path, paint);
86 outer = colors.pressedOuterColor();
87 inner = colors.pressedInnerColor();
88 } else {
89 outer = colors.selectedOuterColor();
90 inner = colors.selectedInnerColor();
91 }
92 paint.setStyle(SkPaint::kStroke_Style);
93 paint.setStrokeWidth(colors.outerWidth() * WIDTH_SCALE);
94 paint.setColor(outer);
95 canvas->drawPath(path, paint);
96 paint.setStrokeWidth(colors.innerWidth() * WIDTH_SCALE);
97 paint.setColor(inner);
98 canvas->drawPath(path, paint);
99 SkRect localBounds, globalBounds;
100 localBounds = path.getBounds();
101 float width = std::max(colors.innerWidth(), colors.outerWidth());
102 width *= WIDTH_SCALE;
103 localBounds.inset(-width, -width);
104 const SkMatrix& matrix = canvas->getTotalMatrix();
105 matrix.mapRect(&globalBounds, localBounds);
106 SkIRect globalIBounds;
107 globalBounds.round(&globalIBounds);
108 m_lastBounds = globalIBounds;
109 inval->unite(m_lastBounds);
110 }
111
setIsButton(const CachedNode * node)112 void CursorRing::setIsButton(const CachedNode* node)
113 {
114 m_isButton = false;
115 m_viewImpl->gButtonMutex.lock();
116 // If this is a button drawn by us (rather than webkit) do not draw the
117 // cursor ring, since its cursor will be shown by a change in what we draw.
118 // Should be in sync with recordButtons, since that will be called
119 // before this.
120 if (m_viewImpl->m_buttons.size() > 0) {
121 WebCore::Node* cursorPointer = (WebCore::Node*) node->nodePointer();
122 Container* end = m_viewImpl->m_buttons.end();
123 for (Container* ptr = m_viewImpl->m_buttons.begin(); ptr != end; ptr++) {
124 if (ptr->matches(cursorPointer)) {
125 m_isButton = true;
126 break;
127 }
128 }
129 }
130 m_viewImpl->gButtonMutex.unlock();
131 }
132
setup()133 bool CursorRing::setup()
134 {
135 m_node->cursorRings(m_frame, &m_rings);
136 if (!m_rings.size()) {
137 DBG_NAV_LOG("!rings.size()");
138 m_viewImpl->m_hasCursorBounds = false;
139 return false;
140 }
141 setIsButton(m_node);
142 m_bounds = m_node->bounds(m_frame);
143 m_viewImpl->updateCursorBounds(m_root, m_frame, m_node);
144
145 bool useHitBounds = m_node->useHitBounds();
146 if (useHitBounds)
147 m_bounds = m_node->hitBounds(m_frame);
148 if (useHitBounds || m_node->useBounds()) {
149 m_rings.clear();
150 m_rings.append(m_bounds);
151 }
152 m_absBounds = m_node->bounds(m_frame);
153 const CachedColor& colors = m_frame->color(m_node);
154 m_bounds.inflate(SkScalarCeil(colors.outerWidth()));
155 m_absBounds.inflate(SkScalarCeil(colors.outerWidth()));
156 if (!m_node->hasCursorRing() || (m_node->isPlugin() && m_node->isFocus()))
157 return false;
158 #if DEBUG_NAV_UI
159 const WebCore::IntRect& ring = m_rings[0];
160 DBG_NAV_LOGD("cursorNode=%d (nodePointer=%p) pressed=%s rings=%d"
161 " (%d, %d, %d, %d) isPlugin=%s",
162 m_node->index(), m_node->nodePointer(),
163 m_isPressed ? "true" : "false",
164 m_rings.size(), ring.x(), ring.y(), ring.width(), ring.height(),
165 m_node->isPlugin() ? "true" : "false");
166 DBG_NAV_LOGD("[%d] inner=%d outer=%d outset=%d radius=%d"
167 " fill=0x%08x pin=0x%08x pout=0x%08x sin=0x%08x sout=0x%08x",
168 m_node->colorIndex(), colors.innerWidth(), colors.outerWidth(),
169 colors.outset(), colors.radius(), colors.fillColor(),
170 colors.pressedInnerColor(), colors.pressedOuterColor(),
171 colors.selectedInnerColor(), colors.selectedInnerColor());
172 #endif
173 return true;
174 }
175
176 }
177