Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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) |