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/dom/Selection.h" 14 #include "nsCoord.h" 15 #include "nsISelectionListener.h" 16 #include "nsIWeakReferenceUtils.h" 17 #include "CaretAssociationHint.h" 18 #include "nsPoint.h" 19 #include "nsRect.h" 20 21 class nsDisplayListBuilder; 22 class nsFrameSelection; 23 class nsIContent; 24 class nsIFrame; 25 class nsINode; 26 class nsITimer; 27 28 namespace mozilla { 29 class PresShell; 30 namespace gfx { 31 class DrawTarget; 32 } // namespace gfx 33 } // namespace mozilla 34 35 //----------------------------------------------------------------------------- 36 class nsCaret final : public nsISelectionListener { 37 typedef mozilla::gfx::DrawTarget DrawTarget; 38 39 public: 40 nsCaret(); 41 42 protected: 43 virtual ~nsCaret(); 44 45 public: 46 NS_DECL_ISUPPORTS 47 48 typedef mozilla::CaretAssociationHint CaretAssociationHint; 49 50 nsresult Init(mozilla::PresShell* aPresShell); 51 void Terminate(); 52 53 void SetSelection(mozilla::dom::Selection* aDOMSel); 54 mozilla::dom::Selection* GetSelection(); 55 56 /** 57 * Sets whether the caret should only be visible in nodes that are not 58 * user-modify: read-only, or whether it should be visible in all nodes. 59 * 60 * @param aIgnoreUserModify true to have the cursor visible in all nodes, 61 * false to have it visible in all nodes except 62 * those with user-modify: read-only 63 */ 64 void SetIgnoreUserModify(bool aIgnoreUserModify); 65 /** SetVisible will set the visibility of the caret 66 * @param inMakeVisible true to show the caret, false to hide it 67 */ 68 void SetVisible(bool intMakeVisible); 69 /** IsVisible will get the visibility of the caret. 70 * This returns false if the caret is hidden because it was set 71 * to not be visible, or because the selection is not collapsed, or 72 * because an open popup is hiding the caret. 73 * It does not take account of blinking or the caret being hidden 74 * because we're in non-editable/disabled content. 75 */ 76 bool IsVisible(mozilla::dom::Selection* aSelection = nullptr) { 77 if (!mVisible || mHideCount) { 78 return false; 79 } 80 81 if (!mShowDuringSelection) { 82 mozilla::dom::Selection* selection; 83 if (aSelection) { 84 selection = aSelection; 85 } else { 86 selection = GetSelection(); 87 } 88 if (!selection || !selection->IsCollapsed()) { 89 return false; 90 } 91 } 92 93 if (IsMenuPopupHidingCaret()) { 94 return false; 95 } 96 97 return true; 98 } 99 /** 100 * AddForceHide() increases mHideCount and hide the caret even if 101 * SetVisible(true) has been or will be called. This is useful when the 102 * caller wants to hide caret temporarily and it needs to cancel later. 103 * Especially, in the latter case, it's too difficult to decide if the 104 * caret should be actually visible or not because caret visible state 105 * is set from a lot of event handlers. So, it's very stateful. 106 */ 107 void AddForceHide(); 108 /** 109 * RemoveForceHide() decreases mHideCount if it's over 0. 110 * If the value becomes 0, this may show the caret if SetVisible(true) 111 * has been called. 112 */ 113 void RemoveForceHide(); 114 /** SetCaretReadOnly set the appearance of the caret 115 * @param inMakeReadonly true to show the caret in a 'read only' state, 116 * false to show the caret in normal, editing state 117 */ 118 void SetCaretReadOnly(bool inMakeReadonly); 119 /** 120 * @param aVisibility true if the caret should be visible even when the 121 * selection is not collapsed. 122 */ 123 void SetVisibilityDuringSelection(bool aVisibility); 124 125 /** 126 * Set the caret's position explicitly to the specified node and offset 127 * instead of tracking its selection. 128 * Passing null for aNode would set the caret to track its selection again. 129 **/ 130 void SetCaretPosition(nsINode* aNode, int32_t aOffset); 131 132 /** 133 * Schedule a repaint for the frame where the caret would appear. 134 * Does not check visibility etc. 135 */ 136 void SchedulePaint(mozilla::dom::Selection* aSelection = nullptr); 137 138 /** 139 * Returns a frame to paint in, and the bounds of the painted caret 140 * relative to that frame. 141 * The rectangle includes bidi decorations. 142 * Returns null if the caret should not be drawn (including if it's blinked 143 * off). 144 */ 145 nsIFrame* GetPaintGeometry(nsRect* aRect); 146 /** 147 * A simple wrapper around GetGeometry. Does not take any caret state into 148 * account other than the current selection. 149 */ GetGeometry(nsRect * aRect)150 nsIFrame* GetGeometry(nsRect* aRect) { 151 return GetGeometry(GetSelection(), aRect); 152 } 153 154 /** PaintCaret 155 * Actually paint the caret onto the given rendering context. 156 */ 157 void PaintCaret(DrawTarget& aDrawTarget, nsIFrame* aForFrame, 158 const nsPoint& aOffset); 159 160 // nsISelectionListener interface 161 NS_DECL_NSISELECTIONLISTENER 162 163 /** 164 * Gets the position and size of the caret that would be drawn for 165 * the focus node/offset of aSelection (assuming it would be drawn, 166 * i.e., disregarding blink status). The geometry is stored in aRect, 167 * and we return the frame aRect is relative to. 168 * Only looks at the focus node of aSelection, so you can call it even if 169 * aSelection is not collapsed. 170 * This rect does not include any extra decorations for bidi. 171 * @param aRect must be non-null 172 */ 173 static nsIFrame* GetGeometry(const mozilla::dom::Selection* aSelection, 174 nsRect* aRect); 175 static nsIFrame* GetCaretFrameForNodeOffset( 176 nsFrameSelection* aFrameSelection, nsIContent* aContentNode, 177 int32_t aOffset, CaretAssociationHint aFrameHint, uint8_t aBidiLevel, 178 nsIFrame** aReturnUnadjustedFrame, int32_t* aReturnOffset); 179 static nsRect GetGeometryForFrame(nsIFrame* aFrame, int32_t aFrameOffset, 180 nscoord* aBidiIndicatorSize); 181 182 // Get the frame and frame offset based on the focus node and focus offset 183 // of aSelection. If aOverrideNode and aOverride are provided, use them 184 // instead. 185 // @param aFrameOffset return the frame offset if non-null. 186 // @param aUnadjustedFrame return the original frame that the selection is 187 // targeting, without any adjustment for painting. 188 // @return the frame of the focus node. 189 static nsIFrame* GetFrameAndOffset(const mozilla::dom::Selection* aSelection, 190 nsINode* aOverrideNode, 191 int32_t aOverrideOffset, 192 int32_t* aFrameOffset, 193 nsIFrame** aUnadjustedFrame = nullptr); 194 195 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; 196 197 nsIFrame* GetFrame(int32_t* aContentOffset); 198 void ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset, 199 nsRect* aCaretRect, nsRect* aHookRect); 200 201 protected: 202 static void CaretBlinkCallback(nsITimer* aTimer, void* aClosure); 203 204 void CheckSelectionLanguageChange(); 205 206 void ResetBlinking(); 207 void StopBlinking(); 208 209 struct Metrics { 210 nscoord mBidiIndicatorSize; // width and height of bidi indicator 211 nscoord mCaretWidth; // full caret width including bidi indicator 212 }; 213 static Metrics ComputeMetrics(nsIFrame* aFrame, int32_t aOffset, 214 nscoord aCaretHeight); 215 216 // Returns true if we should not draw the caret because of XUL menu popups. 217 // The caret should be hidden if: 218 // 1. An open popup contains the caret, but a menu popup exists before the 219 // caret-owning popup in the popup list (i.e. a menu is in front of the 220 // popup with the caret). If the menu itself contains the caret we don't 221 // hide it. 222 // 2. A menu popup is open, but there is no caret present in any popup. 223 // 3. The caret selection is empty. 224 bool IsMenuPopupHidingCaret(); 225 226 nsWeakPtr mPresShell; 227 mozilla::WeakPtr<mozilla::dom::Selection> mDomSelectionWeak; 228 229 nsCOMPtr<nsITimer> mBlinkTimer; 230 231 /** 232 * The content to draw the caret at. If null, we use mDomSelectionWeak's 233 * focus node instead. 234 */ 235 nsCOMPtr<nsINode> mOverrideContent; 236 /** 237 * The character offset to draw the caret at. 238 * Ignored if mOverrideContent is null. 239 */ 240 int32_t mOverrideOffset; 241 /** 242 * mBlinkCount is used to control the number of times to blink the caret 243 * before stopping the blink. This is reset each time we reset the 244 * blinking. 245 */ 246 int32_t mBlinkCount; 247 /** 248 * mBlinkRate is the rate of the caret blinking the last time we read it. 249 * It is used as a way to optimize whether we need to reset the blinking 250 * timer. 251 */ 252 uint32_t mBlinkRate; 253 /** 254 * mHideCount is not 0, it means that somebody doesn't want the caret 255 * to be visible. See AddForceHide() and RemoveForceHide(). 256 */ 257 uint32_t mHideCount; 258 259 /** 260 * mIsBlinkOn is true when we're in a blink cycle where the caret is on. 261 */ 262 bool mIsBlinkOn; 263 /** 264 * mIsVisible is true when SetVisible was last called with 'true'. 265 */ 266 bool mVisible; 267 /** 268 * mReadOnly is true when the caret is set to "read only" mode (i.e., 269 * it doesn't blink). 270 */ 271 bool mReadOnly; 272 /** 273 * mShowDuringSelection is true when the caret should be shown even when 274 * the selection is not collapsed. 275 */ 276 bool mShowDuringSelection; 277 /** 278 * mIgnoreUserModify is true when the caret should be shown even when 279 * it's in non-user-modifiable content. 280 */ 281 bool mIgnoreUserModify; 282 }; 283 284 #endif // nsCaret_h__ 285