layout/xul/nsSliderFrame.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 //
michael@0 7 // Eric Vaughan
michael@0 8 // Netscape Communications
michael@0 9 //
michael@0 10 // See documentation in associated header file
michael@0 11 //
michael@0 12
michael@0 13 #include "nsSliderFrame.h"
michael@0 14 #include "nsStyleContext.h"
michael@0 15 #include "nsPresContext.h"
michael@0 16 #include "nsIContent.h"
michael@0 17 #include "nsCOMPtr.h"
michael@0 18 #include "nsNameSpaceManager.h"
michael@0 19 #include "nsGkAtoms.h"
michael@0 20 #include "nsHTMLParts.h"
michael@0 21 #include "nsIPresShell.h"
michael@0 22 #include "nsCSSRendering.h"
michael@0 23 #include "nsIDOMMouseEvent.h"
michael@0 24 #include "nsScrollbarButtonFrame.h"
michael@0 25 #include "nsISliderListener.h"
michael@0 26 #include "nsIScrollbarMediator.h"
michael@0 27 #include "nsScrollbarFrame.h"
michael@0 28 #include "nsRepeatService.h"
michael@0 29 #include "nsBoxLayoutState.h"
michael@0 30 #include "nsSprocketLayout.h"
michael@0 31 #include "nsIServiceManager.h"
michael@0 32 #include "nsContentUtils.h"
michael@0 33 #include "nsLayoutUtils.h"
michael@0 34 #include "nsDisplayList.h"
michael@0 35 #include "mozilla/Preferences.h"
michael@0 36 #include "mozilla/LookAndFeel.h"
michael@0 37 #include "mozilla/MouseEvents.h"
michael@0 38 #include <algorithm>
michael@0 39
michael@0 40 using namespace mozilla;
michael@0 41
michael@0 42 bool nsSliderFrame::gMiddlePref = false;
michael@0 43 int32_t nsSliderFrame::gSnapMultiplier;
michael@0 44
michael@0 45 // Turn this on if you want to debug slider frames.
michael@0 46 #undef DEBUG_SLIDER
michael@0 47
michael@0 48 static already_AddRefed<nsIContent>
michael@0 49 GetContentOfBox(nsIFrame *aBox)
michael@0 50 {
michael@0 51 nsCOMPtr<nsIContent> content = aBox->GetContent();
michael@0 52 return content.forget();
michael@0 53 }
michael@0 54
michael@0 55 nsIFrame*
michael@0 56 NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 57 {
michael@0 58 return new (aPresShell) nsSliderFrame(aPresShell, aContext);
michael@0 59 }
michael@0 60
michael@0 61 NS_IMPL_FRAMEARENA_HELPERS(nsSliderFrame)
michael@0 62
michael@0 63 nsSliderFrame::nsSliderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext):
michael@0 64 nsBoxFrame(aPresShell, aContext),
michael@0 65 mCurPos(0),
michael@0 66 mChange(0),
michael@0 67 mUserChanged(false)
michael@0 68 {
michael@0 69 }
michael@0 70
michael@0 71 // stop timer
michael@0 72 nsSliderFrame::~nsSliderFrame()
michael@0 73 {
michael@0 74 }
michael@0 75
michael@0 76 void
michael@0 77 nsSliderFrame::Init(nsIContent* aContent,
michael@0 78 nsIFrame* aParent,
michael@0 79 nsIFrame* aPrevInFlow)
michael@0 80 {
michael@0 81 nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
michael@0 82
michael@0 83 static bool gotPrefs = false;
michael@0 84 if (!gotPrefs) {
michael@0 85 gotPrefs = true;
michael@0 86
michael@0 87 gMiddlePref = Preferences::GetBool("middlemouse.scrollbarPosition");
michael@0 88 gSnapMultiplier = Preferences::GetInt("slider.snapMultiplier");
michael@0 89 }
michael@0 90
michael@0 91 mCurPos = GetCurrentPosition(aContent);
michael@0 92 }
michael@0 93
michael@0 94 nsresult
michael@0 95 nsSliderFrame::RemoveFrame(ChildListID aListID,
michael@0 96 nsIFrame* aOldFrame)
michael@0 97 {
michael@0 98 nsresult rv = nsBoxFrame::RemoveFrame(aListID, aOldFrame);
michael@0 99 if (mFrames.IsEmpty())
michael@0 100 RemoveListener();
michael@0 101
michael@0 102 return rv;
michael@0 103 }
michael@0 104
michael@0 105 nsresult
michael@0 106 nsSliderFrame::InsertFrames(ChildListID aListID,
michael@0 107 nsIFrame* aPrevFrame,
michael@0 108 nsFrameList& aFrameList)
michael@0 109 {
michael@0 110 bool wasEmpty = mFrames.IsEmpty();
michael@0 111 nsresult rv = nsBoxFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
michael@0 112 if (wasEmpty)
michael@0 113 AddListener();
michael@0 114
michael@0 115 return rv;
michael@0 116 }
michael@0 117
michael@0 118 nsresult
michael@0 119 nsSliderFrame::AppendFrames(ChildListID aListID,
michael@0 120 nsFrameList& aFrameList)
michael@0 121 {
michael@0 122 // if we have no children and on was added then make sure we add the
michael@0 123 // listener
michael@0 124 bool wasEmpty = mFrames.IsEmpty();
michael@0 125 nsresult rv = nsBoxFrame::AppendFrames(aListID, aFrameList);
michael@0 126 if (wasEmpty)
michael@0 127 AddListener();
michael@0 128
michael@0 129 return rv;
michael@0 130 }
michael@0 131
michael@0 132 int32_t
michael@0 133 nsSliderFrame::GetCurrentPosition(nsIContent* content)
michael@0 134 {
michael@0 135 return GetIntegerAttribute(content, nsGkAtoms::curpos, 0);
michael@0 136 }
michael@0 137
michael@0 138 int32_t
michael@0 139 nsSliderFrame::GetMinPosition(nsIContent* content)
michael@0 140 {
michael@0 141 return GetIntegerAttribute(content, nsGkAtoms::minpos, 0);
michael@0 142 }
michael@0 143
michael@0 144 int32_t
michael@0 145 nsSliderFrame::GetMaxPosition(nsIContent* content)
michael@0 146 {
michael@0 147 return GetIntegerAttribute(content, nsGkAtoms::maxpos, 100);
michael@0 148 }
michael@0 149
michael@0 150 int32_t
michael@0 151 nsSliderFrame::GetIncrement(nsIContent* content)
michael@0 152 {
michael@0 153 return GetIntegerAttribute(content, nsGkAtoms::increment, 1);
michael@0 154 }
michael@0 155
michael@0 156
michael@0 157 int32_t
michael@0 158 nsSliderFrame::GetPageIncrement(nsIContent* content)
michael@0 159 {
michael@0 160 return GetIntegerAttribute(content, nsGkAtoms::pageincrement, 10);
michael@0 161 }
michael@0 162
michael@0 163 int32_t
michael@0 164 nsSliderFrame::GetIntegerAttribute(nsIContent* content, nsIAtom* atom, int32_t defaultValue)
michael@0 165 {
michael@0 166 nsAutoString value;
michael@0 167 content->GetAttr(kNameSpaceID_None, atom, value);
michael@0 168 if (!value.IsEmpty()) {
michael@0 169 nsresult error;
michael@0 170
michael@0 171 // convert it to an integer
michael@0 172 defaultValue = value.ToInteger(&error);
michael@0 173 }
michael@0 174
michael@0 175 return defaultValue;
michael@0 176 }
michael@0 177
michael@0 178 class nsValueChangedRunnable : public nsRunnable
michael@0 179 {
michael@0 180 public:
michael@0 181 nsValueChangedRunnable(nsISliderListener* aListener,
michael@0 182 nsIAtom* aWhich,
michael@0 183 int32_t aValue,
michael@0 184 bool aUserChanged)
michael@0 185 : mListener(aListener), mWhich(aWhich),
michael@0 186 mValue(aValue), mUserChanged(aUserChanged)
michael@0 187 {}
michael@0 188
michael@0 189 NS_IMETHODIMP Run()
michael@0 190 {
michael@0 191 return mListener->ValueChanged(nsDependentAtomString(mWhich),
michael@0 192 mValue, mUserChanged);
michael@0 193 }
michael@0 194
michael@0 195 nsCOMPtr<nsISliderListener> mListener;
michael@0 196 nsCOMPtr<nsIAtom> mWhich;
michael@0 197 int32_t mValue;
michael@0 198 bool mUserChanged;
michael@0 199 };
michael@0 200
michael@0 201 class nsDragStateChangedRunnable : public nsRunnable
michael@0 202 {
michael@0 203 public:
michael@0 204 nsDragStateChangedRunnable(nsISliderListener* aListener,
michael@0 205 bool aDragBeginning)
michael@0 206 : mListener(aListener),
michael@0 207 mDragBeginning(aDragBeginning)
michael@0 208 {}
michael@0 209
michael@0 210 NS_IMETHODIMP Run()
michael@0 211 {
michael@0 212 return mListener->DragStateChanged(mDragBeginning);
michael@0 213 }
michael@0 214
michael@0 215 nsCOMPtr<nsISliderListener> mListener;
michael@0 216 bool mDragBeginning;
michael@0 217 };
michael@0 218
michael@0 219 nsresult
michael@0 220 nsSliderFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 221 nsIAtom* aAttribute,
michael@0 222 int32_t aModType)
michael@0 223 {
michael@0 224 nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
michael@0 225 aModType);
michael@0 226 // if the current position changes
michael@0 227 if (aAttribute == nsGkAtoms::curpos) {
michael@0 228 CurrentPositionChanged();
michael@0 229 } else if (aAttribute == nsGkAtoms::minpos ||
michael@0 230 aAttribute == nsGkAtoms::maxpos) {
michael@0 231 // bounds check it.
michael@0 232
michael@0 233 nsIFrame* scrollbarBox = GetScrollbar();
michael@0 234 nsCOMPtr<nsIContent> scrollbar;
michael@0 235 scrollbar = GetContentOfBox(scrollbarBox);
michael@0 236 int32_t current = GetCurrentPosition(scrollbar);
michael@0 237 int32_t min = GetMinPosition(scrollbar);
michael@0 238 int32_t max = GetMaxPosition(scrollbar);
michael@0 239
michael@0 240 // inform the parent <scale> that the minimum or maximum changed
michael@0 241 nsIFrame* parent = GetParent();
michael@0 242 if (parent) {
michael@0 243 nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent());
michael@0 244 if (sliderListener) {
michael@0 245 nsContentUtils::AddScriptRunner(
michael@0 246 new nsValueChangedRunnable(sliderListener, aAttribute,
michael@0 247 aAttribute == nsGkAtoms::minpos ? min : max, false));
michael@0 248 }
michael@0 249 }
michael@0 250
michael@0 251 if (current < min || current > max)
michael@0 252 {
michael@0 253 if (current < min || max < min)
michael@0 254 current = min;
michael@0 255 else if (current > max)
michael@0 256 current = max;
michael@0 257
michael@0 258 // set the new position and notify observers
michael@0 259 nsScrollbarFrame* scrollbarFrame = do_QueryFrame(scrollbarBox);
michael@0 260 if (scrollbarFrame) {
michael@0 261 nsIScrollbarMediator* mediator = scrollbarFrame->GetScrollbarMediator();
michael@0 262 if (mediator) {
michael@0 263 mediator->PositionChanged(scrollbarFrame, GetCurrentPosition(scrollbar), current);
michael@0 264 }
michael@0 265 }
michael@0 266
michael@0 267 nsContentUtils::AddScriptRunner(
michael@0 268 new nsSetAttrRunnable(scrollbar, nsGkAtoms::curpos, current));
michael@0 269 }
michael@0 270 }
michael@0 271
michael@0 272 if (aAttribute == nsGkAtoms::minpos ||
michael@0 273 aAttribute == nsGkAtoms::maxpos ||
michael@0 274 aAttribute == nsGkAtoms::pageincrement ||
michael@0 275 aAttribute == nsGkAtoms::increment) {
michael@0 276
michael@0 277 PresContext()->PresShell()->
michael@0 278 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 279 }
michael@0 280
michael@0 281 return rv;
michael@0 282 }
michael@0 283
michael@0 284 void
michael@0 285 nsSliderFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 286 const nsRect& aDirtyRect,
michael@0 287 const nsDisplayListSet& aLists)
michael@0 288 {
michael@0 289 if (aBuilder->IsForEventDelivery() && isDraggingThumb()) {
michael@0 290 // This is EVIL, we shouldn't be messing with event delivery just to get
michael@0 291 // thumb mouse drag events to arrive at the slider!
michael@0 292 aLists.Outlines()->AppendNewToTop(new (aBuilder)
michael@0 293 nsDisplayEventReceiver(aBuilder, this));
michael@0 294 return;
michael@0 295 }
michael@0 296
michael@0 297 nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
michael@0 298 }
michael@0 299
michael@0 300 void
michael@0 301 nsSliderFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
michael@0 302 const nsRect& aDirtyRect,
michael@0 303 const nsDisplayListSet& aLists)
michael@0 304 {
michael@0 305 // if we are too small to have a thumb don't paint it.
michael@0 306 nsIFrame* thumb = GetChildBox();
michael@0 307
michael@0 308 if (thumb) {
michael@0 309 nsRect thumbRect(thumb->GetRect());
michael@0 310 nsMargin m;
michael@0 311 thumb->GetMargin(m);
michael@0 312 thumbRect.Inflate(m);
michael@0 313
michael@0 314 nsRect crect;
michael@0 315 GetClientRect(crect);
michael@0 316
michael@0 317 if (crect.width < thumbRect.width || crect.height < thumbRect.height)
michael@0 318 return;
michael@0 319 }
michael@0 320
michael@0 321 nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, aLists);
michael@0 322 }
michael@0 323
michael@0 324 NS_IMETHODIMP
michael@0 325 nsSliderFrame::DoLayout(nsBoxLayoutState& aState)
michael@0 326 {
michael@0 327 // get the thumb should be our only child
michael@0 328 nsIFrame* thumbBox = GetChildBox();
michael@0 329
michael@0 330 if (!thumbBox) {
michael@0 331 SyncLayout(aState);
michael@0 332 return NS_OK;
michael@0 333 }
michael@0 334
michael@0 335 EnsureOrient();
michael@0 336
michael@0 337 #ifdef DEBUG_LAYOUT
michael@0 338 if (mState & NS_STATE_DEBUG_WAS_SET) {
michael@0 339 if (mState & NS_STATE_SET_TO_DEBUG)
michael@0 340 SetDebug(aState, true);
michael@0 341 else
michael@0 342 SetDebug(aState, false);
michael@0 343 }
michael@0 344 #endif
michael@0 345
michael@0 346 // get the content area inside our borders
michael@0 347 nsRect clientRect;
michael@0 348 GetClientRect(clientRect);
michael@0 349
michael@0 350 // get the scrollbar
michael@0 351 nsIFrame* scrollbarBox = GetScrollbar();
michael@0 352 nsCOMPtr<nsIContent> scrollbar;
michael@0 353 scrollbar = GetContentOfBox(scrollbarBox);
michael@0 354
michael@0 355 // get the thumb's pref size
michael@0 356 nsSize thumbSize = thumbBox->GetPrefSize(aState);
michael@0 357
michael@0 358 if (IsHorizontal())
michael@0 359 thumbSize.height = clientRect.height;
michael@0 360 else
michael@0 361 thumbSize.width = clientRect.width;
michael@0 362
michael@0 363 int32_t curPos = GetCurrentPosition(scrollbar);
michael@0 364 int32_t minPos = GetMinPosition(scrollbar);
michael@0 365 int32_t maxPos = GetMaxPosition(scrollbar);
michael@0 366 int32_t pageIncrement = GetPageIncrement(scrollbar);
michael@0 367
michael@0 368 maxPos = std::max(minPos, maxPos);
michael@0 369 curPos = clamped(curPos, minPos, maxPos);
michael@0 370
michael@0 371 nscoord& availableLength = IsHorizontal() ? clientRect.width : clientRect.height;
michael@0 372 nscoord& thumbLength = IsHorizontal() ? thumbSize.width : thumbSize.height;
michael@0 373
michael@0 374 if ((pageIncrement + maxPos - minPos) > 0 && thumbBox->GetFlex(aState) > 0) {
michael@0 375 float ratio = float(pageIncrement) / float(maxPos - minPos + pageIncrement);
michael@0 376 thumbLength = std::max(thumbLength, NSToCoordRound(availableLength * ratio));
michael@0 377 }
michael@0 378
michael@0 379 // Round the thumb's length to device pixels.
michael@0 380 nsPresContext* presContext = PresContext();
michael@0 381 thumbLength = presContext->DevPixelsToAppUnits(
michael@0 382 presContext->AppUnitsToDevPixels(thumbLength));
michael@0 383
michael@0 384 // mRatio translates the thumb position in app units to the value.
michael@0 385 mRatio = (minPos != maxPos) ? float(availableLength - thumbLength) / float(maxPos - minPos) : 1;
michael@0 386
michael@0 387 // in reverse mode, curpos is reversed such that lower values are to the
michael@0 388 // right or bottom and increase leftwards or upwards. In this case, use the
michael@0 389 // offset from the end instead of the beginning.
michael@0 390 bool reverse = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
michael@0 391 nsGkAtoms::reverse, eCaseMatters);
michael@0 392 nscoord pos = reverse ? (maxPos - curPos) : (curPos - minPos);
michael@0 393
michael@0 394 // set the thumb's coord to be the current pos * the ratio.
michael@0 395 nsRect thumbRect(clientRect.x, clientRect.y, thumbSize.width, thumbSize.height);
michael@0 396 int32_t& thumbPos = (IsHorizontal() ? thumbRect.x : thumbRect.y);
michael@0 397 thumbPos += NSToCoordRound(pos * mRatio);
michael@0 398
michael@0 399 nsRect oldThumbRect(thumbBox->GetRect());
michael@0 400 LayoutChildAt(aState, thumbBox, thumbRect);
michael@0 401
michael@0 402 SyncLayout(aState);
michael@0 403
michael@0 404 // Redraw only if thumb changed size.
michael@0 405 if (!oldThumbRect.IsEqualInterior(thumbRect))
michael@0 406 Redraw(aState);
michael@0 407
michael@0 408 return NS_OK;
michael@0 409 }
michael@0 410
michael@0 411
michael@0 412 nsresult
michael@0 413 nsSliderFrame::HandleEvent(nsPresContext* aPresContext,
michael@0 414 WidgetGUIEvent* aEvent,
michael@0 415 nsEventStatus* aEventStatus)
michael@0 416 {
michael@0 417 NS_ENSURE_ARG_POINTER(aEventStatus);
michael@0 418
michael@0 419 // If a web page calls event.preventDefault() we still want to
michael@0 420 // scroll when scroll arrow is clicked. See bug 511075.
michael@0 421 if (!mContent->IsInNativeAnonymousSubtree() &&
michael@0 422 nsEventStatus_eConsumeNoDefault == *aEventStatus) {
michael@0 423 return NS_OK;
michael@0 424 }
michael@0 425
michael@0 426 nsIFrame* scrollbarBox = GetScrollbar();
michael@0 427 nsCOMPtr<nsIContent> scrollbar;
michael@0 428 scrollbar = GetContentOfBox(scrollbarBox);
michael@0 429 bool isHorizontal = IsHorizontal();
michael@0 430
michael@0 431 if (isDraggingThumb())
michael@0 432 {
michael@0 433 switch (aEvent->message) {
michael@0 434 case NS_TOUCH_MOVE:
michael@0 435 case NS_MOUSE_MOVE: {
michael@0 436 nsPoint eventPoint;
michael@0 437 if (!GetEventPoint(aEvent, eventPoint)) {
michael@0 438 break;
michael@0 439 }
michael@0 440 if (mChange) {
michael@0 441 // We're in the process of moving the thumb to the mouse,
michael@0 442 // but the mouse just moved. Make sure to update our
michael@0 443 // destination point.
michael@0 444 mDestinationPoint = eventPoint;
michael@0 445 StopRepeat();
michael@0 446 StartRepeat();
michael@0 447 break;
michael@0 448 }
michael@0 449
michael@0 450 nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y;
michael@0 451
michael@0 452 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 453 if (!thumbFrame) {
michael@0 454 return NS_OK;
michael@0 455 }
michael@0 456
michael@0 457 // take our current position and subtract the start location
michael@0 458 pos -= mDragStart;
michael@0 459 bool isMouseOutsideThumb = false;
michael@0 460 if (gSnapMultiplier) {
michael@0 461 nsSize thumbSize = thumbFrame->GetSize();
michael@0 462 if (isHorizontal) {
michael@0 463 // horizontal scrollbar - check if mouse is above or below thumb
michael@0 464 // XXXbz what about looking at the .y of the thumb's rect? Is that
michael@0 465 // always zero here?
michael@0 466 if (eventPoint.y < -gSnapMultiplier * thumbSize.height ||
michael@0 467 eventPoint.y > thumbSize.height +
michael@0 468 gSnapMultiplier * thumbSize.height)
michael@0 469 isMouseOutsideThumb = true;
michael@0 470 }
michael@0 471 else {
michael@0 472 // vertical scrollbar - check if mouse is left or right of thumb
michael@0 473 if (eventPoint.x < -gSnapMultiplier * thumbSize.width ||
michael@0 474 eventPoint.x > thumbSize.width +
michael@0 475 gSnapMultiplier * thumbSize.width)
michael@0 476 isMouseOutsideThumb = true;
michael@0 477 }
michael@0 478 }
michael@0 479 if (aEvent->eventStructType == NS_TOUCH_EVENT) {
michael@0 480 *aEventStatus = nsEventStatus_eConsumeNoDefault;
michael@0 481 }
michael@0 482 if (isMouseOutsideThumb)
michael@0 483 {
michael@0 484 SetCurrentThumbPosition(scrollbar, mThumbStart, false, false);
michael@0 485 return NS_OK;
michael@0 486 }
michael@0 487
michael@0 488 // set it
michael@0 489 SetCurrentThumbPosition(scrollbar, pos, false, true); // with snapping
michael@0 490 }
michael@0 491 break;
michael@0 492
michael@0 493 case NS_TOUCH_END:
michael@0 494 case NS_MOUSE_BUTTON_UP:
michael@0 495 if (ShouldScrollForEvent(aEvent)) {
michael@0 496 // stop capturing
michael@0 497 AddListener();
michael@0 498 DragThumb(false);
michael@0 499 if (mChange) {
michael@0 500 StopRepeat();
michael@0 501 mChange = 0;
michael@0 502 }
michael@0 503 //we MUST call nsFrame HandleEvent for mouse ups to maintain the selection state and capture state.
michael@0 504 return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
michael@0 505 }
michael@0 506 }
michael@0 507
michael@0 508 //return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
michael@0 509 return NS_OK;
michael@0 510 } else if (ShouldScrollToClickForEvent(aEvent)) {
michael@0 511 nsPoint eventPoint;
michael@0 512 if (!GetEventPoint(aEvent, eventPoint)) {
michael@0 513 return NS_OK;
michael@0 514 }
michael@0 515 nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y;
michael@0 516
michael@0 517 // adjust so that the middle of the thumb is placed under the click
michael@0 518 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 519 if (!thumbFrame) {
michael@0 520 return NS_OK;
michael@0 521 }
michael@0 522 nsSize thumbSize = thumbFrame->GetSize();
michael@0 523 nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
michael@0 524
michael@0 525 // set it
michael@0 526 nsWeakFrame weakFrame(this);
michael@0 527 // should aMaySnap be true here?
michael@0 528 SetCurrentThumbPosition(scrollbar, pos - thumbLength/2, false, false);
michael@0 529 NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
michael@0 530
michael@0 531 DragThumb(true);
michael@0 532 if (aEvent->eventStructType == NS_TOUCH_EVENT) {
michael@0 533 *aEventStatus = nsEventStatus_eConsumeNoDefault;
michael@0 534 }
michael@0 535
michael@0 536 if (isHorizontal)
michael@0 537 mThumbStart = thumbFrame->GetPosition().x;
michael@0 538 else
michael@0 539 mThumbStart = thumbFrame->GetPosition().y;
michael@0 540
michael@0 541 mDragStart = pos - mThumbStart;
michael@0 542 }
michael@0 543
michael@0 544 // XXX hack until handle release is actually called in nsframe.
michael@0 545 // if (aEvent->message == NS_MOUSE_EXIT_SYNTH || aEvent->message == NS_MOUSE_RIGHT_BUTTON_UP || aEvent->message == NS_MOUSE_LEFT_BUTTON_UP)
michael@0 546 // HandleRelease(aPresContext, aEvent, aEventStatus);
michael@0 547
michael@0 548 if (aEvent->message == NS_MOUSE_EXIT_SYNTH && mChange)
michael@0 549 HandleRelease(aPresContext, aEvent, aEventStatus);
michael@0 550
michael@0 551 return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
michael@0 552 }
michael@0 553
michael@0 554 // Helper function to collect the "scroll to click" metric. Beware of
michael@0 555 // caching this, users expect to be able to change the system preference
michael@0 556 // and see the browser change its behavior immediately.
michael@0 557 bool
michael@0 558 nsSliderFrame::GetScrollToClick()
michael@0 559 {
michael@0 560 if (GetScrollbar() != this) {
michael@0 561 return LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollToClick, false);
michael@0 562 }
michael@0 563
michael@0 564 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
michael@0 565 nsGkAtoms::_true, eCaseMatters)) {
michael@0 566 return true;
michael@0 567 }
michael@0 568 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
michael@0 569 nsGkAtoms::_false, eCaseMatters)) {
michael@0 570 return false;
michael@0 571 }
michael@0 572
michael@0 573 #ifdef XP_MACOSX
michael@0 574 return true;
michael@0 575 #else
michael@0 576 return false;
michael@0 577 #endif
michael@0 578 }
michael@0 579
michael@0 580 nsIFrame*
michael@0 581 nsSliderFrame::GetScrollbar()
michael@0 582 {
michael@0 583 // if we are in a scrollbar then return the scrollbar's content node
michael@0 584 // if we are not then return ours.
michael@0 585 nsIFrame* scrollbar;
michael@0 586 nsScrollbarButtonFrame::GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar);
michael@0 587
michael@0 588 if (scrollbar == nullptr)
michael@0 589 return this;
michael@0 590
michael@0 591 return scrollbar->IsBoxFrame() ? scrollbar : this;
michael@0 592 }
michael@0 593
michael@0 594 void
michael@0 595 nsSliderFrame::PageUpDown(nscoord change)
michael@0 596 {
michael@0 597 // on a page up or down get our page increment. We get this by getting the scrollbar we are in and
michael@0 598 // asking it for the current position and the page increment. If we are not in a scrollbar we will
michael@0 599 // get the values from our own node.
michael@0 600 nsIFrame* scrollbarBox = GetScrollbar();
michael@0 601 nsCOMPtr<nsIContent> scrollbar;
michael@0 602 scrollbar = GetContentOfBox(scrollbarBox);
michael@0 603
michael@0 604 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
michael@0 605 nsGkAtoms::reverse, eCaseMatters))
michael@0 606 change = -change;
michael@0 607
michael@0 608 nscoord pageIncrement = GetPageIncrement(scrollbar);
michael@0 609 int32_t curpos = GetCurrentPosition(scrollbar);
michael@0 610 int32_t minpos = GetMinPosition(scrollbar);
michael@0 611 int32_t maxpos = GetMaxPosition(scrollbar);
michael@0 612
michael@0 613 // get the new position and make sure it is in bounds
michael@0 614 int32_t newpos = curpos + change * pageIncrement;
michael@0 615 if (newpos < minpos || maxpos < minpos)
michael@0 616 newpos = minpos;
michael@0 617 else if (newpos > maxpos)
michael@0 618 newpos = maxpos;
michael@0 619
michael@0 620 SetCurrentPositionInternal(scrollbar, newpos, true);
michael@0 621 }
michael@0 622
michael@0 623 // called when the current position changed and we need to update the thumb's location
michael@0 624 void
michael@0 625 nsSliderFrame::CurrentPositionChanged()
michael@0 626 {
michael@0 627 nsIFrame* scrollbarBox = GetScrollbar();
michael@0 628 nsCOMPtr<nsIContent> scrollbar;
michael@0 629 scrollbar = GetContentOfBox(scrollbarBox);
michael@0 630
michael@0 631 // get the current position
michael@0 632 int32_t curPos = GetCurrentPosition(scrollbar);
michael@0 633
michael@0 634 // do nothing if the position did not change
michael@0 635 if (mCurPos == curPos)
michael@0 636 return;
michael@0 637
michael@0 638 // get our current min and max position from our content node
michael@0 639 int32_t minPos = GetMinPosition(scrollbar);
michael@0 640 int32_t maxPos = GetMaxPosition(scrollbar);
michael@0 641
michael@0 642 maxPos = std::max(minPos, maxPos);
michael@0 643 curPos = clamped(curPos, minPos, maxPos);
michael@0 644
michael@0 645 // get the thumb's rect
michael@0 646 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 647 if (!thumbFrame)
michael@0 648 return; // The thumb may stream in asynchronously via XBL.
michael@0 649
michael@0 650 nsRect thumbRect = thumbFrame->GetRect();
michael@0 651
michael@0 652 nsRect clientRect;
michael@0 653 GetClientRect(clientRect);
michael@0 654
michael@0 655 // figure out the new rect
michael@0 656 nsRect newThumbRect(thumbRect);
michael@0 657
michael@0 658 bool reverse = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
michael@0 659 nsGkAtoms::reverse, eCaseMatters);
michael@0 660 nscoord pos = reverse ? (maxPos - curPos) : (curPos - minPos);
michael@0 661
michael@0 662 if (IsHorizontal())
michael@0 663 newThumbRect.x = clientRect.x + NSToCoordRound(pos * mRatio);
michael@0 664 else
michael@0 665 newThumbRect.y = clientRect.y + NSToCoordRound(pos * mRatio);
michael@0 666
michael@0 667 #ifdef MOZ_WIDGET_GONK
michael@0 668 // avoid putting the scroll thumb at subpixel positions which cause needless invalidations
michael@0 669 nscoord appUnitsPerPixel = PresContext()->AppUnitsPerDevPixel();
michael@0 670 newThumbRect = newThumbRect.ToNearestPixels(appUnitsPerPixel).ToAppUnits(appUnitsPerPixel);
michael@0 671 #endif
michael@0 672 // set the rect
michael@0 673 thumbFrame->SetRect(newThumbRect);
michael@0 674
michael@0 675 // Request a repaint of the scrollbar
michael@0 676 SchedulePaint();
michael@0 677
michael@0 678 mCurPos = curPos;
michael@0 679
michael@0 680 // inform the parent <scale> if it exists that the value changed
michael@0 681 nsIFrame* parent = GetParent();
michael@0 682 if (parent) {
michael@0 683 nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent());
michael@0 684 if (sliderListener) {
michael@0 685 nsContentUtils::AddScriptRunner(
michael@0 686 new nsValueChangedRunnable(sliderListener, nsGkAtoms::curpos, mCurPos, mUserChanged));
michael@0 687 }
michael@0 688 }
michael@0 689 }
michael@0 690
michael@0 691 static void UpdateAttribute(nsIContent* aScrollbar, nscoord aNewPos, bool aNotify, bool aIsSmooth) {
michael@0 692 nsAutoString str;
michael@0 693 str.AppendInt(aNewPos);
michael@0 694
michael@0 695 if (aIsSmooth) {
michael@0 696 aScrollbar->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, NS_LITERAL_STRING("true"), false);
michael@0 697 }
michael@0 698 aScrollbar->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, str, aNotify);
michael@0 699 if (aIsSmooth) {
michael@0 700 aScrollbar->UnsetAttr(kNameSpaceID_None, nsGkAtoms::smooth, false);
michael@0 701 }
michael@0 702 }
michael@0 703
michael@0 704 // Use this function when you want to set the scroll position via the position
michael@0 705 // of the scrollbar thumb, e.g. when dragging the slider. This function scrolls
michael@0 706 // the content in such a way that thumbRect.x/.y becomes aNewThumbPos.
michael@0 707 void
michael@0 708 nsSliderFrame::SetCurrentThumbPosition(nsIContent* aScrollbar, nscoord aNewThumbPos,
michael@0 709 bool aIsSmooth, bool aMaySnap)
michael@0 710 {
michael@0 711 nsRect crect;
michael@0 712 GetClientRect(crect);
michael@0 713 nscoord offset = IsHorizontal() ? crect.x : crect.y;
michael@0 714 int32_t newPos = NSToIntRound((aNewThumbPos - offset) / mRatio);
michael@0 715
michael@0 716 if (aMaySnap && mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::snap,
michael@0 717 nsGkAtoms::_true, eCaseMatters)) {
michael@0 718 // If snap="true", then the slider may only be set to min + (increment * x).
michael@0 719 // Otherwise, the slider may be set to any positive integer.
michael@0 720 int32_t increment = GetIncrement(aScrollbar);
michael@0 721 newPos = NSToIntRound(newPos / float(increment)) * increment;
michael@0 722 }
michael@0 723
michael@0 724 SetCurrentPosition(aScrollbar, newPos, aIsSmooth);
michael@0 725 }
michael@0 726
michael@0 727 // Use this function when you know the target scroll position of the scrolled content.
michael@0 728 // aNewPos should be passed to this function as a position as if the minpos is 0.
michael@0 729 // That is, the minpos will be added to the position by this function. In a reverse
michael@0 730 // direction slider, the newpos should be the distance from the end.
michael@0 731 void
michael@0 732 nsSliderFrame::SetCurrentPosition(nsIContent* aScrollbar, int32_t aNewPos,
michael@0 733 bool aIsSmooth)
michael@0 734 {
michael@0 735 // get min and max position from our content node
michael@0 736 int32_t minpos = GetMinPosition(aScrollbar);
michael@0 737 int32_t maxpos = GetMaxPosition(aScrollbar);
michael@0 738
michael@0 739 // in reverse direction sliders, flip the value so that it goes from
michael@0 740 // right to left, or bottom to top.
michael@0 741 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
michael@0 742 nsGkAtoms::reverse, eCaseMatters))
michael@0 743 aNewPos = maxpos - aNewPos;
michael@0 744 else
michael@0 745 aNewPos += minpos;
michael@0 746
michael@0 747 // get the new position and make sure it is in bounds
michael@0 748 if (aNewPos < minpos || maxpos < minpos)
michael@0 749 aNewPos = minpos;
michael@0 750 else if (aNewPos > maxpos)
michael@0 751 aNewPos = maxpos;
michael@0 752
michael@0 753 SetCurrentPositionInternal(aScrollbar, aNewPos, aIsSmooth);
michael@0 754 }
michael@0 755
michael@0 756 void
michael@0 757 nsSliderFrame::SetCurrentPositionInternal(nsIContent* aScrollbar, int32_t aNewPos,
michael@0 758 bool aIsSmooth)
michael@0 759 {
michael@0 760 nsCOMPtr<nsIContent> scrollbar = aScrollbar;
michael@0 761 nsIFrame* scrollbarBox = GetScrollbar();
michael@0 762
michael@0 763 mUserChanged = true;
michael@0 764
michael@0 765 nsScrollbarFrame* scrollbarFrame = do_QueryFrame(scrollbarBox);
michael@0 766 if (scrollbarFrame) {
michael@0 767 // See if we have a mediator.
michael@0 768 nsIScrollbarMediator* mediator = scrollbarFrame->GetScrollbarMediator();
michael@0 769 if (mediator) {
michael@0 770 nsRefPtr<nsPresContext> context = PresContext();
michael@0 771 nsCOMPtr<nsIContent> content = GetContent();
michael@0 772 mediator->PositionChanged(scrollbarFrame, GetCurrentPosition(scrollbar), aNewPos);
michael@0 773 // 'mediator' might be dangling now...
michael@0 774 UpdateAttribute(scrollbar, aNewPos, false, aIsSmooth);
michael@0 775 nsIFrame* frame = content->GetPrimaryFrame();
michael@0 776 if (frame && frame->GetType() == nsGkAtoms::sliderFrame) {
michael@0 777 static_cast<nsSliderFrame*>(frame)->CurrentPositionChanged();
michael@0 778 }
michael@0 779 mUserChanged = false;
michael@0 780 return;
michael@0 781 }
michael@0 782 }
michael@0 783
michael@0 784 UpdateAttribute(scrollbar, aNewPos, true, aIsSmooth);
michael@0 785 mUserChanged = false;
michael@0 786
michael@0 787 #ifdef DEBUG_SLIDER
michael@0 788 printf("Current Pos=%d\n",aNewPos);
michael@0 789 #endif
michael@0 790
michael@0 791 }
michael@0 792
michael@0 793 nsIAtom*
michael@0 794 nsSliderFrame::GetType() const
michael@0 795 {
michael@0 796 return nsGkAtoms::sliderFrame;
michael@0 797 }
michael@0 798
michael@0 799 nsresult
michael@0 800 nsSliderFrame::SetInitialChildList(ChildListID aListID,
michael@0 801 nsFrameList& aChildList)
michael@0 802 {
michael@0 803 nsresult r = nsBoxFrame::SetInitialChildList(aListID, aChildList);
michael@0 804
michael@0 805 AddListener();
michael@0 806
michael@0 807 return r;
michael@0 808 }
michael@0 809
michael@0 810 nsresult
michael@0 811 nsSliderMediator::HandleEvent(nsIDOMEvent* aEvent)
michael@0 812 {
michael@0 813 // Only process the event if the thumb is not being dragged.
michael@0 814 if (mSlider && !mSlider->isDraggingThumb())
michael@0 815 return mSlider->StartDrag(aEvent);
michael@0 816
michael@0 817 return NS_OK;
michael@0 818 }
michael@0 819
michael@0 820 nsresult
michael@0 821 nsSliderFrame::StartDrag(nsIDOMEvent* aEvent)
michael@0 822 {
michael@0 823 #ifdef DEBUG_SLIDER
michael@0 824 printf("Begin dragging\n");
michael@0 825 #endif
michael@0 826 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
michael@0 827 nsGkAtoms::_true, eCaseMatters))
michael@0 828 return NS_OK;
michael@0 829
michael@0 830 WidgetGUIEvent* event = aEvent->GetInternalNSEvent()->AsGUIEvent();
michael@0 831
michael@0 832 if (!ShouldScrollForEvent(event)) {
michael@0 833 return NS_OK;
michael@0 834 }
michael@0 835
michael@0 836 nsPoint pt;
michael@0 837 if (!GetEventPoint(event, pt)) {
michael@0 838 return NS_OK;
michael@0 839 }
michael@0 840 bool isHorizontal = IsHorizontal();
michael@0 841 nscoord pos = isHorizontal ? pt.x : pt.y;
michael@0 842
michael@0 843 // If we should scroll-to-click, first place the middle of the slider thumb
michael@0 844 // under the mouse.
michael@0 845 nsCOMPtr<nsIContent> scrollbar;
michael@0 846 nscoord newpos = pos;
michael@0 847 bool scrollToClick = ShouldScrollToClickForEvent(event);
michael@0 848 if (scrollToClick) {
michael@0 849 // adjust so that the middle of the thumb is placed under the click
michael@0 850 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 851 if (!thumbFrame) {
michael@0 852 return NS_OK;
michael@0 853 }
michael@0 854 nsSize thumbSize = thumbFrame->GetSize();
michael@0 855 nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
michael@0 856
michael@0 857 newpos -= (thumbLength/2);
michael@0 858
michael@0 859 nsIFrame* scrollbarBox = GetScrollbar();
michael@0 860 scrollbar = GetContentOfBox(scrollbarBox);
michael@0 861 }
michael@0 862
michael@0 863 DragThumb(true);
michael@0 864
michael@0 865 if (scrollToClick) {
michael@0 866 // should aMaySnap be true here?
michael@0 867 SetCurrentThumbPosition(scrollbar, newpos, false, false);
michael@0 868 }
michael@0 869
michael@0 870 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 871 if (!thumbFrame) {
michael@0 872 return NS_OK;
michael@0 873 }
michael@0 874
michael@0 875 if (isHorizontal)
michael@0 876 mThumbStart = thumbFrame->GetPosition().x;
michael@0 877 else
michael@0 878 mThumbStart = thumbFrame->GetPosition().y;
michael@0 879
michael@0 880 mDragStart = pos - mThumbStart;
michael@0 881
michael@0 882 #ifdef DEBUG_SLIDER
michael@0 883 printf("Pressed mDragStart=%d\n",mDragStart);
michael@0 884 #endif
michael@0 885
michael@0 886 return NS_OK;
michael@0 887 }
michael@0 888
michael@0 889 void
michael@0 890 nsSliderFrame::DragThumb(bool aGrabMouseEvents)
michael@0 891 {
michael@0 892 // inform the parent <scale> that a drag is beginning or ending
michael@0 893 nsIFrame* parent = GetParent();
michael@0 894 if (parent) {
michael@0 895 nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent());
michael@0 896 if (sliderListener) {
michael@0 897 nsContentUtils::AddScriptRunner(
michael@0 898 new nsDragStateChangedRunnable(sliderListener, aGrabMouseEvents));
michael@0 899 }
michael@0 900 }
michael@0 901
michael@0 902 nsIPresShell::SetCapturingContent(aGrabMouseEvents ? GetContent() : nullptr,
michael@0 903 aGrabMouseEvents ? CAPTURE_IGNOREALLOWED : 0);
michael@0 904 }
michael@0 905
michael@0 906 bool
michael@0 907 nsSliderFrame::isDraggingThumb()
michael@0 908 {
michael@0 909 return (nsIPresShell::GetCapturingContent() == GetContent());
michael@0 910 }
michael@0 911
michael@0 912 void
michael@0 913 nsSliderFrame::AddListener()
michael@0 914 {
michael@0 915 if (!mMediator) {
michael@0 916 mMediator = new nsSliderMediator(this);
michael@0 917 }
michael@0 918
michael@0 919 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 920 if (!thumbFrame) {
michael@0 921 return;
michael@0 922 }
michael@0 923 thumbFrame->GetContent()->
michael@0 924 AddSystemEventListener(NS_LITERAL_STRING("mousedown"), mMediator,
michael@0 925 false, false);
michael@0 926 thumbFrame->GetContent()->
michael@0 927 AddSystemEventListener(NS_LITERAL_STRING("touchstart"), mMediator,
michael@0 928 false, false);
michael@0 929 }
michael@0 930
michael@0 931 void
michael@0 932 nsSliderFrame::RemoveListener()
michael@0 933 {
michael@0 934 NS_ASSERTION(mMediator, "No listener was ever added!!");
michael@0 935
michael@0 936 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 937 if (!thumbFrame)
michael@0 938 return;
michael@0 939
michael@0 940 thumbFrame->GetContent()->
michael@0 941 RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMediator, false);
michael@0 942 }
michael@0 943
michael@0 944 bool
michael@0 945 nsSliderFrame::ShouldScrollForEvent(WidgetGUIEvent* aEvent)
michael@0 946 {
michael@0 947 switch (aEvent->message) {
michael@0 948 case NS_TOUCH_START:
michael@0 949 case NS_TOUCH_END:
michael@0 950 return true;
michael@0 951 case NS_MOUSE_BUTTON_DOWN:
michael@0 952 case NS_MOUSE_BUTTON_UP: {
michael@0 953 uint16_t button = aEvent->AsMouseEvent()->button;
michael@0 954 return (button == WidgetMouseEvent::eLeftButton) ||
michael@0 955 (button == WidgetMouseEvent::eMiddleButton && gMiddlePref);
michael@0 956 }
michael@0 957 default:
michael@0 958 return false;
michael@0 959 }
michael@0 960 }
michael@0 961
michael@0 962 bool
michael@0 963 nsSliderFrame::ShouldScrollToClickForEvent(WidgetGUIEvent* aEvent)
michael@0 964 {
michael@0 965 if (!ShouldScrollForEvent(aEvent)) {
michael@0 966 return false;
michael@0 967 }
michael@0 968
michael@0 969 if (aEvent->message == NS_TOUCH_START) {
michael@0 970 return GetScrollToClick();
michael@0 971 }
michael@0 972
michael@0 973 if (aEvent->message != NS_MOUSE_BUTTON_DOWN) {
michael@0 974 return false;
michael@0 975 }
michael@0 976
michael@0 977 #ifdef XP_MACOSX
michael@0 978 // On Mac, clicking the scrollbar thumb should never scroll to click.
michael@0 979 if (IsEventOverThumb(aEvent)) {
michael@0 980 return false;
michael@0 981 }
michael@0 982 #endif
michael@0 983
michael@0 984 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
michael@0 985 if (mouseEvent->button == WidgetMouseEvent::eLeftButton) {
michael@0 986 #ifdef XP_MACOSX
michael@0 987 bool invertPref = mouseEvent->IsAlt();
michael@0 988 #else
michael@0 989 bool invertPref = mouseEvent->IsShift();
michael@0 990 #endif
michael@0 991 return GetScrollToClick() != invertPref;
michael@0 992 }
michael@0 993
michael@0 994 return true;
michael@0 995 }
michael@0 996
michael@0 997 bool
michael@0 998 nsSliderFrame::IsEventOverThumb(WidgetGUIEvent* aEvent)
michael@0 999 {
michael@0 1000 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 1001 if (!thumbFrame) {
michael@0 1002 return false;
michael@0 1003 }
michael@0 1004
michael@0 1005 nsPoint eventPoint;
michael@0 1006 if (!GetEventPoint(aEvent, eventPoint)) {
michael@0 1007 return false;
michael@0 1008 }
michael@0 1009
michael@0 1010 bool isHorizontal = IsHorizontal();
michael@0 1011 nsRect thumbRect = thumbFrame->GetRect();
michael@0 1012 nscoord eventPos = isHorizontal ? eventPoint.x : eventPoint.y;
michael@0 1013 nscoord thumbStart = isHorizontal ? thumbRect.x : thumbRect.y;
michael@0 1014 nscoord thumbEnd = isHorizontal ? thumbRect.XMost() : thumbRect.YMost();
michael@0 1015
michael@0 1016 return eventPos >= thumbStart && eventPos < thumbEnd;
michael@0 1017 }
michael@0 1018
michael@0 1019 NS_IMETHODIMP
michael@0 1020 nsSliderFrame::HandlePress(nsPresContext* aPresContext,
michael@0 1021 WidgetGUIEvent* aEvent,
michael@0 1022 nsEventStatus* aEventStatus)
michael@0 1023 {
michael@0 1024 if (!ShouldScrollForEvent(aEvent) || ShouldScrollToClickForEvent(aEvent)) {
michael@0 1025 return NS_OK;
michael@0 1026 }
michael@0 1027
michael@0 1028 if (IsEventOverThumb(aEvent)) {
michael@0 1029 return NS_OK;
michael@0 1030 }
michael@0 1031
michael@0 1032 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 1033 if (!thumbFrame) // display:none?
michael@0 1034 return NS_OK;
michael@0 1035
michael@0 1036 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
michael@0 1037 nsGkAtoms::_true, eCaseMatters))
michael@0 1038 return NS_OK;
michael@0 1039
michael@0 1040 nsRect thumbRect = thumbFrame->GetRect();
michael@0 1041
michael@0 1042 nscoord change = 1;
michael@0 1043 nsPoint eventPoint;
michael@0 1044 if (!GetEventPoint(aEvent, eventPoint)) {
michael@0 1045 return NS_OK;
michael@0 1046 }
michael@0 1047 if (IsHorizontal() ? eventPoint.x < thumbRect.x
michael@0 1048 : eventPoint.y < thumbRect.y)
michael@0 1049 change = -1;
michael@0 1050
michael@0 1051 mChange = change;
michael@0 1052 DragThumb(true);
michael@0 1053 mDestinationPoint = eventPoint;
michael@0 1054 StartRepeat();
michael@0 1055 PageUpDown(change);
michael@0 1056 return NS_OK;
michael@0 1057 }
michael@0 1058
michael@0 1059 NS_IMETHODIMP
michael@0 1060 nsSliderFrame::HandleRelease(nsPresContext* aPresContext,
michael@0 1061 WidgetGUIEvent* aEvent,
michael@0 1062 nsEventStatus* aEventStatus)
michael@0 1063 {
michael@0 1064 StopRepeat();
michael@0 1065
michael@0 1066 return NS_OK;
michael@0 1067 }
michael@0 1068
michael@0 1069 void
michael@0 1070 nsSliderFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 1071 {
michael@0 1072 // tell our mediator if we have one we are gone.
michael@0 1073 if (mMediator) {
michael@0 1074 mMediator->SetSlider(nullptr);
michael@0 1075 mMediator = nullptr;
michael@0 1076 }
michael@0 1077 StopRepeat();
michael@0 1078
michael@0 1079 // call base class Destroy()
michael@0 1080 nsBoxFrame::DestroyFrom(aDestructRoot);
michael@0 1081 }
michael@0 1082
michael@0 1083 nsSize
michael@0 1084 nsSliderFrame::GetPrefSize(nsBoxLayoutState& aState)
michael@0 1085 {
michael@0 1086 EnsureOrient();
michael@0 1087 return nsBoxFrame::GetPrefSize(aState);
michael@0 1088 }
michael@0 1089
michael@0 1090 nsSize
michael@0 1091 nsSliderFrame::GetMinSize(nsBoxLayoutState& aState)
michael@0 1092 {
michael@0 1093 EnsureOrient();
michael@0 1094
michael@0 1095 // our min size is just our borders and padding
michael@0 1096 return nsBox::GetMinSize(aState);
michael@0 1097 }
michael@0 1098
michael@0 1099 nsSize
michael@0 1100 nsSliderFrame::GetMaxSize(nsBoxLayoutState& aState)
michael@0 1101 {
michael@0 1102 EnsureOrient();
michael@0 1103 return nsBoxFrame::GetMaxSize(aState);
michael@0 1104 }
michael@0 1105
michael@0 1106 void
michael@0 1107 nsSliderFrame::EnsureOrient()
michael@0 1108 {
michael@0 1109 nsIFrame* scrollbarBox = GetScrollbar();
michael@0 1110
michael@0 1111 bool isHorizontal = (scrollbarBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0;
michael@0 1112 if (isHorizontal)
michael@0 1113 mState |= NS_STATE_IS_HORIZONTAL;
michael@0 1114 else
michael@0 1115 mState &= ~NS_STATE_IS_HORIZONTAL;
michael@0 1116 }
michael@0 1117
michael@0 1118
michael@0 1119 void nsSliderFrame::Notify(void)
michael@0 1120 {
michael@0 1121 bool stop = false;
michael@0 1122
michael@0 1123 nsIFrame* thumbFrame = mFrames.FirstChild();
michael@0 1124 if (!thumbFrame) {
michael@0 1125 StopRepeat();
michael@0 1126 return;
michael@0 1127 }
michael@0 1128 nsRect thumbRect = thumbFrame->GetRect();
michael@0 1129
michael@0 1130 bool isHorizontal = IsHorizontal();
michael@0 1131
michael@0 1132 // See if the thumb has moved past our destination point.
michael@0 1133 // if it has we want to stop.
michael@0 1134 if (isHorizontal) {
michael@0 1135 if (mChange < 0) {
michael@0 1136 if (thumbRect.x < mDestinationPoint.x)
michael@0 1137 stop = true;
michael@0 1138 } else {
michael@0 1139 if (thumbRect.x + thumbRect.width > mDestinationPoint.x)
michael@0 1140 stop = true;
michael@0 1141 }
michael@0 1142 } else {
michael@0 1143 if (mChange < 0) {
michael@0 1144 if (thumbRect.y < mDestinationPoint.y)
michael@0 1145 stop = true;
michael@0 1146 } else {
michael@0 1147 if (thumbRect.y + thumbRect.height > mDestinationPoint.y)
michael@0 1148 stop = true;
michael@0 1149 }
michael@0 1150 }
michael@0 1151
michael@0 1152
michael@0 1153 if (stop) {
michael@0 1154 StopRepeat();
michael@0 1155 } else {
michael@0 1156 PageUpDown(mChange);
michael@0 1157 }
michael@0 1158 }
michael@0 1159
michael@0 1160 NS_IMPL_ISUPPORTS(nsSliderMediator,
michael@0 1161 nsIDOMEventListener)

mercurial