summaryrefslogtreecommitdiffstats
path: root/layout/generic/ScrollbarActivity.h
blob: 26615eea810856d7423505ce2b27ffbb032e79db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef ScrollbarActivity_h___
#define ScrollbarActivity_h___

#include "mozilla/Attributes.h"
#include "nsCOMPtr.h"
#include "nsIDOMEventListener.h"
#include "mozilla/TimeStamp.h"
#include "nsRefreshObservers.h"

class nsIContent;
class nsIScrollbarMediator;
class nsITimer;
class nsRefreshDriver;

namespace mozilla {

namespace dom {
class Element;
class EventTarget;
}  // namespace dom

namespace layout {

/**
 * ScrollbarActivity
 *
 * This class manages scrollbar behavior that imitates the native Mac OS X
 * Lion overlay scrollbar behavior: Scrollbars are only shown while "scrollbar
 * activity" occurs, and they're hidden with a fade animation after a short
 * delay.
 *
 * Scrollbar activity has these states:
 *  - inactive:
 *      Scrollbars are hidden.
 *  - ongoing activity:
 *      Scrollbars are visible and being operated on in some way, for example
 *      because they're hovered or pressed.
 *  - active, but waiting for fade out
 *      Scrollbars are still completely visible but are about to fade away.
 *  - fading out
 *      Scrollbars are subject to a fade-out animation.
 *
 * Initial scrollbar activity needs to be reported by the scrollbar holder that
 * owns the ScrollbarActivity instance. This needs to happen via a call to
 * ActivityOccurred(), for example when the current scroll position or the size
 * of the scroll area changes.
 *
 * As soon as scrollbars are visible, the ScrollbarActivity class manages the
 * rest of the activity behavior: It ensures that mouse motions inside the
 * scroll area keep the scrollbars visible, and that scrollbars don't fade away
 * while they're being hovered / dragged. It also sets a sticky hover attribute
 * on the most recently hovered scrollbar.
 *
 * ScrollbarActivity falls into hibernation after the scrollbars have faded
 * out. It only starts acting after the next call to ActivityOccurred() /
 * ActivityStarted().
 */

class ScrollbarActivity final : public nsIDOMEventListener,
                                public nsARefreshObserver {
 public:
  explicit ScrollbarActivity(nsIScrollbarMediator* aScrollableFrame)
      : mScrollableFrame(aScrollableFrame),
        mNestedActivityCounter(0),
        mIsActive(false),
        mIsFading(false),
        mListeningForScrollbarEvents(false),
        mListeningForScrollAreaEvents(false),
        mHScrollbarHovered(false),
        mVScrollbarHovered(false),
        mDisplayOnMouseMove(false),
        mScrollbarFadeBeginDelay(0),
        mScrollbarFadeDuration(0) {
    QueryLookAndFeelVals();
  }

  NS_DECL_ISUPPORTS
  NS_DECL_NSIDOMEVENTLISTENER

  void Destroy();

  void ActivityOccurred();
  void ActivityStarted();
  void ActivityStopped();

  virtual void WillRefresh(TimeStamp aTime) override;

  static void FadeBeginTimerFired(nsITimer* aTimer, void* aSelf) {
    RefPtr<ScrollbarActivity> scrollbarActivity(
        reinterpret_cast<ScrollbarActivity*>(aSelf));
    scrollbarActivity->BeginFade();
  }

 protected:
  virtual ~ScrollbarActivity() = default;

  bool IsActivityOngoing() { return mNestedActivityCounter > 0; }
  bool IsStillFading(TimeStamp aTime);
  void QueryLookAndFeelVals();

  void HandleEventForScrollbar(const nsAString& aType, nsIContent* aTarget,
                               dom::Element* aScrollbar,
                               bool* aStoredHoverState);

  void SetIsActive(bool aNewActive);
  bool SetIsFading(bool aNewFading);  // returns false if 'this' was destroyed

  void BeginFade();
  void EndFade();

  void StartFadeBeginTimer();
  void CancelFadeBeginTimer();

  void StartListeningForScrollbarEvents();
  void StartListeningForScrollAreaEvents();
  void StopListeningForScrollbarEvents();
  void StopListeningForScrollAreaEvents();
  void AddScrollbarEventListeners(dom::EventTarget* aScrollbar);
  void RemoveScrollbarEventListeners(dom::EventTarget* aScrollbar);

  void RegisterWithRefreshDriver();
  void UnregisterFromRefreshDriver();

  bool UpdateOpacity(TimeStamp aTime);  // returns false if 'this' was destroyed
  void HoveredScrollbar(dom::Element* aScrollbar);

  nsRefreshDriver* GetRefreshDriver();
  dom::Element* GetScrollbarContent(bool aVertical);
  dom::Element* GetHorizontalScrollbar() { return GetScrollbarContent(false); }
  dom::Element* GetVerticalScrollbar() { return GetScrollbarContent(true); }

  const TimeDuration FadeDuration() {
    return TimeDuration::FromMilliseconds(mScrollbarFadeDuration);
  }

  nsIScrollbarMediator* mScrollableFrame;
  TimeStamp mFadeBeginTime;
  nsCOMPtr<nsITimer> mFadeBeginTimer;
  nsCOMPtr<dom::EventTarget> mHorizontalScrollbar;  // null while inactive
  nsCOMPtr<dom::EventTarget> mVerticalScrollbar;    // null while inactive
  int mNestedActivityCounter;
  bool mIsActive;
  bool mIsFading;
  bool mListeningForScrollbarEvents;
  bool mListeningForScrollAreaEvents;
  bool mHScrollbarHovered;
  bool mVScrollbarHovered;

  // LookAndFeel values we load on creation
  bool mDisplayOnMouseMove;
  int mScrollbarFadeBeginDelay;
  int mScrollbarFadeDuration;
};

}  // namespace layout
}  // namespace mozilla

#endif /* ScrollbarActivity_h___ */