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