/src/mozilla-central/layout/xul/nsMenuPopupFrame.h
Line | Count | Source (jump to first uncovered line) |
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 | | // |
8 | | // nsMenuPopupFrame |
9 | | // |
10 | | |
11 | | #ifndef nsMenuPopupFrame_h__ |
12 | | #define nsMenuPopupFrame_h__ |
13 | | |
14 | | #include "mozilla/Attributes.h" |
15 | | #include "mozilla/gfx/Types.h" |
16 | | #include "nsAtom.h" |
17 | | #include "nsGkAtoms.h" |
18 | | #include "nsCOMPtr.h" |
19 | | #include "nsMenuFrame.h" |
20 | | |
21 | | #include "nsBoxFrame.h" |
22 | | #include "nsMenuParent.h" |
23 | | |
24 | | #include "nsITimer.h" |
25 | | |
26 | | #include "Units.h" |
27 | | |
28 | | class nsIWidget; |
29 | | |
30 | | namespace mozilla { |
31 | | namespace dom { |
32 | | class KeyboardEvent; |
33 | | } // namespace dom |
34 | | } // namespace mozilla |
35 | | |
36 | | // XUL popups can be in several different states. When opening a popup, the |
37 | | // state changes as follows: |
38 | | // ePopupClosed - initial state |
39 | | // ePopupShowing - during the period when the popupshowing event fires |
40 | | // ePopupOpening - between the popupshowing event and being visible. Creation |
41 | | // of the child frames, layout and reflow occurs in this |
42 | | // state. The popup is stored in the popup manager's list of |
43 | | // open popups during this state. |
44 | | // ePopupVisible - layout is done and the popup's view and widget are made |
45 | | // visible. The popup is visible on screen but may be |
46 | | // transitioning. The popupshown event has not yet fired. |
47 | | // ePopupShown - the popup has been shown and is fully ready. This state is |
48 | | // assigned just before the popupshown event fires. |
49 | | // When closing a popup: |
50 | | // ePopupHidden - during the period when the popuphiding event fires and |
51 | | // the popup is removed. |
52 | | // ePopupClosed - the popup's widget is made invisible. |
53 | | enum nsPopupState { |
54 | | // state when a popup is not open |
55 | | ePopupClosed, |
56 | | // state from when a popup is requested to be shown to after the |
57 | | // popupshowing event has been fired. |
58 | | ePopupShowing, |
59 | | // state while a popup is waiting to be laid out and positioned |
60 | | ePopupPositioning, |
61 | | // state while a popup is open but the widget is not yet visible |
62 | | ePopupOpening, |
63 | | // state while a popup is visible and waiting for the popupshown event |
64 | | ePopupVisible, |
65 | | // state while a popup is open and visible on screen |
66 | | ePopupShown, |
67 | | // state from when a popup is requested to be hidden to when it is closed. |
68 | | ePopupHiding, |
69 | | // state which indicates that the popup was hidden without firing the |
70 | | // popuphiding or popuphidden events. It is used when executing a menu |
71 | | // command because the menu needs to be hidden before the command event |
72 | | // fires, yet the popuphiding and popuphidden events are fired after. This |
73 | | // state can also occur when the popup is removed because the document is |
74 | | // unloaded. |
75 | | ePopupInvisible |
76 | | }; |
77 | | |
78 | | enum ConsumeOutsideClicksResult { |
79 | | ConsumeOutsideClicks_ParentOnly = 0, // Only consume clicks on the parent anchor |
80 | | ConsumeOutsideClicks_True = 1, // Always consume clicks |
81 | | ConsumeOutsideClicks_Never = 2 // Never consume clicks |
82 | | }; |
83 | | |
84 | | // How a popup may be flipped. Flipping to the outside edge is like how |
85 | | // a submenu would work. The entire popup is flipped to the opposite side |
86 | | // of the anchor. |
87 | | enum FlipStyle { |
88 | | FlipStyle_None = 0, |
89 | | FlipStyle_Outside = 1, |
90 | | FlipStyle_Inside = 2 |
91 | | }; |
92 | | |
93 | | // Values for the flip attribute |
94 | | enum FlipType { |
95 | | FlipType_Default = 0, |
96 | | FlipType_None = 1, // don't try to flip or translate to stay onscreen |
97 | | FlipType_Both = 2, // flip in both directions |
98 | | FlipType_Slide = 3 // allow the arrow to "slide" instead of resizing |
99 | | }; |
100 | | |
101 | | enum MenuPopupAnchorType { |
102 | | MenuPopupAnchorType_Node = 0, // anchored to a node |
103 | | MenuPopupAnchorType_Point = 1, // unanchored and positioned at a screen point |
104 | | MenuPopupAnchorType_Rect = 2, // anchored at a screen rectangle |
105 | | }; |
106 | | |
107 | | // values are selected so that the direction can be flipped just by |
108 | | // changing the sign |
109 | 0 | #define POPUPALIGNMENT_NONE 0 |
110 | 0 | #define POPUPALIGNMENT_TOPLEFT 1 |
111 | 0 | #define POPUPALIGNMENT_TOPRIGHT -1 |
112 | 0 | #define POPUPALIGNMENT_BOTTOMLEFT 2 |
113 | 0 | #define POPUPALIGNMENT_BOTTOMRIGHT -2 |
114 | | |
115 | 0 | #define POPUPALIGNMENT_LEFTCENTER 16 |
116 | 0 | #define POPUPALIGNMENT_RIGHTCENTER -16 |
117 | 0 | #define POPUPALIGNMENT_TOPCENTER 17 |
118 | 0 | #define POPUPALIGNMENT_BOTTOMCENTER 18 |
119 | | |
120 | | // The constants here are selected so that horizontally and vertically flipping |
121 | | // can be easily handled using the two flip macros below. |
122 | 0 | #define POPUPPOSITION_UNKNOWN -1 |
123 | 0 | #define POPUPPOSITION_BEFORESTART 0 |
124 | 0 | #define POPUPPOSITION_BEFOREEND 1 |
125 | 0 | #define POPUPPOSITION_AFTERSTART 2 |
126 | 0 | #define POPUPPOSITION_AFTEREND 3 |
127 | 0 | #define POPUPPOSITION_STARTBEFORE 4 |
128 | 0 | #define POPUPPOSITION_ENDBEFORE 5 |
129 | 0 | #define POPUPPOSITION_STARTAFTER 6 |
130 | 0 | #define POPUPPOSITION_ENDAFTER 7 |
131 | 0 | #define POPUPPOSITION_OVERLAP 8 |
132 | 0 | #define POPUPPOSITION_AFTERPOINTER 9 |
133 | 0 | #define POPUPPOSITION_SELECTION 10 |
134 | | |
135 | 0 | #define POPUPPOSITION_HFLIP(v) (v ^ 1) |
136 | 0 | #define POPUPPOSITION_VFLIP(v) (v ^ 2) |
137 | | |
138 | | nsIFrame* NS_NewMenuPopupFrame(nsIPresShell* aPresShell, mozilla::ComputedStyle* aStyle); |
139 | | |
140 | | class nsView; |
141 | | class nsMenuPopupFrame; |
142 | | |
143 | | // this class is used for dispatching popupshown events asynchronously. |
144 | | class nsXULPopupShownEvent final : public mozilla::Runnable, |
145 | | public nsIDOMEventListener |
146 | | { |
147 | | public: |
148 | | nsXULPopupShownEvent(nsIContent* aPopup, nsPresContext* aPresContext) |
149 | | : mozilla::Runnable("nsXULPopupShownEvent") |
150 | | , mPopup(aPopup) |
151 | | , mPresContext(aPresContext) |
152 | 0 | { |
153 | 0 | } |
154 | | |
155 | | NS_DECL_ISUPPORTS_INHERITED |
156 | | NS_DECL_NSIRUNNABLE |
157 | | NS_DECL_NSIDOMEVENTLISTENER |
158 | | |
159 | | void CancelListener(); |
160 | | |
161 | | protected: |
162 | 0 | virtual ~nsXULPopupShownEvent() { } |
163 | | |
164 | | private: |
165 | | nsCOMPtr<nsIContent> mPopup; |
166 | | RefPtr<nsPresContext> mPresContext; |
167 | | }; |
168 | | |
169 | | class nsMenuPopupFrame final : public nsBoxFrame, public nsMenuParent, |
170 | | public nsIReflowCallback |
171 | | { |
172 | | public: |
173 | | NS_DECL_QUERYFRAME |
174 | | NS_DECL_FRAMEARENA_HELPERS(nsMenuPopupFrame) |
175 | | |
176 | | explicit nsMenuPopupFrame(ComputedStyle* aStyle); |
177 | | |
178 | | // nsMenuParent interface |
179 | | virtual nsMenuFrame* GetCurrentMenuItem() override; |
180 | | NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem) override; |
181 | | virtual void CurrentMenuIsBeingDestroyed() override; |
182 | | NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, |
183 | | bool aSelectFirstItem, |
184 | | bool aFromKey) override; |
185 | | |
186 | | // as popups are opened asynchronously, the popup pending state is used to |
187 | | // prevent multiple requests from attempting to open the same popup twice |
188 | 0 | nsPopupState PopupState() { return mPopupState; } |
189 | 0 | void SetPopupState(nsPopupState aPopupState) { mPopupState = aPopupState; } |
190 | | |
191 | 0 | NS_IMETHOD SetActive(bool aActiveFlag) override { return NS_OK; } // We don't care. |
192 | 0 | virtual bool IsActive() override { return false; } |
193 | 0 | virtual bool IsMenuBar() override { return false; } |
194 | | |
195 | | /* |
196 | | * When this popup is open, should clicks outside of it be consumed? |
197 | | * Return true if the popup should rollup on an outside click, |
198 | | * but consume that click so it can't be used for anything else. |
199 | | * Return false to allow clicks outside the popup to activate content |
200 | | * even when the popup is open. |
201 | | * --------------------------------------------------------------------- |
202 | | * |
203 | | * Should clicks outside of a popup be eaten? |
204 | | * |
205 | | * Menus Autocomplete Comboboxes |
206 | | * Mac Eat No Eat |
207 | | * Win No No Eat |
208 | | * Unix Eat No Eat |
209 | | * |
210 | | */ |
211 | | ConsumeOutsideClicksResult ConsumeOutsideClicks(); |
212 | | |
213 | 0 | virtual bool IsContextMenu() override { return mIsContextMenu; } |
214 | | |
215 | 0 | virtual bool MenuClosed() override { return true; } |
216 | | |
217 | | virtual void LockMenuUntilClosed(bool aLock) override; |
218 | 0 | virtual bool IsMenuLocked() override { return mIsMenuLocked; } |
219 | | |
220 | | nsIWidget* GetWidget(); |
221 | | |
222 | | // Overridden methods |
223 | | virtual void Init(nsIContent* aContent, |
224 | | nsContainerFrame* aParent, |
225 | | nsIFrame* aPrevInFlow) override; |
226 | | |
227 | | virtual nsresult AttributeChanged(int32_t aNameSpaceID, |
228 | | nsAtom* aAttribute, |
229 | | int32_t aModType) override; |
230 | | |
231 | | virtual void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) override; |
232 | | |
233 | | bool HasRemoteContent() const; |
234 | | |
235 | | // returns true if the popup is a panel with the noautohide attribute set to |
236 | | // true. These panels do not roll up automatically. |
237 | | bool IsNoAutoHide() const; |
238 | | |
239 | | nsPopupLevel PopupLevel() const |
240 | 0 | { |
241 | 0 | return PopupLevel(IsNoAutoHide()); |
242 | 0 | } |
243 | | |
244 | | // Ensure that a widget has already been created for this view, and create |
245 | | // one if it hasn't. If aRecreate is true, destroys any existing widget and |
246 | | // creates a new one, regardless of whether one has already been created. |
247 | | void EnsureWidget(bool aRecreate = false); |
248 | | |
249 | | nsresult CreateWidgetForView(nsView* aView); |
250 | | uint8_t GetShadowStyle(); |
251 | | |
252 | | virtual void SetInitialChildList(ChildListID aListID, |
253 | | nsFrameList& aChildList) override; |
254 | | |
255 | | virtual bool IsLeafDynamic() const override; |
256 | | |
257 | | virtual void UpdateWidgetProperties() override; |
258 | | |
259 | | // layout, position and display the popup as needed |
260 | | void LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu, |
261 | | nsIFrame* aAnchor, bool aSizedToPopup); |
262 | | |
263 | | nsView* GetRootViewForPopup(nsIFrame* aStartFrame); |
264 | | |
265 | | // Set the position of the popup either relative to the anchor aAnchorFrame |
266 | | // (or the frame for mAnchorContent if aAnchorFrame is null), anchored at a |
267 | | // rectangle, or at a specific point if a screen position is set. The popup |
268 | | // will be adjusted so that it is on screen. If aIsMove is true, then the |
269 | | // popup is being moved, and should not be flipped. If aNotify is true, then |
270 | | // a popuppositioned event is sent. |
271 | | nsresult SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, |
272 | | bool aSizedToPopup, bool aNotify); |
273 | | |
274 | 0 | bool HasGeneratedChildren() { return mGeneratedChildren; } |
275 | 0 | void SetGeneratedChildren() { mGeneratedChildren = true; } |
276 | | |
277 | | // called when the Enter key is pressed while the popup is open. This will |
278 | | // just pass the call down to the current menu, if any. If a current menu |
279 | | // should be opened as a result, this method should return the frame for |
280 | | // that menu, or null if no menu should be opened. Also, calling Enter will |
281 | | // reset the current incremental search string, calculated in |
282 | | // FindMenuWithShortcut. |
283 | | nsMenuFrame* Enter(mozilla::WidgetGUIEvent* aEvent); |
284 | | |
285 | 0 | nsPopupType PopupType() const { return mPopupType; } |
286 | 0 | bool IsMenu() override { return mPopupType == ePopupTypeMenu; } |
287 | 0 | bool IsOpen() override { return mPopupState == ePopupOpening || |
288 | 0 | mPopupState == ePopupVisible || |
289 | 0 | mPopupState == ePopupShown; } |
290 | 0 | bool IsVisible() { return mPopupState == ePopupVisible || |
291 | 0 | mPopupState == ePopupShown; } |
292 | | |
293 | | // Return true if the popup is for a menulist. |
294 | | bool IsMenuList(); |
295 | | |
296 | 0 | bool IsMouseTransparent() { return mMouseTransparent; } |
297 | | |
298 | | static nsIContent* GetTriggerContent(nsMenuPopupFrame* aMenuPopupFrame); |
299 | 0 | void ClearTriggerContent() { mTriggerContent = nullptr; } |
300 | | |
301 | | // returns true if the popup is in a content shell, or false for a popup in |
302 | | // a chrome shell |
303 | 0 | bool IsInContentShell() { return mInContentShell; } |
304 | | |
305 | | // the Initialize methods are used to set the anchor position for |
306 | | // each way of opening a popup. |
307 | | void InitializePopup(nsIContent* aAnchorContent, |
308 | | nsIContent* aTriggerContent, |
309 | | const nsAString& aPosition, |
310 | | int32_t aXPos, int32_t aYPos, |
311 | | MenuPopupAnchorType aAnchorType, |
312 | | bool aAttributesOverride); |
313 | | |
314 | | void InitializePopupAtRect(nsIContent* aTriggerContent, |
315 | | const nsAString& aPosition, |
316 | | const nsIntRect& aRect, |
317 | | bool aAttributesOverride); |
318 | | |
319 | | /** |
320 | | * @param aIsContextMenu if true, then the popup is |
321 | | * positioned at a slight offset from aXPos/aYPos to ensure the |
322 | | * (presumed) mouse position is not over the menu. |
323 | | */ |
324 | | void InitializePopupAtScreen(nsIContent* aTriggerContent, |
325 | | int32_t aXPos, int32_t aYPos, |
326 | | bool aIsContextMenu); |
327 | | |
328 | | // indicate that the popup should be opened |
329 | | void ShowPopup(bool aIsContextMenu); |
330 | | // indicate that the popup should be hidden. The new state should either be |
331 | | // ePopupClosed or ePopupInvisible. |
332 | | void HidePopup(bool aDeselectMenu, nsPopupState aNewState); |
333 | | |
334 | | // locate and return the menu frame that should be activated for the |
335 | | // supplied key event. If doAction is set to true by this method, |
336 | | // then the menu's action should be carried out, as if the user had pressed |
337 | | // the Enter key. If doAction is false, the menu should just be highlighted. |
338 | | // This method also handles incremental searching in menus so the user can |
339 | | // type the first few letters of an item/s name to select it. |
340 | | nsMenuFrame* FindMenuWithShortcut(mozilla::dom::KeyboardEvent* aKeyEvent, |
341 | | bool& doAction); |
342 | | |
343 | 0 | void ClearIncrementalString() { mIncrementalString.Truncate(); } |
344 | 0 | static bool IsWithinIncrementalTime(DOMTimeStamp time) { |
345 | 0 | return !sTimeoutOfIncrementalSearch || time - sLastKeyTime <= sTimeoutOfIncrementalSearch; |
346 | 0 | } |
347 | | |
348 | | #ifdef DEBUG_FRAME_DUMP |
349 | | virtual nsresult GetFrameName(nsAString& aResult) const override |
350 | | { |
351 | | return MakeFrameName(NS_LITERAL_STRING("MenuPopup"), aResult); |
352 | | } |
353 | | #endif |
354 | | |
355 | | void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame); |
356 | | |
357 | | void ChangeByPage(bool aIsUp); |
358 | | |
359 | | // Move the popup to the screen coordinate |aPos| in CSS pixels. |
360 | | // If aUpdateAttrs is true, and the popup already has left or top attributes, |
361 | | // then those attributes are updated to the new location. |
362 | | // The frame may be destroyed by this method. |
363 | | void MoveTo(const mozilla::CSSIntPoint& aPos, bool aUpdateAttrs); |
364 | | |
365 | | void MoveToAnchor(nsIContent* aAnchorContent, |
366 | | const nsAString& aPosition, |
367 | | int32_t aXPos, int32_t aYPos, |
368 | | bool aAttributesOverride); |
369 | | |
370 | | bool GetAutoPosition(); |
371 | | void SetAutoPosition(bool aShouldAutoPosition); |
372 | | |
373 | | nsIScrollableFrame* GetScrollFrame(nsIFrame* aStart); |
374 | | |
375 | 0 | void SetOverrideConstraintRect(mozilla::LayoutDeviceIntRect aRect) { |
376 | 0 | mOverrideConstraintRect = ToAppUnits(aRect, mozilla::AppUnitsPerCSSPixel()); |
377 | 0 | } |
378 | | |
379 | | // For a popup that should appear anchored at the given rect, determine |
380 | | // the screen area that it is constrained by. This will be the available |
381 | | // area of the screen the popup should be displayed on. Content popups, |
382 | | // however, will also be constrained by the content area, given by |
383 | | // aRootScreenRect. All coordinates are in app units. |
384 | | // For non-toplevel popups (which will always be panels), we will also |
385 | | // constrain them to the available screen rect, ie they will not fall |
386 | | // underneath the taskbar, dock or other fixed OS elements. |
387 | | // This operates in device pixels. |
388 | | mozilla::LayoutDeviceIntRect |
389 | | GetConstraintRect(const mozilla::LayoutDeviceIntRect& aAnchorRect, |
390 | | const mozilla::LayoutDeviceIntRect& aRootScreenRect, |
391 | | nsPopupLevel aPopupLevel); |
392 | | |
393 | | // Determines whether the given edges of the popup may be moved, where |
394 | | // aHorizontalSide and aVerticalSide are one of the enum Side constants. |
395 | | // aChange is the distance to move on those sides. If will be reset to 0 |
396 | | // if the side cannot be adjusted at all in that direction. For example, a |
397 | | // popup cannot be moved if it is anchored on a particular side. |
398 | | // |
399 | | // Later, when bug 357725 is implemented, we can make this adjust aChange by |
400 | | // the amount that the side can be resized, so that minimums and maximums |
401 | | // can be taken into account. |
402 | | void CanAdjustEdges(mozilla::Side aHorizontalSide, |
403 | | mozilla::Side aVerticalSide, |
404 | | mozilla::LayoutDeviceIntPoint& aChange); |
405 | | |
406 | | // Return true if the popup is positioned relative to an anchor. |
407 | 0 | bool IsAnchored() const { return mAnchorType != MenuPopupAnchorType_Point; } |
408 | | |
409 | | // Return the anchor if there is one. |
410 | 0 | nsIContent* GetAnchor() const { return mAnchorContent; } |
411 | | |
412 | | // Return the screen coordinates in CSS pixels of the popup, |
413 | | // or (-1, -1, 0, 0) if anchored. |
414 | 0 | nsIntRect GetScreenAnchorRect() const { return mScreenRect; } |
415 | | |
416 | | mozilla::LayoutDeviceIntPoint GetLastClientOffset() const |
417 | 0 | { |
418 | 0 | return mLastClientOffset; |
419 | 0 | } |
420 | | |
421 | | // Return the alignment of the popup |
422 | | int8_t GetAlignmentPosition() const; |
423 | | |
424 | | // Return the offset applied to the alignment of the popup |
425 | 0 | nscoord GetAlignmentOffset() const { return mAlignmentOffset; } |
426 | | |
427 | | // Clear the mPopupShownDispatcher, remove the listener and return true if |
428 | | // mPopupShownDispatcher was non-null. |
429 | | bool ClearPopupShownDispatcher() |
430 | 0 | { |
431 | 0 | if (mPopupShownDispatcher) { |
432 | 0 | mPopupShownDispatcher->CancelListener(); |
433 | 0 | mPopupShownDispatcher = nullptr; |
434 | 0 | return true; |
435 | 0 | } |
436 | 0 | |
437 | 0 | return false; |
438 | 0 | } |
439 | | |
440 | 0 | void ShowWithPositionedEvent() { |
441 | 0 | mPopupState = ePopupPositioning; |
442 | 0 | mShouldAutoPosition = true; |
443 | 0 | } |
444 | | |
445 | | // Checks for the anchor to change and either moves or hides the popup |
446 | | // accordingly. The original position of the anchor should be supplied as |
447 | | // the argument. If the popup needs to be hidden, HidePopup will be called by |
448 | | // CheckForAnchorChange. If the popup needs to be moved, aRect will be updated |
449 | | // with the new rectangle. |
450 | | void CheckForAnchorChange(nsRect& aRect); |
451 | | |
452 | | // nsIReflowCallback |
453 | | virtual bool ReflowFinished() override; |
454 | | virtual void ReflowCallbackCanceled() override; |
455 | | |
456 | | protected: |
457 | | |
458 | | // returns the popup's level. |
459 | | nsPopupLevel PopupLevel(bool aIsNoAutoHide) const; |
460 | | |
461 | | // redefine to tell the box system not to move the views. |
462 | | virtual uint32_t GetXULLayoutFlags() override; |
463 | | |
464 | | void InitPositionFromAnchorAlign(const nsAString& aAnchor, |
465 | | const nsAString& aAlign); |
466 | | |
467 | | // return the position where the popup should be, when it should be |
468 | | // anchored at anchorRect. aHFlip and aVFlip will be set if the popup may be |
469 | | // flipped in that direction if there is not enough space available. |
470 | | nsPoint AdjustPositionForAnchorAlign(nsRect& anchorRect, |
471 | | FlipStyle& aHFlip, FlipStyle& aVFlip); |
472 | | |
473 | | // For popups that are going to align to their selected item, get the frame of |
474 | | // the selected item. |
475 | | nsIFrame* GetSelectedItemForAlignment(); |
476 | | |
477 | | // check if the popup will fit into the available space and resize it. This |
478 | | // method handles only one axis at a time so is called twice, once for |
479 | | // horizontal and once for vertical. All arguments are specified for this |
480 | | // one axis. All coordinates are in app units relative to the screen. |
481 | | // aScreenPoint - the point where the popup should appear |
482 | | // aSize - the size of the popup |
483 | | // aScreenBegin - the left or top edge of the screen |
484 | | // aScreenEnd - the right or bottom edge of the screen |
485 | | // aAnchorBegin - the left or top edge of the anchor rectangle |
486 | | // aAnchorEnd - the right or bottom edge of the anchor rectangle |
487 | | // aMarginBegin - the left or top margin of the popup |
488 | | // aMarginEnd - the right or bottom margin of the popup |
489 | | // aOffsetForContextMenu - the additional offset to add for context menus |
490 | | // aFlip - how to flip or resize the popup when there isn't space |
491 | | // aFlipSide - pointer to where current flip mode is stored |
492 | | nscoord FlipOrResize(nscoord& aScreenPoint, nscoord aSize, |
493 | | nscoord aScreenBegin, nscoord aScreenEnd, |
494 | | nscoord aAnchorBegin, nscoord aAnchorEnd, |
495 | | nscoord aMarginBegin, nscoord aMarginEnd, |
496 | | nscoord aOffsetForContextMenu, FlipStyle aFlip, |
497 | | bool aIsOnEnd, bool* aFlipSide); |
498 | | |
499 | | // check if the popup can fit into the available space by "sliding" (i.e., |
500 | | // by having the anchor arrow slide along one axis and only resizing if that |
501 | | // can't provide the requested size). Only one axis can be slid - the other |
502 | | // axis is "flipped" as normal. This method can handle either axis, but is |
503 | | // only called for the sliding axis. All coordinates are in app units |
504 | | // relative to the screen. |
505 | | // aScreenPoint - the point where the popup should appear |
506 | | // aSize - the size of the popup |
507 | | // aScreenBegin - the left or top edge of the screen |
508 | | // aScreenEnd - the right or bottom edge of the screen |
509 | | // aOffset - the amount by which the arrow must be slid such that it is |
510 | | // still aligned with the anchor. |
511 | | // Result is the new size of the popup, which will typically be the same |
512 | | // as aSize, unless aSize is greater than the screen width/height. |
513 | | nscoord SlideOrResize(nscoord& aScreenPoint, nscoord aSize, |
514 | | nscoord aScreenBegin, nscoord aScreenEnd, |
515 | | nscoord *aOffset); |
516 | | |
517 | | // Given an anchor frame, compute the anchor rectangle relative to the screen, |
518 | | // using the popup frame's app units, and taking into account transforms. |
519 | | nsRect ComputeAnchorRect(nsPresContext* aRootPresContext, nsIFrame* aAnchorFrame); |
520 | | |
521 | | // Move the popup to the position specified in its |left| and |top| attributes. |
522 | | void MoveToAttributePosition(); |
523 | | |
524 | | /** |
525 | | * Return whether the popup direction should be RTL. |
526 | | * If the popup has an anchor, its direction is the anchor direction. |
527 | | * Otherwise, its the general direction of the UI. |
528 | | * |
529 | | * Return whether the popup direction should be RTL. |
530 | | */ |
531 | 0 | bool IsDirectionRTL() const { |
532 | 0 | return mAnchorContent && mAnchorContent->GetPrimaryFrame() |
533 | 0 | ? mAnchorContent->GetPrimaryFrame()->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL |
534 | 0 | : StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
535 | 0 | } |
536 | | |
537 | | // Create a popup view for this frame. The view is added a child of the root |
538 | | // view, and is initially hidden. |
539 | | void CreatePopupView(); |
540 | | |
541 | 0 | nsView* GetViewInternal() const override { return mView; } |
542 | 0 | void SetViewInternal(nsView* aView) override { mView = aView; } |
543 | | |
544 | | // Returns true if the popup should try to remain at the same relative |
545 | | // location as the anchor while it is open. If the anchor becomes hidden |
546 | | // either directly or indirectly because a parent popup or other element |
547 | | // is no longer visible, or a parent deck page is changed, the popup hides |
548 | | // as well. The second variation also sets the anchor rectangle, relative to |
549 | | // the popup frame. |
550 | | bool ShouldFollowAnchor(); |
551 | | public: |
552 | | bool ShouldFollowAnchor(nsRect& aRect); |
553 | | |
554 | | protected: |
555 | | nsString mIncrementalString; // for incremental typing navigation |
556 | | |
557 | | // the content that the popup is anchored to, if any, which may be in a |
558 | | // different document than the popup. |
559 | | nsCOMPtr<nsIContent> mAnchorContent; |
560 | | |
561 | | // the content that triggered the popup, typically the node where the mouse |
562 | | // was clicked. It will be cleared when the popup is hidden. |
563 | | nsCOMPtr<nsIContent> mTriggerContent; |
564 | | |
565 | | nsMenuFrame* mCurrentMenu; // The current menu that is active. |
566 | | nsView* mView; |
567 | | |
568 | | RefPtr<nsXULPopupShownEvent> mPopupShownDispatcher; |
569 | | |
570 | | // The popup's screen rectangle in app units. |
571 | | nsIntRect mUsedScreenRect; |
572 | | |
573 | | // A popup's preferred size may be different than its actual size stored in |
574 | | // mRect in the case where the popup was resized because it was too large |
575 | | // for the screen. The preferred size mPrefSize holds the full size the popup |
576 | | // would be before resizing. Computations are performed using this size. |
577 | | nsSize mPrefSize; |
578 | | |
579 | | // The position of the popup, in CSS pixels. |
580 | | // The screen coordinates, if set to values other than -1, |
581 | | // override mXPos and mYPos. |
582 | | int32_t mXPos; |
583 | | int32_t mYPos; |
584 | | nsIntRect mScreenRect; |
585 | | |
586 | | // If the panel prefers to "slide" rather than resize, then the arrow gets |
587 | | // positioned at this offset (along either the x or y axis, depending on |
588 | | // mPosition) |
589 | | nscoord mAlignmentOffset; |
590 | | |
591 | | // The value of the client offset of our widget the last time we positioned |
592 | | // ourselves. We store this so that we can detect when it changes but the |
593 | | // position of our widget didn't change. |
594 | | mozilla::LayoutDeviceIntPoint mLastClientOffset; |
595 | | |
596 | | nsPopupType mPopupType; // type of popup |
597 | | nsPopupState mPopupState; // open state of the popup |
598 | | |
599 | | // popup alignment relative to the anchor node |
600 | | int8_t mPopupAlignment; |
601 | | int8_t mPopupAnchor; |
602 | | int8_t mPosition; |
603 | | |
604 | | FlipType mFlip; // Whether to flip |
605 | | |
606 | | struct ReflowCallbackData { |
607 | | ReflowCallbackData() : |
608 | | mPosted(false), |
609 | | mAnchor(nullptr), |
610 | | mSizedToPopup(false), |
611 | | mIsOpenChanged(false) |
612 | 0 | {} |
613 | 0 | void MarkPosted(nsIFrame* aAnchor, bool aSizedToPopup, bool aIsOpenChanged) { |
614 | 0 | mPosted = true; |
615 | 0 | mAnchor = aAnchor; |
616 | 0 | mSizedToPopup = aSizedToPopup; |
617 | 0 | mIsOpenChanged = aIsOpenChanged; |
618 | 0 | } |
619 | 0 | void Clear() { |
620 | 0 | mPosted = false; |
621 | 0 | mAnchor = nullptr; |
622 | 0 | mSizedToPopup = false; |
623 | 0 | mIsOpenChanged = false; |
624 | 0 | } |
625 | | bool mPosted; |
626 | | nsIFrame* mAnchor; |
627 | | bool mSizedToPopup; |
628 | | bool mIsOpenChanged; |
629 | | }; |
630 | | ReflowCallbackData mReflowCallbackData; |
631 | | |
632 | | bool mIsOpenChanged; // true if the open state changed since the last layout |
633 | | bool mIsContextMenu; // true for context menus |
634 | | // true if we need to offset the popup to ensure it's not under the mouse |
635 | | bool mAdjustOffsetForContextMenu; |
636 | | bool mGeneratedChildren; // true if the contents have been created |
637 | | |
638 | | bool mMenuCanOverlapOSBar; // can we appear over the taskbar/menubar? |
639 | | bool mShouldAutoPosition; // Should SetPopupPosition be allowed to auto position popup? |
640 | | bool mInContentShell; // True if the popup is in a content shell |
641 | | bool mIsMenuLocked; // Should events inside this menu be ignored? |
642 | | bool mMouseTransparent; // True if this is a popup is transparent to mouse events |
643 | | |
644 | | // True if this popup has been offset due to moving off / near the edge of the screen. |
645 | | // (This is useful for ensuring that a move, which can't offset the popup, doesn't undo |
646 | | // a previously set offset.) |
647 | | bool mIsOffset; |
648 | | |
649 | | // the flip modes that were used when the popup was opened |
650 | | bool mHFlip; |
651 | | bool mVFlip; |
652 | | |
653 | | // When POPUPPOSITION_SELECTION is used, this indicates the vertical offset that the |
654 | | // original selected item was. This needs to be used in case the popup gets changed |
655 | | // so that we can keep the popup at the same vertical offset. |
656 | | nscoord mPositionedOffset; |
657 | | |
658 | | // How the popup is anchored. |
659 | | MenuPopupAnchorType mAnchorType; |
660 | | |
661 | | nsRect mOverrideConstraintRect; |
662 | | |
663 | | static int8_t sDefaultLevelIsTop; |
664 | | |
665 | | static DOMTimeStamp sLastKeyTime; |
666 | | |
667 | | // If 0, never timed out. Otherwise, the value is in milliseconds. |
668 | | static uint32_t sTimeoutOfIncrementalSearch; |
669 | | }; // class nsMenuPopupFrame |
670 | | |
671 | | #endif |