Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / layout / xul / nsMenuPopupFrame.h
bloba033b2769da0766cd10be4fbc5b89e43f1990dda
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/. */
6 //
7 // nsMenuPopupFrame
8 //
10 #ifndef nsMenuPopupFrame_h__
11 #define nsMenuPopupFrame_h__
13 #include "mozilla/Attributes.h"
14 #include "nsIAtom.h"
15 #include "nsGkAtoms.h"
16 #include "nsCOMPtr.h"
17 #include "nsMenuFrame.h"
19 #include "nsBoxFrame.h"
20 #include "nsMenuParent.h"
22 #include "nsITimer.h"
24 class nsIWidget;
26 // XUL popups can be in several different states. When opening a popup, the
27 // state changes as follows:
28 // ePopupClosed - initial state
29 // ePopupShowing - during the period when the popupshowing event fires
30 // ePopupOpening - between the popupshowing event and being visible. Creation
31 // of the child frames, layout and reflow occurs in this
32 // state. The popup is stored in the popup manager's list of
33 // open popups during this state.
34 // ePopupVisible - layout is done and the popup's view and widget are made
35 // visible. The popup is visible on screen but may be
36 // transitioning. The popupshown event has not yet fired.
37 // ePopupShown - the popup has been shown and is fully ready. This state is
38 // assigned just before the popupshown event fires.
39 // When closing a popup:
40 // ePopupHidden - during the period when the popuphiding event fires and
41 // the popup is removed.
42 // ePopupClosed - the popup's widget is made invisible.
43 enum nsPopupState {
44 // state when a popup is not open
45 ePopupClosed,
46 // state from when a popup is requested to be shown to after the
47 // popupshowing event has been fired.
48 ePopupShowing,
49 // state while a popup is open but the widget is not yet visible
50 ePopupOpening,
51 // state while a popup is visible and waiting for the popupshown event
52 ePopupVisible,
53 // state while a popup is open and visible on screen
54 ePopupShown,
55 // state from when a popup is requested to be hidden to when it is closed.
56 ePopupHiding,
57 // state which indicates that the popup was hidden without firing the
58 // popuphiding or popuphidden events. It is used when executing a menu
59 // command because the menu needs to be hidden before the command event
60 // fires, yet the popuphiding and popuphidden events are fired after. This
61 // state can also occur when the popup is removed because the document is
62 // unloaded.
63 ePopupInvisible
66 // How a popup may be flipped. Flipping to the outside edge is like how
67 // a submenu would work. The entire popup is flipped to the opposite side
68 // of the anchor.
69 enum FlipStyle {
70 FlipStyle_None = 0,
71 FlipStyle_Outside = 1,
72 FlipStyle_Inside = 2
75 // Values for the flip attribute
76 enum FlipType {
77 FlipType_Default = 0,
78 FlipType_None = 1, // don't try to flip or translate to stay onscreen
79 FlipType_Both = 2, // flip in both directions
80 FlipType_Slide = 3 // allow the arrow to "slide" instead of resizing
83 // values are selected so that the direction can be flipped just by
84 // changing the sign
85 #define POPUPALIGNMENT_NONE 0
86 #define POPUPALIGNMENT_TOPLEFT 1
87 #define POPUPALIGNMENT_TOPRIGHT -1
88 #define POPUPALIGNMENT_BOTTOMLEFT 2
89 #define POPUPALIGNMENT_BOTTOMRIGHT -2
91 #define POPUPALIGNMENT_LEFTCENTER 16
92 #define POPUPALIGNMENT_RIGHTCENTER -16
93 #define POPUPALIGNMENT_TOPCENTER 17
94 #define POPUPALIGNMENT_BOTTOMCENTER 18
96 // The constants here are selected so that horizontally and vertically flipping
97 // can be easily handled using the two flip macros below.
98 #define POPUPPOSITION_UNKNOWN -1
99 #define POPUPPOSITION_BEFORESTART 0
100 #define POPUPPOSITION_BEFOREEND 1
101 #define POPUPPOSITION_AFTERSTART 2
102 #define POPUPPOSITION_AFTEREND 3
103 #define POPUPPOSITION_STARTBEFORE 4
104 #define POPUPPOSITION_ENDBEFORE 5
105 #define POPUPPOSITION_STARTAFTER 6
106 #define POPUPPOSITION_ENDAFTER 7
107 #define POPUPPOSITION_OVERLAP 8
108 #define POPUPPOSITION_AFTERPOINTER 9
110 #define POPUPPOSITION_HFLIP(v) (v ^ 1)
111 #define POPUPPOSITION_VFLIP(v) (v ^ 2)
113 #define INC_TYP_INTERVAL 1000 // 1s. If the interval between two keypresses is shorter than this,
114 // treat as a continue typing
115 // XXX, kyle.yuan@sun.com, there are 4 definitions for the same purpose:
116 // nsMenuPopupFrame.h, nsListControlFrame.cpp, listbox.xml, tree.xml
117 // need to find a good place to put them together.
118 // if someone changes one, please also change the other.
120 #define CONTEXT_MENU_OFFSET_PIXELS 2
122 nsIFrame* NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
124 class nsViewManager;
125 class nsView;
126 class nsMenuPopupFrame;
128 // this class is used for dispatching popupshown events asynchronously.
129 class nsXULPopupShownEvent : public nsRunnable, public nsIDOMEventListener
131 public:
132 nsXULPopupShownEvent(nsIContent *aPopup, nsPresContext* aPresContext)
133 : mPopup(aPopup), mPresContext(aPresContext)
137 NS_DECL_ISUPPORTS_INHERITED
138 NS_DECL_NSIRUNNABLE
139 NS_DECL_NSIDOMEVENTLISTENER
141 void CancelListener();
143 protected:
144 virtual ~nsXULPopupShownEvent() { }
146 private:
147 nsCOMPtr<nsIContent> mPopup;
148 nsRefPtr<nsPresContext> mPresContext;
151 class nsMenuPopupFrame MOZ_FINAL : public nsBoxFrame, public nsMenuParent
153 public:
154 NS_DECL_QUERYFRAME_TARGET(nsMenuPopupFrame)
155 NS_DECL_QUERYFRAME
156 NS_DECL_FRAMEARENA_HELPERS
158 nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext);
160 // nsMenuParent interface
161 virtual nsMenuFrame* GetCurrentMenuItem() MOZ_OVERRIDE;
162 NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem) MOZ_OVERRIDE;
163 virtual void CurrentMenuIsBeingDestroyed() MOZ_OVERRIDE;
164 NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, bool aSelectFirstItem) MOZ_OVERRIDE;
166 // as popups are opened asynchronously, the popup pending state is used to
167 // prevent multiple requests from attempting to open the same popup twice
168 nsPopupState PopupState() { return mPopupState; }
169 void SetPopupState(nsPopupState aPopupState) { mPopupState = aPopupState; }
171 NS_IMETHOD SetActive(bool aActiveFlag) MOZ_OVERRIDE { return NS_OK; } // We don't care.
172 virtual bool IsActive() MOZ_OVERRIDE { return false; }
173 virtual bool IsMenuBar() MOZ_OVERRIDE { return false; }
176 * When this popup is open, should clicks outside of it be consumed?
177 * Return true if the popup should rollup on an outside click,
178 * but consume that click so it can't be used for anything else.
179 * Return false to allow clicks outside the popup to activate content
180 * even when the popup is open.
181 * ---------------------------------------------------------------------
183 * Should clicks outside of a popup be eaten?
185 * Menus Autocomplete Comboboxes
186 * Mac Eat No Eat
187 * Win No No Eat
188 * Unix Eat No Eat
191 bool ConsumeOutsideClicks();
193 virtual bool IsContextMenu() MOZ_OVERRIDE { return mIsContextMenu; }
195 virtual bool MenuClosed() MOZ_OVERRIDE { return true; }
197 virtual void LockMenuUntilClosed(bool aLock) MOZ_OVERRIDE;
198 virtual bool IsMenuLocked() MOZ_OVERRIDE { return mIsMenuLocked; }
200 nsIWidget* GetWidget();
202 // The dismissal listener gets created and attached to the window.
203 void AttachedDismissalListener();
205 // Overridden methods
206 virtual void Init(nsIContent* aContent,
207 nsContainerFrame* aParent,
208 nsIFrame* aPrevInFlow) MOZ_OVERRIDE;
210 virtual nsresult AttributeChanged(int32_t aNameSpaceID,
211 nsIAtom* aAttribute,
212 int32_t aModType) MOZ_OVERRIDE;
214 virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
216 // returns true if the popup is a panel with the noautohide attribute set to
217 // true. These panels do not roll up automatically.
218 bool IsNoAutoHide() const;
220 nsPopupLevel PopupLevel() const
222 return PopupLevel(IsNoAutoHide());
225 void EnsureWidget();
227 nsresult CreateWidgetForView(nsView* aView);
228 uint8_t GetShadowStyle();
230 virtual void SetInitialChildList(ChildListID aListID,
231 nsFrameList& aChildList) MOZ_OVERRIDE;
233 virtual bool IsLeaf() const MOZ_OVERRIDE;
235 // layout, position and display the popup as needed
236 void LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
237 nsIFrame* aAnchor, bool aSizedToPopup);
239 nsView* GetRootViewForPopup(nsIFrame* aStartFrame);
241 // set the position of the popup either relative to the anchor aAnchorFrame
242 // (or the frame for mAnchorContent if aAnchorFrame is null) or at a specific
243 // point if a screen position (mScreenXPos and mScreenYPos) are set. The popup
244 // will be adjusted so that it is on screen. If aIsMove is true, then the popup
245 // is being moved, and should not be flipped.
246 nsresult SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aSizedToPopup);
248 bool HasGeneratedChildren() { return mGeneratedChildren; }
249 void SetGeneratedChildren() { mGeneratedChildren = true; }
251 // called when the Enter key is pressed while the popup is open. This will
252 // just pass the call down to the current menu, if any. If a current menu
253 // should be opened as a result, this method should return the frame for
254 // that menu, or null if no menu should be opened. Also, calling Enter will
255 // reset the current incremental search string, calculated in
256 // FindMenuWithShortcut.
257 nsMenuFrame* Enter(mozilla::WidgetGUIEvent* aEvent);
259 nsPopupType PopupType() const { return mPopupType; }
260 bool IsMenu() MOZ_OVERRIDE { return mPopupType == ePopupTypeMenu; }
261 bool IsOpen() MOZ_OVERRIDE { return mPopupState == ePopupOpening ||
262 mPopupState == ePopupVisible ||
263 mPopupState == ePopupShown; }
264 bool IsVisible() { return mPopupState == ePopupVisible ||
265 mPopupState == ePopupShown; }
267 bool IsMouseTransparent() { return mMouseTransparent; }
269 static nsIContent* GetTriggerContent(nsMenuPopupFrame* aMenuPopupFrame);
270 void ClearTriggerContent() { mTriggerContent = nullptr; }
272 // returns true if the popup is in a content shell, or false for a popup in
273 // a chrome shell
274 bool IsInContentShell() { return mInContentShell; }
276 // the Initialize methods are used to set the anchor position for
277 // each way of opening a popup.
278 void InitializePopup(nsIContent* aAnchorContent,
279 nsIContent* aTriggerContent,
280 const nsAString& aPosition,
281 int32_t aXPos, int32_t aYPos,
282 bool aAttributesOverride);
285 * @param aIsContextMenu if true, then the popup is
286 * positioned at a slight offset from aXPos/aYPos to ensure the
287 * (presumed) mouse position is not over the menu.
289 void InitializePopupAtScreen(nsIContent* aTriggerContent,
290 int32_t aXPos, int32_t aYPos,
291 bool aIsContextMenu);
293 void InitializePopupWithAnchorAlign(nsIContent* aAnchorContent,
294 nsAString& aAnchor,
295 nsAString& aAlign,
296 int32_t aXPos, int32_t aYPos);
298 // indicate that the popup should be opened
299 void ShowPopup(bool aIsContextMenu, bool aSelectFirstItem);
300 // indicate that the popup should be hidden. The new state should either be
301 // ePopupClosed or ePopupInvisible.
302 void HidePopup(bool aDeselectMenu, nsPopupState aNewState);
304 // locate and return the menu frame that should be activated for the
305 // supplied key event. If doAction is set to true by this method,
306 // then the menu's action should be carried out, as if the user had pressed
307 // the Enter key. If doAction is false, the menu should just be highlighted.
308 // This method also handles incremental searching in menus so the user can
309 // type the first few letters of an item/s name to select it.
310 nsMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, bool& doAction);
312 void ClearIncrementalString() { mIncrementalString.Truncate(); }
314 virtual nsIAtom* GetType() const MOZ_OVERRIDE { return nsGkAtoms::menuPopupFrame; }
316 #ifdef DEBUG_FRAME_DUMP
317 virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE
319 return MakeFrameName(NS_LITERAL_STRING("MenuPopup"), aResult);
321 #endif
323 void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame);
325 // Move the popup to the screen coordinate (aLeft, aTop) in CSS pixels.
326 // If aUpdateAttrs is true, and the popup already has left or top attributes,
327 // then those attributes are updated to the new location.
328 // The frame may be destroyed by this method.
329 void MoveTo(int32_t aLeft, int32_t aTop, bool aUpdateAttrs);
331 void MoveToAnchor(nsIContent* aAnchorContent,
332 const nsAString& aPosition,
333 int32_t aXPos, int32_t aYPos,
334 bool aAttributesOverride);
336 bool GetAutoPosition();
337 void SetAutoPosition(bool aShouldAutoPosition);
338 void SetConsumeRollupEvent(uint32_t aConsumeMode);
340 nsIScrollableFrame* GetScrollFrame(nsIFrame* aStart);
342 // For a popup that should appear anchored at the given rect, determine
343 // the screen area that it is constrained by. This will be the available
344 // area of the screen the popup should be displayed on. Content popups,
345 // however, will also be constrained by the content area, given by
346 // aRootScreenRect. All coordinates are in app units.
347 // For non-toplevel popups (which will always be panels), we will also
348 // constrain them to the available screen rect, ie they will not fall
349 // underneath the taskbar, dock or other fixed OS elements.
350 nsRect GetConstraintRect(const nsRect& aAnchorRect, const nsRect& aRootScreenRect,
351 nsPopupLevel aPopupLevel);
353 // Determines whether the given edges of the popup may be moved, where
354 // aHorizontalSide and aVerticalSide are one of the NS_SIDE_* constants, or
355 // 0 for no movement in that direction. aChange is the distance to move on
356 // those sides. If will be reset to 0 if the side cannot be adjusted at all
357 // in that direction. For example, a popup cannot be moved if it is anchored
358 // on a particular side.
360 // Later, when bug 357725 is implemented, we can make this adjust aChange by
361 // the amount that the side can be resized, so that minimums and maximums
362 // can be taken into account.
363 void CanAdjustEdges(int8_t aHorizontalSide, int8_t aVerticalSide, nsIntPoint& aChange);
365 // Return true if the popup is positioned relative to an anchor.
366 bool IsAnchored() const { return mScreenXPos == -1 && mScreenYPos == -1; }
368 // Return the anchor if there is one.
369 nsIContent* GetAnchor() const { return mAnchorContent; }
371 // Return the screen coordinates of the popup, or (-1, -1) if anchored.
372 // This position is in CSS pixels.
373 nsIntPoint ScreenPosition() const { return nsIntPoint(mScreenXPos, mScreenYPos); }
375 nsIntPoint GetLastClientOffset() const { return mLastClientOffset; }
377 // Return the alignment of the popup
378 int8_t GetAlignmentPosition() const;
380 // Return the offset applied to the alignment of the popup
381 nscoord GetAlignmentOffset() const { return mAlignmentOffset; }
383 // Clear the mPopupShownDispatcher, remove the listener and return true if
384 // mPopupShownDispatcher was non-null.
385 bool ClearPopupShownDispatcher()
387 if (mPopupShownDispatcher) {
388 mPopupShownDispatcher->CancelListener();
389 mPopupShownDispatcher = nullptr;
390 return true;
393 return false;
396 protected:
398 // returns the popup's level.
399 nsPopupLevel PopupLevel(bool aIsNoAutoHide) const;
401 // redefine to tell the box system not to move the views.
402 virtual void GetLayoutFlags(uint32_t& aFlags) MOZ_OVERRIDE;
404 void InitPositionFromAnchorAlign(const nsAString& aAnchor,
405 const nsAString& aAlign);
407 // return the position where the popup should be, when it should be
408 // anchored at anchorRect. aHFlip and aVFlip will be set if the popup may be
409 // flipped in that direction if there is not enough space available.
410 nsPoint AdjustPositionForAnchorAlign(nsRect& anchorRect,
411 FlipStyle& aHFlip, FlipStyle& aVFlip);
413 // check if the popup will fit into the available space and resize it. This
414 // method handles only one axis at a time so is called twice, once for
415 // horizontal and once for vertical. All arguments are specified for this
416 // one axis. All coordinates are in app units relative to the screen.
417 // aScreenPoint - the point where the popup should appear
418 // aSize - the size of the popup
419 // aScreenBegin - the left or top edge of the screen
420 // aScreenEnd - the right or bottom edge of the screen
421 // aAnchorBegin - the left or top edge of the anchor rectangle
422 // aAnchorEnd - the right or bottom edge of the anchor rectangle
423 // aMarginBegin - the left or top margin of the popup
424 // aMarginEnd - the right or bottom margin of the popup
425 // aOffsetForContextMenu - the additional offset to add for context menus
426 // aFlip - how to flip or resize the popup when there isn't space
427 // aFlipSide - pointer to where current flip mode is stored
428 nscoord FlipOrResize(nscoord& aScreenPoint, nscoord aSize,
429 nscoord aScreenBegin, nscoord aScreenEnd,
430 nscoord aAnchorBegin, nscoord aAnchorEnd,
431 nscoord aMarginBegin, nscoord aMarginEnd,
432 nscoord aOffsetForContextMenu, FlipStyle aFlip,
433 bool* aFlipSide);
435 // check if the popup can fit into the available space by "sliding" (i.e.,
436 // by having the anchor arrow slide along one axis and only resizing if that
437 // can't provide the requested size). Only one axis can be slid - the other
438 // axis is "flipped" as normal. This method can handle either axis, but is
439 // only called for the sliding axis. All coordinates are in app units
440 // relative to the screen.
441 // aScreenPoint - the point where the popup should appear
442 // aSize - the size of the popup
443 // aScreenBegin - the left or top edge of the screen
444 // aScreenEnd - the right or bottom edge of the screen
445 // aOffset - the amount by which the arrow must be slid such that it is
446 // still aligned with the anchor.
447 // Result is the new size of the popup, which will typically be the same
448 // as aSize, unless aSize is greater than the screen width/height.
449 nscoord SlideOrResize(nscoord& aScreenPoint, nscoord aSize,
450 nscoord aScreenBegin, nscoord aScreenEnd,
451 nscoord *aOffset);
453 // Move the popup to the position specified in its |left| and |top| attributes.
454 void MoveToAttributePosition();
457 * Return whether the popup direction should be RTL.
458 * If the popup has an anchor, its direction is the anchor direction.
459 * Otherwise, its the general direction of the UI.
461 * Return whether the popup direction should be RTL.
463 bool IsDirectionRTL() const {
464 return mAnchorContent && mAnchorContent->GetPrimaryFrame()
465 ? mAnchorContent->GetPrimaryFrame()->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL
466 : StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
469 // Create a popup view for this frame. The view is added a child of the root
470 // view, and is initially hidden.
471 void CreatePopupView();
473 nsString mIncrementalString; // for incremental typing navigation
475 // the content that the popup is anchored to, if any, which may be in a
476 // different document than the popup.
477 nsCOMPtr<nsIContent> mAnchorContent;
479 // the content that triggered the popup, typically the node where the mouse
480 // was clicked. It will be cleared when the popup is hidden.
481 nsCOMPtr<nsIContent> mTriggerContent;
483 nsMenuFrame* mCurrentMenu; // The current menu that is active.
485 nsRefPtr<nsXULPopupShownEvent> mPopupShownDispatcher;
487 // A popup's preferred size may be different than its actual size stored in
488 // mRect in the case where the popup was resized because it was too large
489 // for the screen. The preferred size mPrefSize holds the full size the popup
490 // would be before resizing. Computations are performed using this size.
491 nsSize mPrefSize;
493 // The position of the popup, in CSS pixels.
494 // The screen coordinates, if set to values other than -1,
495 // override mXPos and mYPos.
496 int32_t mXPos;
497 int32_t mYPos;
498 int32_t mScreenXPos;
499 int32_t mScreenYPos;
501 // If the panel prefers to "slide" rather than resize, then the arrow gets
502 // positioned at this offset (along either the x or y axis, depending on
503 // mPosition)
504 nscoord mAlignmentOffset;
506 // The value of the client offset of our widget the last time we positioned
507 // ourselves. We store this so that we can detect when it changes but the
508 // position of our widget didn't change.
509 nsIntPoint mLastClientOffset;
511 nsPopupType mPopupType; // type of popup
512 nsPopupState mPopupState; // open state of the popup
514 // popup alignment relative to the anchor node
515 int8_t mPopupAlignment;
516 int8_t mPopupAnchor;
517 int8_t mPosition;
519 // One of nsIPopupBoxObject::ROLLUP_DEFAULT/ROLLUP_CONSUME/ROLLUP_NO_CONSUME
520 int8_t mConsumeRollupEvent;
521 FlipType mFlip; // Whether to flip
523 bool mIsOpenChanged; // true if the open state changed since the last layout
524 bool mIsContextMenu; // true for context menus
525 // true if we need to offset the popup to ensure it's not under the mouse
526 bool mAdjustOffsetForContextMenu;
527 bool mGeneratedChildren; // true if the contents have been created
529 bool mMenuCanOverlapOSBar; // can we appear over the taskbar/menubar?
530 bool mShouldAutoPosition; // Should SetPopupPosition be allowed to auto position popup?
531 bool mInContentShell; // True if the popup is in a content shell
532 bool mIsMenuLocked; // Should events inside this menu be ignored?
533 bool mMouseTransparent; // True if this is a popup is transparent to mouse events
535 // the flip modes that were used when the popup was opened
536 bool mHFlip;
537 bool mVFlip;
539 static int8_t sDefaultLevelIsTop;
540 }; // class nsMenuPopupFrame
542 #endif