michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsComboboxControlFrame_h___ michael@0: #define nsComboboxControlFrame_h___ michael@0: michael@0: #ifdef DEBUG_evaughan michael@0: //#define DEBUG_rods michael@0: #endif michael@0: michael@0: #ifdef DEBUG_rods michael@0: //#define DO_REFLOW_DEBUG michael@0: //#define DO_REFLOW_COUNTER michael@0: //#define DO_UNCONSTRAINED_CHECK michael@0: //#define DO_PIXELS michael@0: //#define DO_NEW_REFLOW michael@0: #endif michael@0: michael@0: //Mark used to indicate when onchange has been fired for current combobox item michael@0: #define NS_SKIP_NOTIFY_INDEX -2 michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsBlockFrame.h" michael@0: #include "nsIFormControlFrame.h" michael@0: #include "nsIComboboxControlFrame.h" michael@0: #include "nsIAnonymousContentCreator.h" michael@0: #include "nsISelectControlFrame.h" michael@0: #include "nsIRollupListener.h" michael@0: #include "nsIStatefulFrame.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: class nsStyleContext; michael@0: class nsIListControlFrame; michael@0: class nsComboboxDisplayFrame; michael@0: class nsIDOMEventListener; michael@0: class nsIScrollableFrame; michael@0: michael@0: class nsComboboxControlFrame : public nsBlockFrame, michael@0: public nsIFormControlFrame, michael@0: public nsIComboboxControlFrame, michael@0: public nsIAnonymousContentCreator, michael@0: public nsISelectControlFrame, michael@0: public nsIRollupListener, michael@0: public nsIStatefulFrame michael@0: { michael@0: public: michael@0: friend nsIFrame* NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags); michael@0: friend class nsComboboxDisplayFrame; michael@0: michael@0: nsComboboxControlFrame(nsStyleContext* aContext); michael@0: ~nsComboboxControlFrame(); michael@0: michael@0: NS_DECL_QUERYFRAME michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: // nsIAnonymousContentCreator michael@0: virtual nsresult CreateAnonymousContent(nsTArray& aElements) MOZ_OVERRIDE; michael@0: virtual void AppendAnonymousContentTo(nsBaseContentList& aElements, michael@0: uint32_t aFilter) MOZ_OVERRIDE; michael@0: virtual nsIFrame* CreateFrameFor(nsIContent* aContent) MOZ_OVERRIDE; michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: michael@0: virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult Reflow(nsPresContext* aCX, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult HandleEvent(nsPresContext* aPresContext, michael@0: mozilla::WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) MOZ_OVERRIDE; michael@0: michael@0: virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) MOZ_OVERRIDE; michael@0: michael@0: void PaintFocus(nsRenderingContext& aRenderingContext, nsPoint aPt); michael@0: michael@0: // XXXbz this is only needed to prevent the quirk percent height stuff from michael@0: // leaking out of the combobox. We may be able to get rid of this as more michael@0: // things move to IsFrameOfType. michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE michael@0: { michael@0: return nsBlockFrame::IsFrameOfType(aFlags & michael@0: ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); michael@0: } michael@0: michael@0: virtual nsIScrollableFrame* GetScrollTargetFrame() MOZ_OVERRIDE { michael@0: return do_QueryFrame(mDropdownFrame); michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; michael@0: #endif michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: virtual nsresult SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) MOZ_OVERRIDE; michael@0: virtual const nsFrameList& GetChildList(ChildListID aListID) const MOZ_OVERRIDE; michael@0: virtual void GetChildLists(nsTArray* aLists) const MOZ_OVERRIDE; michael@0: michael@0: virtual nsIFrame* GetContentInsertionFrame() MOZ_OVERRIDE; michael@0: michael@0: // nsIFormControlFrame michael@0: virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) MOZ_OVERRIDE; michael@0: /** michael@0: * Inform the control that it got (or lost) focus. michael@0: * If it lost focus, the dropdown menu will be rolled up if needed, michael@0: * and FireOnChange() will be called. michael@0: * @param aOn true if got focus, false if lost focus. michael@0: * @param aRepaint if true then force repaint (NOTE: we always force repaint currently) michael@0: * @note This method might destroy |this|. michael@0: */ michael@0: virtual void SetFocus(bool aOn, bool aRepaint) MOZ_OVERRIDE; michael@0: michael@0: //nsIComboboxControlFrame michael@0: virtual bool IsDroppedDown() MOZ_OVERRIDE { return mDroppedDown; } michael@0: /** michael@0: * @note This method might destroy |this|. michael@0: */ michael@0: virtual void ShowDropDown(bool aDoDropDown) MOZ_OVERRIDE; michael@0: virtual nsIFrame* GetDropDown() MOZ_OVERRIDE; michael@0: virtual void SetDropDown(nsIFrame* aDropDownFrame) MOZ_OVERRIDE; michael@0: /** michael@0: * @note This method might destroy |this|. michael@0: */ michael@0: virtual void RollupFromList() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Return the available space above and below this frame for michael@0: * placing the drop-down list, and the current 2D translation. michael@0: * Note that either or both can be less than or equal to zero, michael@0: * if both are then the drop-down should be closed. michael@0: */ michael@0: void GetAvailableDropdownSpace(nscoord* aAbove, michael@0: nscoord* aBelow, michael@0: nsPoint* aTranslation); michael@0: virtual int32_t GetIndexOfDisplayArea() MOZ_OVERRIDE; michael@0: /** michael@0: * @note This method might destroy |this|. michael@0: */ michael@0: NS_IMETHOD RedisplaySelectedText() MOZ_OVERRIDE; michael@0: virtual int32_t UpdateRecentIndex(int32_t aIndex) MOZ_OVERRIDE; michael@0: virtual void OnContentReset() MOZ_OVERRIDE; michael@0: michael@0: // nsISelectControlFrame michael@0: NS_IMETHOD AddOption(int32_t index) MOZ_OVERRIDE; michael@0: NS_IMETHOD RemoveOption(int32_t index) MOZ_OVERRIDE; michael@0: NS_IMETHOD DoneAddingChildren(bool aIsDone) MOZ_OVERRIDE; michael@0: NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) MOZ_OVERRIDE; michael@0: NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) MOZ_OVERRIDE; michael@0: michael@0: //nsIRollupListener michael@0: /** michael@0: * Hide the dropdown menu and stop capturing mouse events. michael@0: * @note This method might destroy |this|. michael@0: */ michael@0: virtual bool Rollup(uint32_t aCount, const nsIntPoint* pos, nsIContent** aLastRolledUp) MOZ_OVERRIDE; michael@0: virtual void NotifyGeometryChange() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * A combobox should roll up if a mousewheel event happens outside of michael@0: * the popup area. michael@0: */ michael@0: virtual bool ShouldRollupOnMouseWheelEvent() MOZ_OVERRIDE michael@0: { return true; } michael@0: michael@0: virtual bool ShouldConsumeOnMouseWheelEvent() MOZ_OVERRIDE michael@0: { return false; } michael@0: michael@0: /** michael@0: * A combobox should not roll up if activated by a mouse activate message michael@0: * (eg. X-mouse). michael@0: */ michael@0: virtual bool ShouldRollupOnMouseActivate() MOZ_OVERRIDE michael@0: { return false; } michael@0: michael@0: virtual uint32_t GetSubmenuWidgetChain(nsTArray *aWidgetChain) MOZ_OVERRIDE michael@0: { return 0; } michael@0: michael@0: virtual nsIWidget* GetRollupWidget() MOZ_OVERRIDE; michael@0: michael@0: //nsIStatefulFrame michael@0: NS_IMETHOD SaveState(nsPresState** aState) MOZ_OVERRIDE; michael@0: NS_IMETHOD RestoreState(nsPresState* aState) MOZ_OVERRIDE; michael@0: michael@0: static bool ToolkitHasNativePopup(); michael@0: michael@0: protected: michael@0: friend class RedisplayTextEvent; michael@0: friend class nsAsyncResize; michael@0: friend class nsResizeDropdownAtFinalPosition; michael@0: michael@0: // Utilities michael@0: nsresult ReflowDropdown(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState& aReflowState); michael@0: michael@0: enum DropDownPositionState { michael@0: // can't show the dropdown at its current position michael@0: eDropDownPositionSuppressed, michael@0: // a resize reflow is pending, don't show it yet michael@0: eDropDownPositionPendingResize, michael@0: // the dropdown has its final size and position and can be displayed here michael@0: eDropDownPositionFinal michael@0: }; michael@0: DropDownPositionState AbsolutelyPositionDropDown(); michael@0: michael@0: // Helper for GetMinWidth/GetPrefWidth michael@0: nscoord GetIntrinsicWidth(nsRenderingContext* aRenderingContext, michael@0: nsLayoutUtils::IntrinsicWidthType aType); michael@0: michael@0: class RedisplayTextEvent : public nsRunnable { michael@0: public: michael@0: NS_DECL_NSIRUNNABLE michael@0: RedisplayTextEvent(nsComboboxControlFrame *c) : mControlFrame(c) {} michael@0: void Revoke() { mControlFrame = nullptr; } michael@0: private: michael@0: nsComboboxControlFrame *mControlFrame; michael@0: }; michael@0: michael@0: /** michael@0: * Show or hide the dropdown list. michael@0: * @note This method might destroy |this|. michael@0: */ michael@0: void ShowPopup(bool aShowPopup); michael@0: michael@0: /** michael@0: * Show or hide the dropdown list. michael@0: * @param aShowList true to show, false to hide the dropdown. michael@0: * @note This method might destroy |this|. michael@0: * @return false if this frame is destroyed, true if still alive. michael@0: */ michael@0: bool ShowList(bool aShowList); michael@0: void CheckFireOnChange(); michael@0: void FireValueChangeEvent(); michael@0: nsresult RedisplayText(int32_t aIndex); michael@0: void HandleRedisplayTextEvent(); michael@0: void ActuallyDisplayText(bool aNotify); michael@0: michael@0: private: michael@0: // If our total transform to the root frame of the root document is only a 2d michael@0: // translation then return that translation, otherwise returns (0,0). michael@0: nsPoint GetCSSTransformTranslation(); michael@0: michael@0: protected: michael@0: nsFrameList mPopupFrames; // additional named child list michael@0: nsCOMPtr mDisplayContent; // Anonymous content used to display the current selection michael@0: nsCOMPtr mButtonContent; // Anonymous content for the button michael@0: nsIFrame* mDisplayFrame; // frame to display selection michael@0: nsIFrame* mButtonFrame; // button frame michael@0: nsIFrame* mDropdownFrame; // dropdown list frame michael@0: nsIListControlFrame * mListControlFrame; // ListControl Interface for the dropdown frame michael@0: michael@0: // The width of our display area. Used by that frame's reflow to michael@0: // size to the full width except the drop-marker. michael@0: nscoord mDisplayWidth; michael@0: michael@0: nsRevocableEventPtr mRedisplayTextEvent; michael@0: michael@0: int32_t mRecentSelectedIndex; michael@0: int32_t mDisplayedIndex; michael@0: nsString mDisplayedOptionText; michael@0: michael@0: // make someone to listen to the button. If its programmatically pressed by someone like Accessibility michael@0: // then open or close the combo box. michael@0: nsCOMPtr mButtonListener; michael@0: michael@0: // The last y-positions used for estimating available space above and michael@0: // below for the dropdown list in GetAvailableDropdownSpace. These are michael@0: // reset to nscoord_MIN in AbsolutelyPositionDropDown when placing the michael@0: // dropdown at its actual position. The GetAvailableDropdownSpace call michael@0: // from nsListControlFrame::ReflowAsDropdown use the last position. michael@0: nscoord mLastDropDownAboveScreenY; michael@0: nscoord mLastDropDownBelowScreenY; michael@0: // Current state of the dropdown list, true is dropped down. michael@0: bool mDroppedDown; michael@0: // See comment in HandleRedisplayTextEvent(). michael@0: bool mInRedisplayText; michael@0: // Acting on ShowDropDown(true) is delayed until we're focused. michael@0: bool mDelayedShowDropDown; michael@0: michael@0: // static class data member for Bug 32920 michael@0: // only one control can be focused at a time michael@0: static nsComboboxControlFrame* sFocused; michael@0: michael@0: #ifdef DO_REFLOW_COUNTER michael@0: int32_t mReflowId; michael@0: #endif michael@0: }; michael@0: michael@0: #endif