OLD | NEW |
| (Empty) |
1 // Copyright (c) 2008, Google Inc. | |
2 // All rights reserved. | |
3 // | |
4 // Redistribution and use in source and binary forms, with or without | |
5 // modification, are permitted provided that the following conditions are | |
6 // met: | |
7 // | |
8 // * Redistributions of source code must retain the above copyright | |
9 // notice, this list of conditions and the following disclaimer. | |
10 // * Redistributions in binary form must reproduce the above | |
11 // copyright notice, this list of conditions and the following disclaimer | |
12 // in the documentation and/or other materials provided with the | |
13 // distribution. | |
14 // * Neither the name of Google Inc. nor the names of its | |
15 // contributors may be used to endorse or promote products derived from | |
16 // this software without specific prior written permission. | |
17 // | |
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | |
30 #include "config.h" | |
31 #include <algorithm> | |
32 #include <windows.h> | |
33 #include <vsstyle.h> | |
34 #include "FrameView.h" | |
35 #include "GraphicsContext.h" | |
36 #include "IntRect.h" | |
37 #include "NativeImageSkia.h" | |
38 #include "PlatformMouseEvent.h" | |
39 #include "PlatformScrollBar.h" | |
40 #include "Range.h" | |
41 #include "ScrollView.h" | |
42 #include "WidgetClientWin.h" | |
43 | |
44 #include "graphics/SkiaUtils.h" | |
45 | |
46 #undef LOG | |
47 #include "base/gfx/native_theme.h" | |
48 #include "base/gfx/platform_canvas_win.h" | |
49 #include "base/win_util.h" | |
50 #include "webkit/glue/webframe_impl.h" | |
51 #include "webkit/glue/webkit_glue.h" | |
52 | |
53 namespace WebCore { | |
54 | |
55 const int PlatformScrollbar::kOffSideMultiplier = 8; | |
56 const int PlatformScrollbar::kOffEndMultiplier = 3; | |
57 const double PlatformScrollbar::kAutorepeatInitialDelay = 0.4; | |
58 const double PlatformScrollbar::kAutorepeatRepeatInterval = 1. / 15.; | |
59 | |
60 // The scrollbar size in DumpRenderTree on the Mac - so we can match their | |
61 // layout results. Entries are for regular, small, and mini scrollbars. | |
62 // Metrics obtained using [NSScroller scrollerWidthForControlSize:] | |
63 static const int kMacScrollbarSize[3] = {15, 11, 15}; | |
64 | |
65 // Scrollbar button and thumb sizes, for consistent layout results. The Mac | |
66 // value is not readily available, but it's not really needed, since these | |
67 // metrics only affect drawing within the scrollbar itself. These are the | |
68 // standard Windows values without Large Fonts. | |
69 static const int kLayoutTestScrollbarButtonGirth = 17; | |
70 static const int kLayoutTestScrollbarThumbGirth = 17; | |
71 | |
72 | |
73 /*static*/ void PlatformScrollbar::themeChanged() | |
74 { | |
75 // TODO(darin): implement this | |
76 } | |
77 | |
78 /*static*/ int PlatformScrollbar::horizontalScrollbarHeight( | |
79 ScrollbarControlSize controlSize) | |
80 { | |
81 return webkit_glue::IsLayoutTestMode() ? kMacScrollbarSize[controlSize] : | |
82 GetSystemMetrics(SM_CYHSCROLL); | |
83 } | |
84 | |
85 /*static*/ int PlatformScrollbar::verticalScrollbarWidth( | |
86 ScrollbarControlSize controlSize) | |
87 { | |
88 return webkit_glue::IsLayoutTestMode() ? kMacScrollbarSize[controlSize] : | |
89 GetSystemMetrics(SM_CXVSCROLL); | |
90 } | |
91 | |
92 PlatformScrollbar::PlatformScrollbar(ScrollbarClient* client, | |
93 ScrollbarOrientation orientation, | |
94 ScrollbarControlSize controlSize) | |
95 : Scrollbar(client, orientation, controlSize) | |
96 , m_lastNativePos(-1, -1) // initialize to bogus values | |
97 , m_mouseOver(None) | |
98 , m_captureStart(None) | |
99 #pragma warning(suppress: 4355) // it's okay to pass |this| here! | |
100 , m_autorepeatTimer(this, &PlatformScrollbar::autoscrollTimerFired) | |
101 , m_enabled(true) | |
102 , m_needsLayout(true) | |
103 { | |
104 } | |
105 | |
106 PlatformScrollbar::~PlatformScrollbar() | |
107 { | |
108 } | |
109 | |
110 int PlatformScrollbar::width() const | |
111 { | |
112 return orientation() == VerticalScrollbar ? | |
113 verticalScrollbarWidth(controlSize()) : Widget::width(); | |
114 } | |
115 | |
116 int PlatformScrollbar::height() const | |
117 { | |
118 return orientation() == HorizontalScrollbar ? | |
119 horizontalScrollbarHeight(controlSize()) : Widget::height(); | |
120 } | |
121 | |
122 void PlatformScrollbar::setRect(const IntRect& rect) | |
123 { | |
124 setFrameGeometry(rect); | |
125 } | |
126 | |
127 void PlatformScrollbar::setEnabled(bool enabled) | |
128 { | |
129 if (m_enabled == enabled) | |
130 return; | |
131 | |
132 m_enabled = enabled; | |
133 invalidate(); | |
134 } | |
135 | |
136 void PlatformScrollbar::DrawTickmarks(GraphicsContext* context) const | |
137 { | |
138 // We don't draw on the horizontal scrollbar. It is too confusing | |
139 // to have the tickmarks appear on both scrollbars. | |
140 const bool horz = orientation() == HorizontalScrollbar; | |
141 if (horz) | |
142 return; | |
143 | |
144 // We need to as the WidgetClientWin for the bitmap to use to draw. | |
145 WidgetClientWin* widget_client = static_cast<WidgetClientWin*>( | |
146 WebCore::Widget::client()); | |
147 if (!widget_client) | |
148 return; // Cannot draw without access to the bitmap. | |
149 | |
150 // Get the frame view this scroll bar belongs to. | |
151 FrameView* view = reinterpret_cast<FrameView*>(parent()); | |
152 ASSERT(view); | |
153 | |
154 // A frame can be null if this function is called for the scroll views | |
155 // used when drawing drop-down boxes. We don't need to draw anything in | |
156 // such cases. | |
157 if (!view->frame()) | |
158 return; | |
159 | |
160 // Find out if the frame has any tickmarks. | |
161 const Vector<RefPtr<Range> >& tickmarks = | |
162 WebFrameImpl::FromFrame(view->frame())->tickmarks(); | |
163 if (tickmarks.isEmpty()) | |
164 return; | |
165 | |
166 RECT track_area; | |
167 if (m_segmentRects[Track].left != -1) { | |
168 // Scroll bar is too small to draw a thumb. | |
169 track_area.left = m_segmentRects[Track].left; | |
170 track_area.top = m_segmentRects[Track].top; | |
171 track_area.right = m_segmentRects[Track].right - 1; | |
172 track_area.bottom = m_segmentRects[Track].bottom - 1; | |
173 } else { | |
174 // Find the area between the arrows of the scroll bar. | |
175 track_area.left = m_segmentRects[BeforeThumb].left; | |
176 track_area.top = m_segmentRects[BeforeThumb].top; | |
177 track_area.right = m_segmentRects[AfterThumb].right - 1; | |
178 track_area.bottom = m_segmentRects[AfterThumb].bottom - 1; | |
179 } | |
180 | |
181 // We now can figure out the actual height and width of the track. | |
182 const int track_height = track_area.bottom - track_area.top; | |
183 const int track_width = track_area.right - track_area.left; | |
184 | |
185 if (track_height <= 0 || track_width <= 0) | |
186 return; // nothing to draw on. | |
187 | |
188 // NOTE: We tolerate the platformContext() call here because the scrollbars | |
189 // will not be serialized, i.e. composition is done in the renderer and | |
190 // never in the browser. | |
191 // Prepare the bitmap for drawing the tickmarks on the scroll bar. | |
192 gfx::PlatformCanvas* canvas = context->platformContext()->canvas(); | |
193 | |
194 // Load the image for the tickmark. | |
195 static RefPtr<Image> dashImg = Image::loadPlatformResource("tickmarkDash"); | |
196 DCHECK(dashImg); | |
197 if (dashImg->isNull()) { | |
198 ASSERT_NOT_REACHED(); | |
199 return; | |
200 } | |
201 const NativeImageSkia* dash = dashImg->nativeImageForCurrentFrame(); | |
202 | |
203 for (Vector<RefPtr<Range> >::const_iterator i = | |
204 tickmarks.begin(); | |
205 i != tickmarks.end(); ++i) { | |
206 const RefPtr<Range> range = (*i); | |
207 | |
208 if (!WebFrameImpl::RangeShouldBeHighlighted(range.get())) | |
209 continue; | |
210 | |
211 const IntRect& bounds = range->boundingBox(); | |
212 | |
213 // Calculate how far down (in %) the tick-mark should appear. | |
214 const float percent = static_cast<float>(bounds.y()) / m_totalSize; | |
215 | |
216 // Calculate how far down (in pixels) the tick-mark should appear. | |
217 const int y_pos = track_area.top + (track_height * percent); | |
218 | |
219 // Draw the tick-mark as a rounded rect with a slightly curved edge. | |
220 canvas->drawBitmap(*dash, track_area.left, y_pos); | |
221 } | |
222 } | |
223 | |
224 // paint in the coordinate space of our parent's content area | |
225 void PlatformScrollbar::paint(GraphicsContext* gc, const IntRect& damageRect) | |
226 { | |
227 if (gc->paintingDisabled()) | |
228 return; | |
229 | |
230 // Don't paint anything if the scrollbar doesn't intersect the damage rect. | |
231 if (!frameGeometry().intersects(damageRect)) | |
232 return; | |
233 | |
234 gc->save(); | |
235 gc->translate(x(), y()); | |
236 | |
237 layout(); | |
238 | |
239 HDC hdc = gc->getWindowsContext(damageRect); | |
240 const bool horz = orientation() == HorizontalScrollbar; | |
241 const PlatformContextSkia* const skia = gc->platformContext(); | |
242 const gfx::NativeTheme* const nativeTheme = skia->nativeTheme(); | |
243 gfx::PlatformCanvasWin* const canvas = skia->canvas(); | |
244 | |
245 // Draw the up/left arrow of the scroll bar. | |
246 nativeTheme->PaintScrollbarArrow(hdc, getThemeArrowState(Arrow1), | |
247 (horz ? DFCS_SCROLLLEFT : DFCS_SCROLLUP) | | |
248 getClassicThemeState(Arrow1), | |
249 &m_segmentRects[Arrow1]); | |
250 | |
251 if (m_segmentRects[Track].left != -1) { | |
252 // The scroll bar is too small to draw the thumb. Just draw a | |
253 // single track between the arrows. | |
254 nativeTheme->PaintScrollbarTrack(hdc, | |
255 horz ? SBP_UPPERTRACKHORZ : | |
256 SBP_UPPERTRACKVERT, | |
257 getThemeState(Track), | |
258 getClassicThemeState(Track), | |
259 &m_segmentRects[Track], | |
260 &m_segmentRects[Track], canvas); | |
261 DrawTickmarks(gc); | |
262 } else { | |
263 // Draw the track area before the thumb on the scroll bar. | |
264 nativeTheme->PaintScrollbarTrack(hdc, | |
265 horz ? SBP_UPPERTRACKHORZ : | |
266 SBP_UPPERTRACKVERT, | |
267 getThemeState(BeforeThumb), | |
268 getClassicThemeState(BeforeThumb), | |
269 &m_segmentRects[BeforeThumb], | |
270 &m_segmentRects[BeforeThumb], canvas); | |
271 | |
272 // Draw the track area after the thumb on the scroll bar. | |
273 nativeTheme->PaintScrollbarTrack(hdc, | |
274 horz ? SBP_LOWERTRACKHORZ : | |
275 SBP_LOWERTRACKVERT, | |
276 getThemeState(AfterThumb), | |
277 getClassicThemeState(AfterThumb), | |
278 &m_segmentRects[AfterThumb], | |
279 &m_segmentRects[BeforeThumb], canvas); | |
280 | |
281 // Draw the tick-marks on the scroll bar, if any tick-marks | |
282 // exist. Note: The thumb will be drawn on top of the tick-marks, | |
283 // which is desired. | |
284 DrawTickmarks(gc); | |
285 | |
286 // Draw the thumb (the box you drag in the scroll bar to scroll). | |
287 nativeTheme->PaintScrollbarThumb(hdc, | |
288 horz ? SBP_THUMBBTNHORZ : | |
289 SBP_THUMBBTNVERT, | |
290 getThemeState(Thumb), | |
291 getClassicThemeState(Thumb), | |
292 &m_segmentRects[Thumb]); | |
293 | |
294 // Draw the gripper (the three little lines on the thumb). | |
295 nativeTheme->PaintScrollbarThumb(hdc, | |
296 horz ? SBP_GRIPPERHORZ : | |
297 SBP_GRIPPERVERT, | |
298 getThemeState(Thumb), | |
299 getClassicThemeState(Thumb), | |
300 &m_segmentRects[Thumb]); | |
301 } | |
302 | |
303 // Draw the down/right arrow of the scroll bar. | |
304 nativeTheme->PaintScrollbarArrow(hdc, getThemeArrowState(Arrow2), | |
305 (horz ? | |
306 DFCS_SCROLLRIGHT : DFCS_SCROLLDOWN) | | |
307 getClassicThemeState(Arrow2), | |
308 &m_segmentRects[Arrow2]); | |
309 gc->releaseWindowsContext(hdc, damageRect); | |
310 | |
311 gc->restore(); | |
312 } | |
313 | |
314 void PlatformScrollbar::setFrameGeometry(const IntRect& rect) | |
315 { | |
316 if (rect == frameGeometry()) | |
317 return; | |
318 | |
319 Widget::setFrameGeometry(rect); | |
320 m_needsLayout = true; | |
321 // NOTE: we assume that our caller will invalidate us | |
322 } | |
323 | |
324 bool PlatformScrollbar::handleMouseMoveEvent(const PlatformMouseEvent& e) | |
325 { | |
326 if (!parent()) | |
327 return true; | |
328 | |
329 if (m_captureStart != None) { | |
330 handleMouseMoveEventWhenCapturing(e); | |
331 return true; | |
332 } | |
333 | |
334 IntPoint pos = convertFromContainingWindow(e.pos()); | |
335 updateMousePosition(pos.x(), pos.y()); | |
336 | |
337 // FIXME: Invalidate only the portions that actually changed | |
338 invalidate(); | |
339 return true; | |
340 } | |
341 | |
342 bool PlatformScrollbar::handleMouseOutEvent(const PlatformMouseEvent& e) | |
343 { | |
344 if (!parent()) | |
345 return true; | |
346 | |
347 ASSERT(m_captureStart == None); | |
348 | |
349 // Pass bogus values that will never match real mouse coords. | |
350 updateMousePosition(-1, -1); | |
351 | |
352 // FIXME: Invalidate only the portions that actually changed | |
353 invalidate(); | |
354 return true; | |
355 } | |
356 | |
357 bool PlatformScrollbar::handleMouseReleaseEvent(const PlatformMouseEvent& e) | |
358 { | |
359 ScrollView* parentView = parent(); | |
360 if (!parentView) | |
361 return true; | |
362 | |
363 IntPoint pos = convertFromContainingWindow(e.pos()); | |
364 updateMousePosition(pos.x(), pos.y()); | |
365 | |
366 setCapturingMouse(false); | |
367 | |
368 // FIXME: Invalidate only the portions that actually changed | |
369 invalidate(); | |
370 return true; | |
371 } | |
372 | |
373 bool PlatformScrollbar::handleMousePressEvent(const PlatformMouseEvent& e) | |
374 { | |
375 if (!parent()) | |
376 return true; | |
377 | |
378 // TODO(pkasting): http://b/583875 Right-click should invoke a context menu | |
379 // (maybe this would be better handled elsewhere?) | |
380 if (!m_enabled || (e.button() != LeftButton)) { | |
381 return true; | |
382 } | |
383 | |
384 ASSERT(m_captureStart == None); | |
385 | |
386 IntPoint pos = convertFromContainingWindow(e.pos()); | |
387 | |
388 const bool horz = (orientation() == HorizontalScrollbar); | |
389 updateMousePosition(pos.x(), pos.y()); | |
390 switch (m_mouseOver) { | |
391 case Arrow1: | |
392 scroll(horz ? ScrollLeft : ScrollUp, ScrollByLine); | |
393 break; | |
394 case Track: | |
395 return true; | |
396 case BeforeThumb: | |
397 scroll(horz ? ScrollLeft : ScrollUp, ScrollByPage); | |
398 break; | |
399 case Thumb: | |
400 m_dragOrigin.thumbPos = horz ? pos.x() : pos.y(); | |
401 m_dragOrigin.scrollVal = value(); | |
402 break; | |
403 case AfterThumb: | |
404 scroll(horz ? ScrollRight : ScrollDown, ScrollByPage); | |
405 break; | |
406 case Arrow2: | |
407 scroll(horz ? ScrollRight : ScrollDown, ScrollByLine); | |
408 break; | |
409 default: | |
410 ASSERT_NOT_REACHED(); | |
411 } | |
412 | |
413 setCapturingMouse(true); | |
414 | |
415 // Kick off auto-repeat timer | |
416 if (m_mouseOver != Thumb) | |
417 m_autorepeatTimer.start(kAutorepeatInitialDelay, | |
418 kAutorepeatRepeatInterval); | |
419 | |
420 m_needsLayout = true; | |
421 // FIXME: Invalidate only the portions that actually changed | |
422 invalidate(); | |
423 | |
424 return true; | |
425 } | |
426 | |
427 void PlatformScrollbar::handleMouseMoveEventWhenCapturing(const PlatformMouseEve
nt& e) | |
428 { | |
429 IntPoint pos = convertFromContainingWindow(e.pos()); | |
430 updateMousePosition(pos.x(), pos.y()); | |
431 | |
432 if (m_captureStart != Thumb) { | |
433 // FIXME: Invalidate only the portions that actually changed | |
434 invalidate(); | |
435 return; | |
436 } | |
437 | |
438 int xCancelDistance, yCancelDistance, backgroundSpan, thumbGirth, delta; | |
439 // NOTE: The cancel distance calculations are based on the behavior of the | |
440 // MSVC8 main window scrollbar + some guessing/extrapolation | |
441 if (orientation() == HorizontalScrollbar) { | |
442 xCancelDistance = kOffEndMultiplier * horizontalScrollbarHeight(); | |
443 yCancelDistance = kOffSideMultiplier * horizontalScrollbarHeight(); | |
444 backgroundSpan = m_segmentRects[AfterThumb].right - | |
445 m_segmentRects[BeforeThumb].left; | |
446 thumbGirth = m_segmentRects[Thumb].right - m_segmentRects[Thumb].left; | |
447 delta = pos.x() - m_dragOrigin.thumbPos; | |
448 } else { | |
449 xCancelDistance = kOffSideMultiplier * verticalScrollbarWidth(); | |
450 yCancelDistance = kOffEndMultiplier * verticalScrollbarWidth(); | |
451 backgroundSpan = m_segmentRects[AfterThumb].bottom - | |
452 m_segmentRects[BeforeThumb].top; | |
453 thumbGirth = m_segmentRects[Thumb].bottom - m_segmentRects[Thumb].top; | |
454 delta = pos.y() - m_dragOrigin.thumbPos; | |
455 } | |
456 | |
457 // Snap scrollbar back to drag origin if mouse gets too far away | |
458 if ((m_lastNativePos.x() < | |
459 (m_segmentRects[BeforeThumb].left - xCancelDistance)) || | |
460 (m_lastNativePos.x() > | |
461 (m_segmentRects[AfterThumb].right + xCancelDistance)) || | |
462 (m_lastNativePos.y() < | |
463 (m_segmentRects[BeforeThumb].top - yCancelDistance)) || | |
464 (m_lastNativePos.y() > | |
465 (m_segmentRects[AfterThumb].bottom + yCancelDistance))) | |
466 delta = 0; | |
467 | |
468 // Convert delta from pixel coords to scrollbar logical coords | |
469 if (backgroundSpan > thumbGirth) { | |
470 if (setValue(m_dragOrigin.scrollVal + (delta * | |
471 (m_totalSize - m_visibleSize) / (backgroundSpan - thumbGirth)))) { | |
472 m_needsLayout = true; | |
473 // FIXME: Invalidate only the portions that actually changed | |
474 invalidate(); | |
475 } | |
476 } | |
477 } | |
478 | |
479 IntRect PlatformScrollbar::windowClipRect() const | |
480 { | |
481 if (m_client) | |
482 return m_client->windowClipRect(); | |
483 | |
484 return convertToContainingWindow(IntRect(0, 0, width(), height())); | |
485 } | |
486 | |
487 void PlatformScrollbar::updateThumbPosition() | |
488 { | |
489 m_needsLayout = true; | |
490 // FIXME: Invalidate only the portions that actually changed | |
491 invalidate(); | |
492 } | |
493 | |
494 void PlatformScrollbar::updateThumbProportion() | |
495 { | |
496 // RenderLayer::updateScrollInfoAfterLayout changes the enabled state when | |
497 // the style is OSCROLL, however it doesn't change it when the style is OAUT
O. | |
498 // As a workaround we enable the scrollbar if the visible size is less than | |
499 // the total size | |
500 if (!m_enabled && m_visibleSize < m_totalSize) { | |
501 setEnabled(true); | |
502 } | |
503 | |
504 // If the thumb was at the end of the track and the scrollbar was resized | |
505 // smaller, we need to cap the value to the new maximum. | |
506 if (setValue(value())) | |
507 return; // updateThumbPosition() already invalidated as needed | |
508 | |
509 m_needsLayout = true; | |
510 // FIXME: Invalidate only the portions that actually changed | |
511 invalidate(); | |
512 } | |
513 | |
514 void PlatformScrollbar::autoscrollTimerFired(Timer<PlatformScrollbar>*) | |
515 { | |
516 ASSERT((m_captureStart != None) && (m_mouseOver == m_captureStart)); | |
517 | |
518 const bool horz = (orientation() == HorizontalScrollbar); | |
519 switch (m_captureStart) { | |
520 case Arrow1: | |
521 scroll(horz ? ScrollLeft : ScrollUp, ScrollByLine); | |
522 break; | |
523 case BeforeThumb: | |
524 scroll(horz ? ScrollLeft : ScrollUp, ScrollByPage); | |
525 break; | |
526 case AfterThumb: | |
527 scroll(horz ? ScrollRight : ScrollDown, ScrollByPage); | |
528 break; | |
529 case Arrow2: | |
530 scroll(horz ? ScrollRight : ScrollDown, ScrollByLine); | |
531 break; | |
532 default: | |
533 ASSERT_NOT_REACHED(); | |
534 } | |
535 } | |
536 | |
537 void PlatformScrollbar::setCapturingMouse(bool capturing) | |
538 { | |
539 if (capturing) { | |
540 m_captureStart = m_mouseOver; | |
541 } else { | |
542 m_captureStart = None; | |
543 m_autorepeatTimer.stop(); | |
544 } | |
545 } | |
546 | |
547 int PlatformScrollbar::scrollButtonGirth(int systemMetricsCode, int limit, | |
548 int* backgroundSpan) | |
549 { | |
550 const int girth = webkit_glue::IsLayoutTestMode() ? | |
551 kLayoutTestScrollbarButtonGirth : GetSystemMetrics(systemMetricsCode); | |
552 *backgroundSpan = limit - 2 * girth; | |
553 if (*backgroundSpan < 0) { | |
554 *backgroundSpan = 0; | |
555 return limit / 2; | |
556 } | |
557 return girth; | |
558 } | |
559 | |
560 int PlatformScrollbar::scrollThumbGirth(int systemMetricsCode, | |
561 int backgroundSpan) | |
562 { | |
563 const int minimumGirth = webkit_glue::IsLayoutTestMode() ? | |
564 kLayoutTestScrollbarThumbGirth : GetSystemMetrics(systemMetricsCode); | |
565 return std::max<int>(m_visibleSize * backgroundSpan / m_totalSize, | |
566 minimumGirth); | |
567 } | |
568 | |
569 void PlatformScrollbar::layout() | |
570 { | |
571 if (!m_needsLayout) | |
572 return; | |
573 m_needsLayout = false; | |
574 | |
575 const RECT invalid = {-1, -1, -1, -1}; | |
576 if (m_totalSize <= 0) { | |
577 for (int i = 0; i < NumSegments; ++i) | |
578 m_segmentRects[i] = invalid; | |
579 return; | |
580 } | |
581 | |
582 int buttonGirth, backgroundSpan, thumbGirth; | |
583 RECT box = {0}; | |
584 LONG* changingCoord1, * changingCoord2; | |
585 // For both orientations, we allow the buttonGirth to determine the | |
586 // backgroundSpan directly, to avoid rounding errors. | |
587 if (orientation() == HorizontalScrollbar) { | |
588 buttonGirth = scrollButtonGirth(SM_CXHSCROLL, width(), &backgroundSpan); | |
589 thumbGirth = scrollThumbGirth(SM_CXHTHUMB, backgroundSpan); | |
590 box.bottom += horizontalScrollbarHeight(); | |
591 changingCoord1 = &box.left; | |
592 changingCoord2 = &box.right; | |
593 } else { | |
594 buttonGirth = scrollButtonGirth(SM_CYVSCROLL, height(), | |
595 &backgroundSpan); | |
596 thumbGirth = scrollThumbGirth(SM_CYVTHUMB, backgroundSpan); | |
597 box.right += verticalScrollbarWidth(); | |
598 changingCoord1 = &box.top; | |
599 changingCoord2 = &box.bottom; | |
600 } | |
601 | |
602 // Scrollbar: |<|--------|XXX|------|>| | |
603 // Start arrow: |<| | |
604 *changingCoord2 += buttonGirth; | |
605 m_segmentRects[Arrow1] = box; | |
606 | |
607 if (thumbGirth >= backgroundSpan) { | |
608 if (backgroundSpan == 0) { | |
609 m_segmentRects[Track] = invalid; | |
610 } else { | |
611 // Track: |-------------------| | |
612 *changingCoord1 = *changingCoord2; | |
613 *changingCoord2 += backgroundSpan; | |
614 m_segmentRects[Track] = box; | |
615 } | |
616 | |
617 m_segmentRects[BeforeThumb] = invalid; | |
618 m_segmentRects[Thumb] = invalid; | |
619 m_segmentRects[AfterThumb] = invalid; | |
620 } else { | |
621 m_segmentRects[Track] = invalid; | |
622 | |
623 const int thumbOffset = (m_totalSize <= m_visibleSize) ? 0 : (value() * | |
624 (backgroundSpan - thumbGirth) / (m_totalSize - m_visibleSize)); | |
625 // Before thumb: |--------| | |
626 *changingCoord1 = *changingCoord2; | |
627 *changingCoord2 += thumbOffset; | |
628 m_segmentRects[BeforeThumb] = box; | |
629 | |
630 // Thumb: |XXX| | |
631 *changingCoord1 = *changingCoord2; | |
632 *changingCoord2 += thumbGirth; | |
633 m_segmentRects[Thumb] = box; | |
634 | |
635 // After thumb: |------| | |
636 *changingCoord1 = *changingCoord2; | |
637 *changingCoord2 += backgroundSpan - (thumbOffset + thumbGirth); | |
638 m_segmentRects[AfterThumb] = box; | |
639 } | |
640 | |
641 // End arrow: |>| | |
642 *changingCoord1 = *changingCoord2; | |
643 *changingCoord2 += buttonGirth; | |
644 m_segmentRects[Arrow2] = box; | |
645 | |
646 // Changed layout, so need to update m_mouseOver and m_autorepeatTimer | |
647 updateMousePositionInternal(); | |
648 | |
649 // DO NOT invalidate() here. We already invalidate()d for this layout when | |
650 // setting m_needsLayout = true; by the time we reach this point, we're | |
651 // called by paint(), so invalidate() is not only unnecessary but will | |
652 // waste effort. | |
653 } | |
654 | |
655 void PlatformScrollbar::updateMousePosition(int x, int y) | |
656 { | |
657 m_lastNativePos.setX(x); | |
658 m_lastNativePos.setY(y); | |
659 | |
660 if (m_needsLayout) | |
661 layout(); // Calls updateMousePositionInternal() | |
662 else | |
663 updateMousePositionInternal(); | |
664 } | |
665 | |
666 void PlatformScrollbar::updateMousePositionInternal() | |
667 { | |
668 m_mouseOver = None; | |
669 for (int i = 0; i < NumSegments; ++i) { | |
670 if ((m_lastNativePos.x() >= m_segmentRects[i].left) && | |
671 (m_lastNativePos.x() < m_segmentRects[i].right) && | |
672 (m_lastNativePos.y() >= m_segmentRects[i].top) && | |
673 (m_lastNativePos.y() < m_segmentRects[i].bottom)) { | |
674 m_mouseOver = static_cast<Segment>(i); | |
675 break; | |
676 } | |
677 } | |
678 | |
679 // Start/stop autorepeat timer if capturing something other than the thumb. | |
680 if ((m_captureStart != None) && (m_captureStart != Thumb)) { | |
681 if (m_mouseOver != m_captureStart) | |
682 m_autorepeatTimer.stop(); // Safe to call when already stopped | |
683 else if (!m_autorepeatTimer.isActive()) | |
684 m_autorepeatTimer.startRepeating(kAutorepeatRepeatInterval); | |
685 } | |
686 } | |
687 | |
688 int PlatformScrollbar::getThemeState(Segment target) const | |
689 { | |
690 // When dragging the thumb, draw thumb pressed and other segments normal | |
691 // regardless of where the cursor actually is. See also four places in | |
692 // getThemeArrowState(). | |
693 if (m_captureStart == Thumb) { | |
694 if (target == Thumb) | |
695 return SCRBS_PRESSED; | |
696 return (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) ? | |
697 SCRBS_NORMAL : SCRBS_HOVER; | |
698 } | |
699 if (!m_enabled) | |
700 return SCRBS_DISABLED; | |
701 if ((m_mouseOver != target) || (target == Track)) { | |
702 return ((m_mouseOver == None) || | |
703 (win_util::GetWinVersion() < win_util::WINVERSION_VISTA)) ? | |
704 SCRBS_NORMAL : SCRBS_HOVER; | |
705 } | |
706 if (m_captureStart == None) | |
707 return SCRBS_HOT; | |
708 return (m_captureStart == target) ? SCRBS_PRESSED : SCRBS_NORMAL; | |
709 } | |
710 | |
711 int PlatformScrollbar::getThemeArrowState(Segment target) const | |
712 { | |
713 // We could take advantage of knowing the values in the state enum to write | |
714 // some simpler code, but treating the state enum as a black box seems | |
715 // clearer and more future-proof. | |
716 if (target == Arrow1) { | |
717 if (orientation() == HorizontalScrollbar) { | |
718 if (m_captureStart == Thumb) { | |
719 return | |
720 (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) ? | |
721 ABS_LEFTNORMAL : ABS_LEFTHOVER; | |
722 } | |
723 if (!m_enabled) | |
724 return ABS_LEFTDISABLED; | |
725 if (m_mouseOver != target) { | |
726 return ((m_mouseOver == None) || | |
727 (win_util::GetWinVersion() < win_util::WINVERSION_VISTA)) ? | |
728 ABS_LEFTNORMAL : ABS_LEFTHOVER; | |
729 } | |
730 if (m_captureStart == None) | |
731 return ABS_LEFTHOT; | |
732 return (m_captureStart == target) ? | |
733 ABS_LEFTPRESSED : ABS_LEFTNORMAL; | |
734 } | |
735 if (m_captureStart == Thumb) { | |
736 return (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) ? | |
737 ABS_UPNORMAL : ABS_UPHOVER; | |
738 } | |
739 if (!m_enabled) | |
740 return ABS_UPDISABLED; | |
741 if (m_mouseOver != target) { | |
742 return ((m_mouseOver == None) || | |
743 (win_util::GetWinVersion() < win_util::WINVERSION_VISTA)) ? | |
744 ABS_UPNORMAL : ABS_UPHOVER; | |
745 } | |
746 if (m_captureStart == None) | |
747 return ABS_UPHOT; | |
748 return (m_captureStart == target) ? ABS_UPPRESSED : ABS_UPNORMAL; | |
749 } | |
750 if (orientation() == HorizontalScrollbar) { | |
751 if (m_captureStart == Thumb) { | |
752 return (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) ? | |
753 ABS_RIGHTNORMAL : ABS_RIGHTHOVER; | |
754 } | |
755 if (!m_enabled) | |
756 return ABS_RIGHTDISABLED; | |
757 if (m_mouseOver != target) { | |
758 return ((m_mouseOver == None) || | |
759 (win_util::GetWinVersion() < win_util::WINVERSION_VISTA)) ? | |
760 ABS_RIGHTNORMAL : ABS_RIGHTHOVER; | |
761 } | |
762 if (m_captureStart == None) | |
763 return ABS_RIGHTHOT; | |
764 return (m_captureStart == target) ? ABS_RIGHTPRESSED : ABS_RIGHTNORMAL; | |
765 } | |
766 if (m_captureStart == Thumb) { | |
767 return (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) ? | |
768 ABS_DOWNNORMAL : ABS_DOWNHOVER; | |
769 } | |
770 if (!m_enabled) | |
771 return ABS_DOWNDISABLED; | |
772 if (m_mouseOver != target) { | |
773 return ((m_mouseOver == None) || | |
774 (win_util::GetWinVersion() < win_util::WINVERSION_VISTA)) ? | |
775 ABS_DOWNNORMAL : ABS_DOWNHOVER; | |
776 } | |
777 if (m_captureStart == None) | |
778 return ABS_DOWNHOT; | |
779 return (m_captureStart == target) ? ABS_DOWNPRESSED : ABS_DOWNNORMAL; | |
780 } | |
781 | |
782 int PlatformScrollbar::getClassicThemeState(Segment target) const | |
783 { | |
784 // When dragging the thumb, draw the buttons normal even when hovered. | |
785 if (m_captureStart == Thumb) | |
786 return 0; | |
787 if (!m_enabled) | |
788 return DFCS_INACTIVE; | |
789 if ((m_mouseOver != target) || (target == Track)) | |
790 return 0; | |
791 if (m_captureStart == None) | |
792 return DFCS_HOT; | |
793 return (m_captureStart == target) ? (DFCS_PUSHED | DFCS_FLAT) : 0; | |
794 } | |
795 | |
796 } // namespace WebCore | |
OLD | NEW |