1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/xul/nsSliderFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1161 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// 1.10 +// Eric Vaughan 1.11 +// Netscape Communications 1.12 +// 1.13 +// See documentation in associated header file 1.14 +// 1.15 + 1.16 +#include "nsSliderFrame.h" 1.17 +#include "nsStyleContext.h" 1.18 +#include "nsPresContext.h" 1.19 +#include "nsIContent.h" 1.20 +#include "nsCOMPtr.h" 1.21 +#include "nsNameSpaceManager.h" 1.22 +#include "nsGkAtoms.h" 1.23 +#include "nsHTMLParts.h" 1.24 +#include "nsIPresShell.h" 1.25 +#include "nsCSSRendering.h" 1.26 +#include "nsIDOMMouseEvent.h" 1.27 +#include "nsScrollbarButtonFrame.h" 1.28 +#include "nsISliderListener.h" 1.29 +#include "nsIScrollbarMediator.h" 1.30 +#include "nsScrollbarFrame.h" 1.31 +#include "nsRepeatService.h" 1.32 +#include "nsBoxLayoutState.h" 1.33 +#include "nsSprocketLayout.h" 1.34 +#include "nsIServiceManager.h" 1.35 +#include "nsContentUtils.h" 1.36 +#include "nsLayoutUtils.h" 1.37 +#include "nsDisplayList.h" 1.38 +#include "mozilla/Preferences.h" 1.39 +#include "mozilla/LookAndFeel.h" 1.40 +#include "mozilla/MouseEvents.h" 1.41 +#include <algorithm> 1.42 + 1.43 +using namespace mozilla; 1.44 + 1.45 +bool nsSliderFrame::gMiddlePref = false; 1.46 +int32_t nsSliderFrame::gSnapMultiplier; 1.47 + 1.48 +// Turn this on if you want to debug slider frames. 1.49 +#undef DEBUG_SLIDER 1.50 + 1.51 +static already_AddRefed<nsIContent> 1.52 +GetContentOfBox(nsIFrame *aBox) 1.53 +{ 1.54 + nsCOMPtr<nsIContent> content = aBox->GetContent(); 1.55 + return content.forget(); 1.56 +} 1.57 + 1.58 +nsIFrame* 1.59 +NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) 1.60 +{ 1.61 + return new (aPresShell) nsSliderFrame(aPresShell, aContext); 1.62 +} 1.63 + 1.64 +NS_IMPL_FRAMEARENA_HELPERS(nsSliderFrame) 1.65 + 1.66 +nsSliderFrame::nsSliderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext): 1.67 + nsBoxFrame(aPresShell, aContext), 1.68 + mCurPos(0), 1.69 + mChange(0), 1.70 + mUserChanged(false) 1.71 +{ 1.72 +} 1.73 + 1.74 +// stop timer 1.75 +nsSliderFrame::~nsSliderFrame() 1.76 +{ 1.77 +} 1.78 + 1.79 +void 1.80 +nsSliderFrame::Init(nsIContent* aContent, 1.81 + nsIFrame* aParent, 1.82 + nsIFrame* aPrevInFlow) 1.83 +{ 1.84 + nsBoxFrame::Init(aContent, aParent, aPrevInFlow); 1.85 + 1.86 + static bool gotPrefs = false; 1.87 + if (!gotPrefs) { 1.88 + gotPrefs = true; 1.89 + 1.90 + gMiddlePref = Preferences::GetBool("middlemouse.scrollbarPosition"); 1.91 + gSnapMultiplier = Preferences::GetInt("slider.snapMultiplier"); 1.92 + } 1.93 + 1.94 + mCurPos = GetCurrentPosition(aContent); 1.95 +} 1.96 + 1.97 +nsresult 1.98 +nsSliderFrame::RemoveFrame(ChildListID aListID, 1.99 + nsIFrame* aOldFrame) 1.100 +{ 1.101 + nsresult rv = nsBoxFrame::RemoveFrame(aListID, aOldFrame); 1.102 + if (mFrames.IsEmpty()) 1.103 + RemoveListener(); 1.104 + 1.105 + return rv; 1.106 +} 1.107 + 1.108 +nsresult 1.109 +nsSliderFrame::InsertFrames(ChildListID aListID, 1.110 + nsIFrame* aPrevFrame, 1.111 + nsFrameList& aFrameList) 1.112 +{ 1.113 + bool wasEmpty = mFrames.IsEmpty(); 1.114 + nsresult rv = nsBoxFrame::InsertFrames(aListID, aPrevFrame, aFrameList); 1.115 + if (wasEmpty) 1.116 + AddListener(); 1.117 + 1.118 + return rv; 1.119 +} 1.120 + 1.121 +nsresult 1.122 +nsSliderFrame::AppendFrames(ChildListID aListID, 1.123 + nsFrameList& aFrameList) 1.124 +{ 1.125 + // if we have no children and on was added then make sure we add the 1.126 + // listener 1.127 + bool wasEmpty = mFrames.IsEmpty(); 1.128 + nsresult rv = nsBoxFrame::AppendFrames(aListID, aFrameList); 1.129 + if (wasEmpty) 1.130 + AddListener(); 1.131 + 1.132 + return rv; 1.133 +} 1.134 + 1.135 +int32_t 1.136 +nsSliderFrame::GetCurrentPosition(nsIContent* content) 1.137 +{ 1.138 + return GetIntegerAttribute(content, nsGkAtoms::curpos, 0); 1.139 +} 1.140 + 1.141 +int32_t 1.142 +nsSliderFrame::GetMinPosition(nsIContent* content) 1.143 +{ 1.144 + return GetIntegerAttribute(content, nsGkAtoms::minpos, 0); 1.145 +} 1.146 + 1.147 +int32_t 1.148 +nsSliderFrame::GetMaxPosition(nsIContent* content) 1.149 +{ 1.150 + return GetIntegerAttribute(content, nsGkAtoms::maxpos, 100); 1.151 +} 1.152 + 1.153 +int32_t 1.154 +nsSliderFrame::GetIncrement(nsIContent* content) 1.155 +{ 1.156 + return GetIntegerAttribute(content, nsGkAtoms::increment, 1); 1.157 +} 1.158 + 1.159 + 1.160 +int32_t 1.161 +nsSliderFrame::GetPageIncrement(nsIContent* content) 1.162 +{ 1.163 + return GetIntegerAttribute(content, nsGkAtoms::pageincrement, 10); 1.164 +} 1.165 + 1.166 +int32_t 1.167 +nsSliderFrame::GetIntegerAttribute(nsIContent* content, nsIAtom* atom, int32_t defaultValue) 1.168 +{ 1.169 + nsAutoString value; 1.170 + content->GetAttr(kNameSpaceID_None, atom, value); 1.171 + if (!value.IsEmpty()) { 1.172 + nsresult error; 1.173 + 1.174 + // convert it to an integer 1.175 + defaultValue = value.ToInteger(&error); 1.176 + } 1.177 + 1.178 + return defaultValue; 1.179 +} 1.180 + 1.181 +class nsValueChangedRunnable : public nsRunnable 1.182 +{ 1.183 +public: 1.184 + nsValueChangedRunnable(nsISliderListener* aListener, 1.185 + nsIAtom* aWhich, 1.186 + int32_t aValue, 1.187 + bool aUserChanged) 1.188 + : mListener(aListener), mWhich(aWhich), 1.189 + mValue(aValue), mUserChanged(aUserChanged) 1.190 + {} 1.191 + 1.192 + NS_IMETHODIMP Run() 1.193 + { 1.194 + return mListener->ValueChanged(nsDependentAtomString(mWhich), 1.195 + mValue, mUserChanged); 1.196 + } 1.197 + 1.198 + nsCOMPtr<nsISliderListener> mListener; 1.199 + nsCOMPtr<nsIAtom> mWhich; 1.200 + int32_t mValue; 1.201 + bool mUserChanged; 1.202 +}; 1.203 + 1.204 +class nsDragStateChangedRunnable : public nsRunnable 1.205 +{ 1.206 +public: 1.207 + nsDragStateChangedRunnable(nsISliderListener* aListener, 1.208 + bool aDragBeginning) 1.209 + : mListener(aListener), 1.210 + mDragBeginning(aDragBeginning) 1.211 + {} 1.212 + 1.213 + NS_IMETHODIMP Run() 1.214 + { 1.215 + return mListener->DragStateChanged(mDragBeginning); 1.216 + } 1.217 + 1.218 + nsCOMPtr<nsISliderListener> mListener; 1.219 + bool mDragBeginning; 1.220 +}; 1.221 + 1.222 +nsresult 1.223 +nsSliderFrame::AttributeChanged(int32_t aNameSpaceID, 1.224 + nsIAtom* aAttribute, 1.225 + int32_t aModType) 1.226 +{ 1.227 + nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, 1.228 + aModType); 1.229 + // if the current position changes 1.230 + if (aAttribute == nsGkAtoms::curpos) { 1.231 + CurrentPositionChanged(); 1.232 + } else if (aAttribute == nsGkAtoms::minpos || 1.233 + aAttribute == nsGkAtoms::maxpos) { 1.234 + // bounds check it. 1.235 + 1.236 + nsIFrame* scrollbarBox = GetScrollbar(); 1.237 + nsCOMPtr<nsIContent> scrollbar; 1.238 + scrollbar = GetContentOfBox(scrollbarBox); 1.239 + int32_t current = GetCurrentPosition(scrollbar); 1.240 + int32_t min = GetMinPosition(scrollbar); 1.241 + int32_t max = GetMaxPosition(scrollbar); 1.242 + 1.243 + // inform the parent <scale> that the minimum or maximum changed 1.244 + nsIFrame* parent = GetParent(); 1.245 + if (parent) { 1.246 + nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent()); 1.247 + if (sliderListener) { 1.248 + nsContentUtils::AddScriptRunner( 1.249 + new nsValueChangedRunnable(sliderListener, aAttribute, 1.250 + aAttribute == nsGkAtoms::minpos ? min : max, false)); 1.251 + } 1.252 + } 1.253 + 1.254 + if (current < min || current > max) 1.255 + { 1.256 + if (current < min || max < min) 1.257 + current = min; 1.258 + else if (current > max) 1.259 + current = max; 1.260 + 1.261 + // set the new position and notify observers 1.262 + nsScrollbarFrame* scrollbarFrame = do_QueryFrame(scrollbarBox); 1.263 + if (scrollbarFrame) { 1.264 + nsIScrollbarMediator* mediator = scrollbarFrame->GetScrollbarMediator(); 1.265 + if (mediator) { 1.266 + mediator->PositionChanged(scrollbarFrame, GetCurrentPosition(scrollbar), current); 1.267 + } 1.268 + } 1.269 + 1.270 + nsContentUtils::AddScriptRunner( 1.271 + new nsSetAttrRunnable(scrollbar, nsGkAtoms::curpos, current)); 1.272 + } 1.273 + } 1.274 + 1.275 + if (aAttribute == nsGkAtoms::minpos || 1.276 + aAttribute == nsGkAtoms::maxpos || 1.277 + aAttribute == nsGkAtoms::pageincrement || 1.278 + aAttribute == nsGkAtoms::increment) { 1.279 + 1.280 + PresContext()->PresShell()-> 1.281 + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); 1.282 + } 1.283 + 1.284 + return rv; 1.285 +} 1.286 + 1.287 +void 1.288 +nsSliderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.289 + const nsRect& aDirtyRect, 1.290 + const nsDisplayListSet& aLists) 1.291 +{ 1.292 + if (aBuilder->IsForEventDelivery() && isDraggingThumb()) { 1.293 + // This is EVIL, we shouldn't be messing with event delivery just to get 1.294 + // thumb mouse drag events to arrive at the slider! 1.295 + aLists.Outlines()->AppendNewToTop(new (aBuilder) 1.296 + nsDisplayEventReceiver(aBuilder, this)); 1.297 + return; 1.298 + } 1.299 + 1.300 + nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); 1.301 +} 1.302 + 1.303 +void 1.304 +nsSliderFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, 1.305 + const nsRect& aDirtyRect, 1.306 + const nsDisplayListSet& aLists) 1.307 +{ 1.308 + // if we are too small to have a thumb don't paint it. 1.309 + nsIFrame* thumb = GetChildBox(); 1.310 + 1.311 + if (thumb) { 1.312 + nsRect thumbRect(thumb->GetRect()); 1.313 + nsMargin m; 1.314 + thumb->GetMargin(m); 1.315 + thumbRect.Inflate(m); 1.316 + 1.317 + nsRect crect; 1.318 + GetClientRect(crect); 1.319 + 1.320 + if (crect.width < thumbRect.width || crect.height < thumbRect.height) 1.321 + return; 1.322 + } 1.323 + 1.324 + nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, aLists); 1.325 +} 1.326 + 1.327 +NS_IMETHODIMP 1.328 +nsSliderFrame::DoLayout(nsBoxLayoutState& aState) 1.329 +{ 1.330 + // get the thumb should be our only child 1.331 + nsIFrame* thumbBox = GetChildBox(); 1.332 + 1.333 + if (!thumbBox) { 1.334 + SyncLayout(aState); 1.335 + return NS_OK; 1.336 + } 1.337 + 1.338 + EnsureOrient(); 1.339 + 1.340 +#ifdef DEBUG_LAYOUT 1.341 + if (mState & NS_STATE_DEBUG_WAS_SET) { 1.342 + if (mState & NS_STATE_SET_TO_DEBUG) 1.343 + SetDebug(aState, true); 1.344 + else 1.345 + SetDebug(aState, false); 1.346 + } 1.347 +#endif 1.348 + 1.349 + // get the content area inside our borders 1.350 + nsRect clientRect; 1.351 + GetClientRect(clientRect); 1.352 + 1.353 + // get the scrollbar 1.354 + nsIFrame* scrollbarBox = GetScrollbar(); 1.355 + nsCOMPtr<nsIContent> scrollbar; 1.356 + scrollbar = GetContentOfBox(scrollbarBox); 1.357 + 1.358 + // get the thumb's pref size 1.359 + nsSize thumbSize = thumbBox->GetPrefSize(aState); 1.360 + 1.361 + if (IsHorizontal()) 1.362 + thumbSize.height = clientRect.height; 1.363 + else 1.364 + thumbSize.width = clientRect.width; 1.365 + 1.366 + int32_t curPos = GetCurrentPosition(scrollbar); 1.367 + int32_t minPos = GetMinPosition(scrollbar); 1.368 + int32_t maxPos = GetMaxPosition(scrollbar); 1.369 + int32_t pageIncrement = GetPageIncrement(scrollbar); 1.370 + 1.371 + maxPos = std::max(minPos, maxPos); 1.372 + curPos = clamped(curPos, minPos, maxPos); 1.373 + 1.374 + nscoord& availableLength = IsHorizontal() ? clientRect.width : clientRect.height; 1.375 + nscoord& thumbLength = IsHorizontal() ? thumbSize.width : thumbSize.height; 1.376 + 1.377 + if ((pageIncrement + maxPos - minPos) > 0 && thumbBox->GetFlex(aState) > 0) { 1.378 + float ratio = float(pageIncrement) / float(maxPos - minPos + pageIncrement); 1.379 + thumbLength = std::max(thumbLength, NSToCoordRound(availableLength * ratio)); 1.380 + } 1.381 + 1.382 + // Round the thumb's length to device pixels. 1.383 + nsPresContext* presContext = PresContext(); 1.384 + thumbLength = presContext->DevPixelsToAppUnits( 1.385 + presContext->AppUnitsToDevPixels(thumbLength)); 1.386 + 1.387 + // mRatio translates the thumb position in app units to the value. 1.388 + mRatio = (minPos != maxPos) ? float(availableLength - thumbLength) / float(maxPos - minPos) : 1; 1.389 + 1.390 + // in reverse mode, curpos is reversed such that lower values are to the 1.391 + // right or bottom and increase leftwards or upwards. In this case, use the 1.392 + // offset from the end instead of the beginning. 1.393 + bool reverse = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, 1.394 + nsGkAtoms::reverse, eCaseMatters); 1.395 + nscoord pos = reverse ? (maxPos - curPos) : (curPos - minPos); 1.396 + 1.397 + // set the thumb's coord to be the current pos * the ratio. 1.398 + nsRect thumbRect(clientRect.x, clientRect.y, thumbSize.width, thumbSize.height); 1.399 + int32_t& thumbPos = (IsHorizontal() ? thumbRect.x : thumbRect.y); 1.400 + thumbPos += NSToCoordRound(pos * mRatio); 1.401 + 1.402 + nsRect oldThumbRect(thumbBox->GetRect()); 1.403 + LayoutChildAt(aState, thumbBox, thumbRect); 1.404 + 1.405 + SyncLayout(aState); 1.406 + 1.407 + // Redraw only if thumb changed size. 1.408 + if (!oldThumbRect.IsEqualInterior(thumbRect)) 1.409 + Redraw(aState); 1.410 + 1.411 + return NS_OK; 1.412 +} 1.413 + 1.414 + 1.415 +nsresult 1.416 +nsSliderFrame::HandleEvent(nsPresContext* aPresContext, 1.417 + WidgetGUIEvent* aEvent, 1.418 + nsEventStatus* aEventStatus) 1.419 +{ 1.420 + NS_ENSURE_ARG_POINTER(aEventStatus); 1.421 + 1.422 + // If a web page calls event.preventDefault() we still want to 1.423 + // scroll when scroll arrow is clicked. See bug 511075. 1.424 + if (!mContent->IsInNativeAnonymousSubtree() && 1.425 + nsEventStatus_eConsumeNoDefault == *aEventStatus) { 1.426 + return NS_OK; 1.427 + } 1.428 + 1.429 + nsIFrame* scrollbarBox = GetScrollbar(); 1.430 + nsCOMPtr<nsIContent> scrollbar; 1.431 + scrollbar = GetContentOfBox(scrollbarBox); 1.432 + bool isHorizontal = IsHorizontal(); 1.433 + 1.434 + if (isDraggingThumb()) 1.435 + { 1.436 + switch (aEvent->message) { 1.437 + case NS_TOUCH_MOVE: 1.438 + case NS_MOUSE_MOVE: { 1.439 + nsPoint eventPoint; 1.440 + if (!GetEventPoint(aEvent, eventPoint)) { 1.441 + break; 1.442 + } 1.443 + if (mChange) { 1.444 + // We're in the process of moving the thumb to the mouse, 1.445 + // but the mouse just moved. Make sure to update our 1.446 + // destination point. 1.447 + mDestinationPoint = eventPoint; 1.448 + StopRepeat(); 1.449 + StartRepeat(); 1.450 + break; 1.451 + } 1.452 + 1.453 + nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y; 1.454 + 1.455 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.456 + if (!thumbFrame) { 1.457 + return NS_OK; 1.458 + } 1.459 + 1.460 + // take our current position and subtract the start location 1.461 + pos -= mDragStart; 1.462 + bool isMouseOutsideThumb = false; 1.463 + if (gSnapMultiplier) { 1.464 + nsSize thumbSize = thumbFrame->GetSize(); 1.465 + if (isHorizontal) { 1.466 + // horizontal scrollbar - check if mouse is above or below thumb 1.467 + // XXXbz what about looking at the .y of the thumb's rect? Is that 1.468 + // always zero here? 1.469 + if (eventPoint.y < -gSnapMultiplier * thumbSize.height || 1.470 + eventPoint.y > thumbSize.height + 1.471 + gSnapMultiplier * thumbSize.height) 1.472 + isMouseOutsideThumb = true; 1.473 + } 1.474 + else { 1.475 + // vertical scrollbar - check if mouse is left or right of thumb 1.476 + if (eventPoint.x < -gSnapMultiplier * thumbSize.width || 1.477 + eventPoint.x > thumbSize.width + 1.478 + gSnapMultiplier * thumbSize.width) 1.479 + isMouseOutsideThumb = true; 1.480 + } 1.481 + } 1.482 + if (aEvent->eventStructType == NS_TOUCH_EVENT) { 1.483 + *aEventStatus = nsEventStatus_eConsumeNoDefault; 1.484 + } 1.485 + if (isMouseOutsideThumb) 1.486 + { 1.487 + SetCurrentThumbPosition(scrollbar, mThumbStart, false, false); 1.488 + return NS_OK; 1.489 + } 1.490 + 1.491 + // set it 1.492 + SetCurrentThumbPosition(scrollbar, pos, false, true); // with snapping 1.493 + } 1.494 + break; 1.495 + 1.496 + case NS_TOUCH_END: 1.497 + case NS_MOUSE_BUTTON_UP: 1.498 + if (ShouldScrollForEvent(aEvent)) { 1.499 + // stop capturing 1.500 + AddListener(); 1.501 + DragThumb(false); 1.502 + if (mChange) { 1.503 + StopRepeat(); 1.504 + mChange = 0; 1.505 + } 1.506 + //we MUST call nsFrame HandleEvent for mouse ups to maintain the selection state and capture state. 1.507 + return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); 1.508 + } 1.509 + } 1.510 + 1.511 + //return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); 1.512 + return NS_OK; 1.513 + } else if (ShouldScrollToClickForEvent(aEvent)) { 1.514 + nsPoint eventPoint; 1.515 + if (!GetEventPoint(aEvent, eventPoint)) { 1.516 + return NS_OK; 1.517 + } 1.518 + nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y; 1.519 + 1.520 + // adjust so that the middle of the thumb is placed under the click 1.521 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.522 + if (!thumbFrame) { 1.523 + return NS_OK; 1.524 + } 1.525 + nsSize thumbSize = thumbFrame->GetSize(); 1.526 + nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height; 1.527 + 1.528 + // set it 1.529 + nsWeakFrame weakFrame(this); 1.530 + // should aMaySnap be true here? 1.531 + SetCurrentThumbPosition(scrollbar, pos - thumbLength/2, false, false); 1.532 + NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); 1.533 + 1.534 + DragThumb(true); 1.535 + if (aEvent->eventStructType == NS_TOUCH_EVENT) { 1.536 + *aEventStatus = nsEventStatus_eConsumeNoDefault; 1.537 + } 1.538 + 1.539 + if (isHorizontal) 1.540 + mThumbStart = thumbFrame->GetPosition().x; 1.541 + else 1.542 + mThumbStart = thumbFrame->GetPosition().y; 1.543 + 1.544 + mDragStart = pos - mThumbStart; 1.545 + } 1.546 + 1.547 + // XXX hack until handle release is actually called in nsframe. 1.548 +// if (aEvent->message == NS_MOUSE_EXIT_SYNTH || aEvent->message == NS_MOUSE_RIGHT_BUTTON_UP || aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) 1.549 + // HandleRelease(aPresContext, aEvent, aEventStatus); 1.550 + 1.551 + if (aEvent->message == NS_MOUSE_EXIT_SYNTH && mChange) 1.552 + HandleRelease(aPresContext, aEvent, aEventStatus); 1.553 + 1.554 + return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); 1.555 +} 1.556 + 1.557 +// Helper function to collect the "scroll to click" metric. Beware of 1.558 +// caching this, users expect to be able to change the system preference 1.559 +// and see the browser change its behavior immediately. 1.560 +bool 1.561 +nsSliderFrame::GetScrollToClick() 1.562 +{ 1.563 + if (GetScrollbar() != this) { 1.564 + return LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollToClick, false); 1.565 + } 1.566 + 1.567 + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick, 1.568 + nsGkAtoms::_true, eCaseMatters)) { 1.569 + return true; 1.570 + } 1.571 + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick, 1.572 + nsGkAtoms::_false, eCaseMatters)) { 1.573 + return false; 1.574 + } 1.575 + 1.576 +#ifdef XP_MACOSX 1.577 + return true; 1.578 +#else 1.579 + return false; 1.580 +#endif 1.581 +} 1.582 + 1.583 +nsIFrame* 1.584 +nsSliderFrame::GetScrollbar() 1.585 +{ 1.586 + // if we are in a scrollbar then return the scrollbar's content node 1.587 + // if we are not then return ours. 1.588 + nsIFrame* scrollbar; 1.589 + nsScrollbarButtonFrame::GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar); 1.590 + 1.591 + if (scrollbar == nullptr) 1.592 + return this; 1.593 + 1.594 + return scrollbar->IsBoxFrame() ? scrollbar : this; 1.595 +} 1.596 + 1.597 +void 1.598 +nsSliderFrame::PageUpDown(nscoord change) 1.599 +{ 1.600 + // on a page up or down get our page increment. We get this by getting the scrollbar we are in and 1.601 + // asking it for the current position and the page increment. If we are not in a scrollbar we will 1.602 + // get the values from our own node. 1.603 + nsIFrame* scrollbarBox = GetScrollbar(); 1.604 + nsCOMPtr<nsIContent> scrollbar; 1.605 + scrollbar = GetContentOfBox(scrollbarBox); 1.606 + 1.607 + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, 1.608 + nsGkAtoms::reverse, eCaseMatters)) 1.609 + change = -change; 1.610 + 1.611 + nscoord pageIncrement = GetPageIncrement(scrollbar); 1.612 + int32_t curpos = GetCurrentPosition(scrollbar); 1.613 + int32_t minpos = GetMinPosition(scrollbar); 1.614 + int32_t maxpos = GetMaxPosition(scrollbar); 1.615 + 1.616 + // get the new position and make sure it is in bounds 1.617 + int32_t newpos = curpos + change * pageIncrement; 1.618 + if (newpos < minpos || maxpos < minpos) 1.619 + newpos = minpos; 1.620 + else if (newpos > maxpos) 1.621 + newpos = maxpos; 1.622 + 1.623 + SetCurrentPositionInternal(scrollbar, newpos, true); 1.624 +} 1.625 + 1.626 +// called when the current position changed and we need to update the thumb's location 1.627 +void 1.628 +nsSliderFrame::CurrentPositionChanged() 1.629 +{ 1.630 + nsIFrame* scrollbarBox = GetScrollbar(); 1.631 + nsCOMPtr<nsIContent> scrollbar; 1.632 + scrollbar = GetContentOfBox(scrollbarBox); 1.633 + 1.634 + // get the current position 1.635 + int32_t curPos = GetCurrentPosition(scrollbar); 1.636 + 1.637 + // do nothing if the position did not change 1.638 + if (mCurPos == curPos) 1.639 + return; 1.640 + 1.641 + // get our current min and max position from our content node 1.642 + int32_t minPos = GetMinPosition(scrollbar); 1.643 + int32_t maxPos = GetMaxPosition(scrollbar); 1.644 + 1.645 + maxPos = std::max(minPos, maxPos); 1.646 + curPos = clamped(curPos, minPos, maxPos); 1.647 + 1.648 + // get the thumb's rect 1.649 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.650 + if (!thumbFrame) 1.651 + return; // The thumb may stream in asynchronously via XBL. 1.652 + 1.653 + nsRect thumbRect = thumbFrame->GetRect(); 1.654 + 1.655 + nsRect clientRect; 1.656 + GetClientRect(clientRect); 1.657 + 1.658 + // figure out the new rect 1.659 + nsRect newThumbRect(thumbRect); 1.660 + 1.661 + bool reverse = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, 1.662 + nsGkAtoms::reverse, eCaseMatters); 1.663 + nscoord pos = reverse ? (maxPos - curPos) : (curPos - minPos); 1.664 + 1.665 + if (IsHorizontal()) 1.666 + newThumbRect.x = clientRect.x + NSToCoordRound(pos * mRatio); 1.667 + else 1.668 + newThumbRect.y = clientRect.y + NSToCoordRound(pos * mRatio); 1.669 + 1.670 +#ifdef MOZ_WIDGET_GONK 1.671 + // avoid putting the scroll thumb at subpixel positions which cause needless invalidations 1.672 + nscoord appUnitsPerPixel = PresContext()->AppUnitsPerDevPixel(); 1.673 + newThumbRect = newThumbRect.ToNearestPixels(appUnitsPerPixel).ToAppUnits(appUnitsPerPixel); 1.674 +#endif 1.675 + // set the rect 1.676 + thumbFrame->SetRect(newThumbRect); 1.677 + 1.678 + // Request a repaint of the scrollbar 1.679 + SchedulePaint(); 1.680 + 1.681 + mCurPos = curPos; 1.682 + 1.683 + // inform the parent <scale> if it exists that the value changed 1.684 + nsIFrame* parent = GetParent(); 1.685 + if (parent) { 1.686 + nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent()); 1.687 + if (sliderListener) { 1.688 + nsContentUtils::AddScriptRunner( 1.689 + new nsValueChangedRunnable(sliderListener, nsGkAtoms::curpos, mCurPos, mUserChanged)); 1.690 + } 1.691 + } 1.692 +} 1.693 + 1.694 +static void UpdateAttribute(nsIContent* aScrollbar, nscoord aNewPos, bool aNotify, bool aIsSmooth) { 1.695 + nsAutoString str; 1.696 + str.AppendInt(aNewPos); 1.697 + 1.698 + if (aIsSmooth) { 1.699 + aScrollbar->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, NS_LITERAL_STRING("true"), false); 1.700 + } 1.701 + aScrollbar->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, str, aNotify); 1.702 + if (aIsSmooth) { 1.703 + aScrollbar->UnsetAttr(kNameSpaceID_None, nsGkAtoms::smooth, false); 1.704 + } 1.705 +} 1.706 + 1.707 +// Use this function when you want to set the scroll position via the position 1.708 +// of the scrollbar thumb, e.g. when dragging the slider. This function scrolls 1.709 +// the content in such a way that thumbRect.x/.y becomes aNewThumbPos. 1.710 +void 1.711 +nsSliderFrame::SetCurrentThumbPosition(nsIContent* aScrollbar, nscoord aNewThumbPos, 1.712 + bool aIsSmooth, bool aMaySnap) 1.713 +{ 1.714 + nsRect crect; 1.715 + GetClientRect(crect); 1.716 + nscoord offset = IsHorizontal() ? crect.x : crect.y; 1.717 + int32_t newPos = NSToIntRound((aNewThumbPos - offset) / mRatio); 1.718 + 1.719 + if (aMaySnap && mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::snap, 1.720 + nsGkAtoms::_true, eCaseMatters)) { 1.721 + // If snap="true", then the slider may only be set to min + (increment * x). 1.722 + // Otherwise, the slider may be set to any positive integer. 1.723 + int32_t increment = GetIncrement(aScrollbar); 1.724 + newPos = NSToIntRound(newPos / float(increment)) * increment; 1.725 + } 1.726 + 1.727 + SetCurrentPosition(aScrollbar, newPos, aIsSmooth); 1.728 +} 1.729 + 1.730 +// Use this function when you know the target scroll position of the scrolled content. 1.731 +// aNewPos should be passed to this function as a position as if the minpos is 0. 1.732 +// That is, the minpos will be added to the position by this function. In a reverse 1.733 +// direction slider, the newpos should be the distance from the end. 1.734 +void 1.735 +nsSliderFrame::SetCurrentPosition(nsIContent* aScrollbar, int32_t aNewPos, 1.736 + bool aIsSmooth) 1.737 +{ 1.738 + // get min and max position from our content node 1.739 + int32_t minpos = GetMinPosition(aScrollbar); 1.740 + int32_t maxpos = GetMaxPosition(aScrollbar); 1.741 + 1.742 + // in reverse direction sliders, flip the value so that it goes from 1.743 + // right to left, or bottom to top. 1.744 + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, 1.745 + nsGkAtoms::reverse, eCaseMatters)) 1.746 + aNewPos = maxpos - aNewPos; 1.747 + else 1.748 + aNewPos += minpos; 1.749 + 1.750 + // get the new position and make sure it is in bounds 1.751 + if (aNewPos < minpos || maxpos < minpos) 1.752 + aNewPos = minpos; 1.753 + else if (aNewPos > maxpos) 1.754 + aNewPos = maxpos; 1.755 + 1.756 + SetCurrentPositionInternal(aScrollbar, aNewPos, aIsSmooth); 1.757 +} 1.758 + 1.759 +void 1.760 +nsSliderFrame::SetCurrentPositionInternal(nsIContent* aScrollbar, int32_t aNewPos, 1.761 + bool aIsSmooth) 1.762 +{ 1.763 + nsCOMPtr<nsIContent> scrollbar = aScrollbar; 1.764 + nsIFrame* scrollbarBox = GetScrollbar(); 1.765 + 1.766 + mUserChanged = true; 1.767 + 1.768 + nsScrollbarFrame* scrollbarFrame = do_QueryFrame(scrollbarBox); 1.769 + if (scrollbarFrame) { 1.770 + // See if we have a mediator. 1.771 + nsIScrollbarMediator* mediator = scrollbarFrame->GetScrollbarMediator(); 1.772 + if (mediator) { 1.773 + nsRefPtr<nsPresContext> context = PresContext(); 1.774 + nsCOMPtr<nsIContent> content = GetContent(); 1.775 + mediator->PositionChanged(scrollbarFrame, GetCurrentPosition(scrollbar), aNewPos); 1.776 + // 'mediator' might be dangling now... 1.777 + UpdateAttribute(scrollbar, aNewPos, false, aIsSmooth); 1.778 + nsIFrame* frame = content->GetPrimaryFrame(); 1.779 + if (frame && frame->GetType() == nsGkAtoms::sliderFrame) { 1.780 + static_cast<nsSliderFrame*>(frame)->CurrentPositionChanged(); 1.781 + } 1.782 + mUserChanged = false; 1.783 + return; 1.784 + } 1.785 + } 1.786 + 1.787 + UpdateAttribute(scrollbar, aNewPos, true, aIsSmooth); 1.788 + mUserChanged = false; 1.789 + 1.790 +#ifdef DEBUG_SLIDER 1.791 + printf("Current Pos=%d\n",aNewPos); 1.792 +#endif 1.793 + 1.794 +} 1.795 + 1.796 +nsIAtom* 1.797 +nsSliderFrame::GetType() const 1.798 +{ 1.799 + return nsGkAtoms::sliderFrame; 1.800 +} 1.801 + 1.802 +nsresult 1.803 +nsSliderFrame::SetInitialChildList(ChildListID aListID, 1.804 + nsFrameList& aChildList) 1.805 +{ 1.806 + nsresult r = nsBoxFrame::SetInitialChildList(aListID, aChildList); 1.807 + 1.808 + AddListener(); 1.809 + 1.810 + return r; 1.811 +} 1.812 + 1.813 +nsresult 1.814 +nsSliderMediator::HandleEvent(nsIDOMEvent* aEvent) 1.815 +{ 1.816 + // Only process the event if the thumb is not being dragged. 1.817 + if (mSlider && !mSlider->isDraggingThumb()) 1.818 + return mSlider->StartDrag(aEvent); 1.819 + 1.820 + return NS_OK; 1.821 +} 1.822 + 1.823 +nsresult 1.824 +nsSliderFrame::StartDrag(nsIDOMEvent* aEvent) 1.825 +{ 1.826 +#ifdef DEBUG_SLIDER 1.827 + printf("Begin dragging\n"); 1.828 +#endif 1.829 + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, 1.830 + nsGkAtoms::_true, eCaseMatters)) 1.831 + return NS_OK; 1.832 + 1.833 + WidgetGUIEvent* event = aEvent->GetInternalNSEvent()->AsGUIEvent(); 1.834 + 1.835 + if (!ShouldScrollForEvent(event)) { 1.836 + return NS_OK; 1.837 + } 1.838 + 1.839 + nsPoint pt; 1.840 + if (!GetEventPoint(event, pt)) { 1.841 + return NS_OK; 1.842 + } 1.843 + bool isHorizontal = IsHorizontal(); 1.844 + nscoord pos = isHorizontal ? pt.x : pt.y; 1.845 + 1.846 + // If we should scroll-to-click, first place the middle of the slider thumb 1.847 + // under the mouse. 1.848 + nsCOMPtr<nsIContent> scrollbar; 1.849 + nscoord newpos = pos; 1.850 + bool scrollToClick = ShouldScrollToClickForEvent(event); 1.851 + if (scrollToClick) { 1.852 + // adjust so that the middle of the thumb is placed under the click 1.853 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.854 + if (!thumbFrame) { 1.855 + return NS_OK; 1.856 + } 1.857 + nsSize thumbSize = thumbFrame->GetSize(); 1.858 + nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height; 1.859 + 1.860 + newpos -= (thumbLength/2); 1.861 + 1.862 + nsIFrame* scrollbarBox = GetScrollbar(); 1.863 + scrollbar = GetContentOfBox(scrollbarBox); 1.864 + } 1.865 + 1.866 + DragThumb(true); 1.867 + 1.868 + if (scrollToClick) { 1.869 + // should aMaySnap be true here? 1.870 + SetCurrentThumbPosition(scrollbar, newpos, false, false); 1.871 + } 1.872 + 1.873 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.874 + if (!thumbFrame) { 1.875 + return NS_OK; 1.876 + } 1.877 + 1.878 + if (isHorizontal) 1.879 + mThumbStart = thumbFrame->GetPosition().x; 1.880 + else 1.881 + mThumbStart = thumbFrame->GetPosition().y; 1.882 + 1.883 + mDragStart = pos - mThumbStart; 1.884 + 1.885 +#ifdef DEBUG_SLIDER 1.886 + printf("Pressed mDragStart=%d\n",mDragStart); 1.887 +#endif 1.888 + 1.889 + return NS_OK; 1.890 +} 1.891 + 1.892 +void 1.893 +nsSliderFrame::DragThumb(bool aGrabMouseEvents) 1.894 +{ 1.895 + // inform the parent <scale> that a drag is beginning or ending 1.896 + nsIFrame* parent = GetParent(); 1.897 + if (parent) { 1.898 + nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent()); 1.899 + if (sliderListener) { 1.900 + nsContentUtils::AddScriptRunner( 1.901 + new nsDragStateChangedRunnable(sliderListener, aGrabMouseEvents)); 1.902 + } 1.903 + } 1.904 + 1.905 + nsIPresShell::SetCapturingContent(aGrabMouseEvents ? GetContent() : nullptr, 1.906 + aGrabMouseEvents ? CAPTURE_IGNOREALLOWED : 0); 1.907 +} 1.908 + 1.909 +bool 1.910 +nsSliderFrame::isDraggingThumb() 1.911 +{ 1.912 + return (nsIPresShell::GetCapturingContent() == GetContent()); 1.913 +} 1.914 + 1.915 +void 1.916 +nsSliderFrame::AddListener() 1.917 +{ 1.918 + if (!mMediator) { 1.919 + mMediator = new nsSliderMediator(this); 1.920 + } 1.921 + 1.922 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.923 + if (!thumbFrame) { 1.924 + return; 1.925 + } 1.926 + thumbFrame->GetContent()-> 1.927 + AddSystemEventListener(NS_LITERAL_STRING("mousedown"), mMediator, 1.928 + false, false); 1.929 + thumbFrame->GetContent()-> 1.930 + AddSystemEventListener(NS_LITERAL_STRING("touchstart"), mMediator, 1.931 + false, false); 1.932 +} 1.933 + 1.934 +void 1.935 +nsSliderFrame::RemoveListener() 1.936 +{ 1.937 + NS_ASSERTION(mMediator, "No listener was ever added!!"); 1.938 + 1.939 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.940 + if (!thumbFrame) 1.941 + return; 1.942 + 1.943 + thumbFrame->GetContent()-> 1.944 + RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMediator, false); 1.945 +} 1.946 + 1.947 +bool 1.948 +nsSliderFrame::ShouldScrollForEvent(WidgetGUIEvent* aEvent) 1.949 +{ 1.950 + switch (aEvent->message) { 1.951 + case NS_TOUCH_START: 1.952 + case NS_TOUCH_END: 1.953 + return true; 1.954 + case NS_MOUSE_BUTTON_DOWN: 1.955 + case NS_MOUSE_BUTTON_UP: { 1.956 + uint16_t button = aEvent->AsMouseEvent()->button; 1.957 + return (button == WidgetMouseEvent::eLeftButton) || 1.958 + (button == WidgetMouseEvent::eMiddleButton && gMiddlePref); 1.959 + } 1.960 + default: 1.961 + return false; 1.962 + } 1.963 +} 1.964 + 1.965 +bool 1.966 +nsSliderFrame::ShouldScrollToClickForEvent(WidgetGUIEvent* aEvent) 1.967 +{ 1.968 + if (!ShouldScrollForEvent(aEvent)) { 1.969 + return false; 1.970 + } 1.971 + 1.972 + if (aEvent->message == NS_TOUCH_START) { 1.973 + return GetScrollToClick(); 1.974 + } 1.975 + 1.976 + if (aEvent->message != NS_MOUSE_BUTTON_DOWN) { 1.977 + return false; 1.978 + } 1.979 + 1.980 +#ifdef XP_MACOSX 1.981 + // On Mac, clicking the scrollbar thumb should never scroll to click. 1.982 + if (IsEventOverThumb(aEvent)) { 1.983 + return false; 1.984 + } 1.985 +#endif 1.986 + 1.987 + WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); 1.988 + if (mouseEvent->button == WidgetMouseEvent::eLeftButton) { 1.989 +#ifdef XP_MACOSX 1.990 + bool invertPref = mouseEvent->IsAlt(); 1.991 +#else 1.992 + bool invertPref = mouseEvent->IsShift(); 1.993 +#endif 1.994 + return GetScrollToClick() != invertPref; 1.995 + } 1.996 + 1.997 + return true; 1.998 +} 1.999 + 1.1000 +bool 1.1001 +nsSliderFrame::IsEventOverThumb(WidgetGUIEvent* aEvent) 1.1002 +{ 1.1003 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.1004 + if (!thumbFrame) { 1.1005 + return false; 1.1006 + } 1.1007 + 1.1008 + nsPoint eventPoint; 1.1009 + if (!GetEventPoint(aEvent, eventPoint)) { 1.1010 + return false; 1.1011 + } 1.1012 + 1.1013 + bool isHorizontal = IsHorizontal(); 1.1014 + nsRect thumbRect = thumbFrame->GetRect(); 1.1015 + nscoord eventPos = isHorizontal ? eventPoint.x : eventPoint.y; 1.1016 + nscoord thumbStart = isHorizontal ? thumbRect.x : thumbRect.y; 1.1017 + nscoord thumbEnd = isHorizontal ? thumbRect.XMost() : thumbRect.YMost(); 1.1018 + 1.1019 + return eventPos >= thumbStart && eventPos < thumbEnd; 1.1020 +} 1.1021 + 1.1022 +NS_IMETHODIMP 1.1023 +nsSliderFrame::HandlePress(nsPresContext* aPresContext, 1.1024 + WidgetGUIEvent* aEvent, 1.1025 + nsEventStatus* aEventStatus) 1.1026 +{ 1.1027 + if (!ShouldScrollForEvent(aEvent) || ShouldScrollToClickForEvent(aEvent)) { 1.1028 + return NS_OK; 1.1029 + } 1.1030 + 1.1031 + if (IsEventOverThumb(aEvent)) { 1.1032 + return NS_OK; 1.1033 + } 1.1034 + 1.1035 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.1036 + if (!thumbFrame) // display:none? 1.1037 + return NS_OK; 1.1038 + 1.1039 + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, 1.1040 + nsGkAtoms::_true, eCaseMatters)) 1.1041 + return NS_OK; 1.1042 + 1.1043 + nsRect thumbRect = thumbFrame->GetRect(); 1.1044 + 1.1045 + nscoord change = 1; 1.1046 + nsPoint eventPoint; 1.1047 + if (!GetEventPoint(aEvent, eventPoint)) { 1.1048 + return NS_OK; 1.1049 + } 1.1050 + if (IsHorizontal() ? eventPoint.x < thumbRect.x 1.1051 + : eventPoint.y < thumbRect.y) 1.1052 + change = -1; 1.1053 + 1.1054 + mChange = change; 1.1055 + DragThumb(true); 1.1056 + mDestinationPoint = eventPoint; 1.1057 + StartRepeat(); 1.1058 + PageUpDown(change); 1.1059 + return NS_OK; 1.1060 +} 1.1061 + 1.1062 +NS_IMETHODIMP 1.1063 +nsSliderFrame::HandleRelease(nsPresContext* aPresContext, 1.1064 + WidgetGUIEvent* aEvent, 1.1065 + nsEventStatus* aEventStatus) 1.1066 +{ 1.1067 + StopRepeat(); 1.1068 + 1.1069 + return NS_OK; 1.1070 +} 1.1071 + 1.1072 +void 1.1073 +nsSliderFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.1074 +{ 1.1075 + // tell our mediator if we have one we are gone. 1.1076 + if (mMediator) { 1.1077 + mMediator->SetSlider(nullptr); 1.1078 + mMediator = nullptr; 1.1079 + } 1.1080 + StopRepeat(); 1.1081 + 1.1082 + // call base class Destroy() 1.1083 + nsBoxFrame::DestroyFrom(aDestructRoot); 1.1084 +} 1.1085 + 1.1086 +nsSize 1.1087 +nsSliderFrame::GetPrefSize(nsBoxLayoutState& aState) 1.1088 +{ 1.1089 + EnsureOrient(); 1.1090 + return nsBoxFrame::GetPrefSize(aState); 1.1091 +} 1.1092 + 1.1093 +nsSize 1.1094 +nsSliderFrame::GetMinSize(nsBoxLayoutState& aState) 1.1095 +{ 1.1096 + EnsureOrient(); 1.1097 + 1.1098 + // our min size is just our borders and padding 1.1099 + return nsBox::GetMinSize(aState); 1.1100 +} 1.1101 + 1.1102 +nsSize 1.1103 +nsSliderFrame::GetMaxSize(nsBoxLayoutState& aState) 1.1104 +{ 1.1105 + EnsureOrient(); 1.1106 + return nsBoxFrame::GetMaxSize(aState); 1.1107 +} 1.1108 + 1.1109 +void 1.1110 +nsSliderFrame::EnsureOrient() 1.1111 +{ 1.1112 + nsIFrame* scrollbarBox = GetScrollbar(); 1.1113 + 1.1114 + bool isHorizontal = (scrollbarBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0; 1.1115 + if (isHorizontal) 1.1116 + mState |= NS_STATE_IS_HORIZONTAL; 1.1117 + else 1.1118 + mState &= ~NS_STATE_IS_HORIZONTAL; 1.1119 +} 1.1120 + 1.1121 + 1.1122 +void nsSliderFrame::Notify(void) 1.1123 +{ 1.1124 + bool stop = false; 1.1125 + 1.1126 + nsIFrame* thumbFrame = mFrames.FirstChild(); 1.1127 + if (!thumbFrame) { 1.1128 + StopRepeat(); 1.1129 + return; 1.1130 + } 1.1131 + nsRect thumbRect = thumbFrame->GetRect(); 1.1132 + 1.1133 + bool isHorizontal = IsHorizontal(); 1.1134 + 1.1135 + // See if the thumb has moved past our destination point. 1.1136 + // if it has we want to stop. 1.1137 + if (isHorizontal) { 1.1138 + if (mChange < 0) { 1.1139 + if (thumbRect.x < mDestinationPoint.x) 1.1140 + stop = true; 1.1141 + } else { 1.1142 + if (thumbRect.x + thumbRect.width > mDestinationPoint.x) 1.1143 + stop = true; 1.1144 + } 1.1145 + } else { 1.1146 + if (mChange < 0) { 1.1147 + if (thumbRect.y < mDestinationPoint.y) 1.1148 + stop = true; 1.1149 + } else { 1.1150 + if (thumbRect.y + thumbRect.height > mDestinationPoint.y) 1.1151 + stop = true; 1.1152 + } 1.1153 + } 1.1154 + 1.1155 + 1.1156 + if (stop) { 1.1157 + StopRepeat(); 1.1158 + } else { 1.1159 + PageUpDown(mChange); 1.1160 + } 1.1161 +} 1.1162 + 1.1163 +NS_IMPL_ISUPPORTS(nsSliderMediator, 1.1164 + nsIDOMEventListener)