nsCaret.h (8556B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* the caret is the text cursor used, e.g., when editing */ 8 9 #ifndef nsCaret_h__ 10 #define nsCaret_h__ 11 12 #include "mozilla/MemoryReporting.h" 13 #include "mozilla/SelectionMovementUtils.h" 14 #include "nsCoord.h" 15 #include "nsIFrame.h" 16 #include "nsISelectionListener.h" 17 #include "nsPoint.h" 18 #include "nsRect.h" 19 20 class nsFrameSelection; 21 class nsIContent; 22 class nsIFrame; 23 class nsINode; 24 class nsITimer; 25 26 namespace mozilla { 27 class PresShell; 28 enum class CaretAssociationHint; 29 namespace gfx { 30 class DrawTarget; 31 } // namespace gfx 32 } // namespace mozilla 33 34 //----------------------------------------------------------------------------- 35 class nsCaret final : public nsISelectionListener { 36 typedef mozilla::gfx::DrawTarget DrawTarget; 37 38 public: 39 nsCaret(); 40 41 protected: 42 virtual ~nsCaret(); 43 44 public: 45 NS_DECL_ISUPPORTS 46 47 using CaretAssociationHint = mozilla::CaretAssociationHint; 48 49 nsresult Init(mozilla::PresShell*); 50 void Terminate(); 51 52 void SetSelection(mozilla::dom::Selection*); 53 mozilla::dom::Selection* GetSelection(); 54 55 /** 56 * SetVisible will set the visibility of the caret 57 * @param aVisible true to show the caret, false to hide it 58 */ 59 void SetVisible(bool aVisible); 60 /** 61 * IsVisible will get the visibility of the caret. 62 * It does not take account of blinking or the caret being hidden because 63 * we're in non-editable/disabled content. 64 */ 65 bool IsVisible() const; 66 67 /** 68 * AddForceHide() increases mHideCount and hide the caret even if 69 * SetVisible(true) has been or will be called. This is useful when the 70 * caller wants to hide caret temporarily and it needs to cancel later. 71 * Especially, in the latter case, it's too difficult to decide if the 72 * caret should be actually visible or not because caret visible state 73 * is set from a lot of event handlers. So, it's very stateful. 74 */ 75 void AddForceHide(); 76 /** 77 * RemoveForceHide() decreases mHideCount if it's over 0. 78 * If the value becomes 0, this may show the caret if SetVisible(true) 79 * has been called. 80 */ 81 void RemoveForceHide(); 82 /** SetCaretReadOnly set the appearance of the caret 83 * @param inMakeReadonly true to show the caret in a 'read only' state, 84 * false to show the caret in normal, editing state 85 */ 86 void SetCaretReadOnly(bool aReadOnly); 87 /** 88 * @param aVisibility true if the caret should be visible even when the 89 * selection is not collapsed. 90 */ 91 void SetVisibilityDuringSelection(bool aVisibility); 92 93 /** 94 * Set the caret's position explicitly to the specified node and offset 95 * instead of tracking its selection. 96 * Passing null for aNode would set the caret to track its selection again. 97 **/ 98 void SetCaretPosition(nsINode* aNode, int32_t aOffset); 99 100 /** 101 * Schedule a repaint for the frame where the caret would appear. 102 * Does not check visibility etc. 103 */ 104 void SchedulePaint(); 105 106 nsIFrame* GetLastPaintedFrame() { return mLastPaintedFrame; } 107 void SetLastPaintedFrame(nsIFrame* aFrame) { mLastPaintedFrame = aFrame; } 108 109 /** 110 * Returns a frame to paint in, and optionally the bounds of the painted caret 111 * relative to that frame. The rectangle includes bidi decorations. 112 * Returns null if the caret should not be drawn (including if it's blinked 113 * off). 114 */ 115 nsIFrame* GetPaintGeometry(); 116 nsIFrame* GetPaintGeometry(nsRect* aRect); 117 118 /** 119 * Same as the overload above, but returns the caret and hook rects 120 * separately, and also computes the color if requested. 121 */ 122 nsIFrame* GetPaintGeometry(nsRect* aCaretRect, nsRect* aHookRect, 123 nscolor* aCaretColor = nullptr); 124 /** 125 * A simple wrapper around GetGeometry. Does not take any caret state into 126 * account other than the current selection. 127 */ 128 nsIFrame* GetGeometry(nsRect* aRect) { 129 return GetGeometry(GetSelection(), aRect); 130 } 131 132 /** PaintCaret 133 * Actually paint the caret onto the given rendering context. 134 */ 135 void PaintCaret(DrawTarget& aDrawTarget, nsIFrame* aForFrame, 136 const nsPoint& aOffset); 137 138 // nsISelectionListener interface 139 NS_DECL_NSISELECTIONLISTENER 140 141 /** The current caret position. */ 142 struct CaretPosition { 143 nsCOMPtr<nsINode> mContent; 144 int32_t mOffset = 0; 145 CaretAssociationHint mHint{0}; 146 mozilla::intl::BidiEmbeddingLevel mBidiLevel; 147 148 bool operator==(const CaretPosition& aOther) const = default; 149 150 explicit operator bool() const { return !!mContent; } 151 }; 152 153 static CaretPosition CaretPositionFor(const mozilla::dom::Selection*); 154 155 /** 156 * Gets the position and size of the caret that would be drawn for 157 * the focus node/offset of aSelection (assuming it would be drawn, 158 * i.e., disregarding blink status). The geometry is stored in aRect, 159 * and we return the frame aRect is relative to. 160 * Only looks at the focus node of aSelection, so you can call it even if 161 * aSelection is not collapsed. 162 * This rect does not include any extra decorations for bidi. 163 * @param aRect must be non-null 164 */ 165 static nsIFrame* GetGeometry(const mozilla::dom::Selection* aSelection, 166 nsRect* aRect); 167 168 static nsRect GetGeometryForFrame(nsIFrame* aFrame, int32_t aFrameOffset, 169 nscoord* aBidiIndicatorSize); 170 171 // Get the frame and frame offset based on aPosition. 172 static mozilla::CaretFrameData GetFrameAndOffset( 173 const CaretPosition& aPosition); 174 175 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; 176 177 protected: 178 static void CaretBlinkCallback(nsITimer* aTimer, void* aClosure); 179 180 void CheckSelectionLanguageChange(); 181 void CaretVisibilityMaybeChanged(); 182 183 void ResetBlinking(); 184 void StopBlinking(); 185 186 struct Metrics { 187 nscoord mBidiIndicatorSize; // width and height of bidi indicator 188 nscoord mCaretWidth; // full caret width including bidi indicator 189 }; 190 static Metrics ComputeMetrics(nsIFrame* aFrame, int32_t aOffset, 191 nscoord aCaretHeight); 192 void ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset, 193 nsRect* aCaretRect, nsRect* aHookRect); 194 195 // If we're tracking the selection, this updates the caret position and 196 // invalidates paint as needed. 197 void UpdateCaretPositionFromSelectionIfNeeded(); 198 199 mozilla::WeakPtr<mozilla::dom::Selection> mDomSelectionWeak; 200 201 nsCOMPtr<nsITimer> mBlinkTimer; 202 // Last time we reset the blink timer. We give it some slack to avoid 203 // resetting it too often. This gets cleared when CaretBlinkCallback fires, 204 // because the point of this variable is just to avoid resetting too many 205 // times in a single blink cycle. 206 mozilla::TimeStamp mLastBlinkTimerReset; 207 208 CaretPosition mCaretPosition; 209 210 // The last frame we painted the caret in. 211 WeakFrame mLastPaintedFrame; 212 213 /** 214 * mBlinkCount is used to control the number of times to blink the caret 215 * before stopping the blink. This is reset each time we reset the 216 * blinking. 217 */ 218 int32_t mBlinkCount = -1; 219 /** 220 * Current blink time (the value that LookAndFeel::CaretBlinkTime() gave us 221 * when we most recently reset our blinking). 222 */ 223 int32_t mBlinkTime = -1; 224 /** 225 * mHideCount is not 0, it means that somebody doesn't want the caret 226 * to be visible. See AddForceHide() and RemoveForceHide(). 227 */ 228 uint32_t mHideCount = 0; 229 230 /** 231 * mIsBlinkOn is true when we're in a blink cycle where the caret is on. 232 */ 233 bool mIsBlinkOn = false; 234 /** 235 * mIsVisible is true when SetVisible was last called with 'true'. 236 */ 237 bool mVisible = false; 238 /** 239 * mReadOnly is true when the caret is set to "read only" mode (i.e., 240 * it doesn't blink). 241 */ 242 bool mReadOnly = false; 243 /** 244 * mShowDuringSelection is true when the caret should be shown even when 245 * the selection is not collapsed. 246 */ 247 bool mShowDuringSelection = false; 248 249 /** 250 * If the caret position is fixed, it's been overridden externally and it 251 * will not track the selection. 252 */ 253 bool mFixedCaretPosition = false; 254 255 /** 256 * If we're currently hiding the caret due to the selection not being 257 * collapsed. Can only be true if mShowDuringSelection is false. 258 */ 259 bool mHiddenDuringSelection = false; 260 }; 261 262 #endif // nsCaret_h__