1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef UI_VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_
6 #define UI_VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_
7 
8 #include <memory>
9 
10 #include "base/compiler_specific.h"
11 #include "base/gtest_prod_util.h"
12 #include "base/macros.h"
13 #include "base/time/time.h"
14 #include "ui/gfx/font_list.h"
15 #include "ui/gfx/geometry/insets.h"
16 #include "ui/views/bubble/bubble_border.h"
17 #include "ui/views/controls/button/button.h"
18 #include "ui/views/controls/label.h"
19 #include "ui/views/controls/progress_bar.h"
20 #include "ui/views/input_event_activation_protector.h"
21 #include "ui/views/window/non_client_view.h"
22 
23 namespace views {
24 
25 class FootnoteContainerView;
26 class ImageView;
27 
28 // The non-client frame view of bubble-styled widgets.
29 class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView,
30                                      public ButtonListener {
31  public:
32   enum class PreferredArrowAdjustment { kMirror, kOffset };
33 
34   // Internal class name.
35   static const char kViewClassName[];
36 
37   BubbleFrameView(const gfx::Insets& title_margins,
38                   const gfx::Insets& content_margins);
39   ~BubbleFrameView() override;
40 
41   static std::unique_ptr<Label> CreateDefaultTitleLabel(
42       const base::string16& title_text);
43 
44   // Creates a close button used in the corner of the dialog.
45   static std::unique_ptr<Button> CreateCloseButton(ButtonListener* listener);
46 
47   // NonClientFrameView:
48   gfx::Rect GetBoundsForClientView() const override;
49   gfx::Rect GetWindowBoundsForClientBounds(
50       const gfx::Rect& client_bounds) const override;
51   bool GetClientMask(const gfx::Size& size, SkPath* path) const override;
52   int NonClientHitTest(const gfx::Point& point) override;
53   void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override;
54   void ResetWindowControls() override;
55   void UpdateWindowIcon() override;
56   void UpdateWindowTitle() override;
57   void SizeConstraintsChanged() override;
58 
59   // Sets a custom view to be the dialog title instead of the |default_title_|
60   // label. If there is an existing title view it will be deleted.
61   void SetTitleView(std::unique_ptr<View> title_view);
62 
63   // Updates the current progress value of |progress_indicator_|. If progress is
64   // absent, hides |the progress_indicator|.
65   void SetProgress(base::Optional<double> progress);
66 
67   // View:
68   const char* GetClassName() const override;
69   gfx::Size CalculatePreferredSize() const override;
70   gfx::Size GetMinimumSize() const override;
71   gfx::Size GetMaximumSize() const override;
72   void Layout() override;
73   void OnPaint(gfx::Canvas* canvas) override;
74   void PaintChildren(const PaintInfo& paint_info) override;
75   void OnThemeChanged() override;
76   void ViewHierarchyChanged(
77       const ViewHierarchyChangedDetails& details) override;
78   void VisibilityChanged(View* starting_from, bool is_visible) override;
79 
80   // ButtonListener:
81   void ButtonPressed(Button* sender, const ui::Event& event) override;
82 
83   // Use SetBubbleBorder() not SetBorder().
84   void SetBubbleBorder(std::unique_ptr<BubbleBorder> border);
85 
title()86   const View* title() const {
87     return custom_title_ ? custom_title_ : default_title_;
88   }
title()89   View* title() {
90     return const_cast<View*>(
91         static_cast<const BubbleFrameView*>(this)->title());
92   }
93 
content_margins()94   gfx::Insets content_margins() const { return content_margins_; }
95 
96   // Sets a custom header view for the dialog. If there is an existing header
97   // view it will be deleted. The header view will be inserted above the title,
98   // so outside the content bounds. If there is a close button, it will be shown
99   // in front of the header view and will overlap with it. The title will be
100   // shown below the header and / or the close button, depending on which is
101   // lower. An example usage for a header view would be a banner image.
102   void SetHeaderView(std::unique_ptr<View> view);
103 
104   // Sets a custom footnote view for the dialog. If there is an existing
105   // footnote view it will be deleted. The footnote will be rendered at the
106   // bottom of the bubble, after the content view. It is separated by a 1 dip
107   // line and has a solid background by being embedded in a
108   // FootnoteContainerView. An example footnote would be some help text.
109   void SetFootnoteView(std::unique_ptr<View> view);
110   View* GetFootnoteView() const;
set_footnote_margins(const gfx::Insets & footnote_margins)111   void set_footnote_margins(const gfx::Insets& footnote_margins) {
112     footnote_margins_ = footnote_margins;
113   }
114 
set_preferred_arrow_adjustment(PreferredArrowAdjustment adjustment)115   void set_preferred_arrow_adjustment(PreferredArrowAdjustment adjustment) {
116     preferred_arrow_adjustment_ = adjustment;
117   }
118 
119   // TODO(crbug.com/1007604): remove this in favor of using
120   // Widget::InitParams::accept_events. In the mean time, don't add new uses of
121   // this flag.
hit_test_transparent()122   bool hit_test_transparent() const { return hit_test_transparent_; }
set_hit_test_transparent(bool hit_test_transparent)123   void set_hit_test_transparent(bool hit_test_transparent) {
124     hit_test_transparent_ = hit_test_transparent;
125   }
126 
127   // Get/set the corner radius of the bubble border.
corner_radius()128   int corner_radius() const {
129     return bubble_border_ ? bubble_border_->corner_radius() : 0;
130   }
131   void SetCornerRadius(int radius);
132 
133   // Set the arrow of the bubble border.
134   void SetArrow(BubbleBorder::Arrow arrow);
135 
136   // Set the background color of the bubble border.
137   void SetBackgroundColor(SkColor color);
138   SkColor GetBackgroundColor() const;
139 
140   // Given the size of the contents and the rect to point at, returns the bounds
141   // of the bubble window. The bubble's arrow location may change if the bubble
142   // does not fit on the monitor or anchor window (if one exists) and
143   // |adjust_to_fit_available_bounds| is true.
144   gfx::Rect GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
145                                    const BubbleBorder::Arrow arrow,
146                                    const gfx::Size& client_size,
147                                    bool adjust_to_fit_available_bounds);
148 
GetCloseButtonForTesting()149   Button* GetCloseButtonForTesting() { return close_; }
150 
GetHeaderViewForTesting()151   View* GetHeaderViewForTesting() const { return header_view_; }
152 
153   // Resets the time when view has been shown. Tests may need to call this
154   // method if they use events that could be otherwise treated as unintended.
155   // See IsPossiblyUnintendedInteraction().
156   void ResetViewShownTimeStampForTesting();
157 
158  protected:
159   // Returns the available screen bounds if the frame were to show in |rect|.
160   virtual gfx::Rect GetAvailableScreenBounds(const gfx::Rect& rect) const;
161 
162   // Returns the available anchor window bounds in the screen.
163   virtual gfx::Rect GetAvailableAnchorWindowBounds() const;
164 
165   // Override and return true to allow client view to overlap into the title
166   // area when HasTitle() returns false and/or ShouldShowCloseButton() returns
167   // true. Returns false by default.
168   virtual bool ExtendClientIntoTitle() const;
169 
170   bool IsCloseButtonVisible() const;
171   gfx::Rect GetCloseButtonMirroredBounds() const;
172 
bubble_border_for_testing()173   BubbleBorder* bubble_border_for_testing() const { return bubble_border_; }
174 
175  private:
176   FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, RemoveFootnoteView);
177   FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, LayoutWithIcon);
178   FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, LayoutWithProgressIndicator);
179   FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, IgnorePossiblyUnintendedClicks);
180   FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CloseReasons);
181   FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest, CloseMethods);
182   FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest, CreateDelegate);
183 
184   // Mirrors the bubble's arrow location on the |vertical| or horizontal axis,
185   // if the generated window bounds don't fit in the given available bounds.
186   void MirrorArrowIfOutOfBounds(bool vertical,
187                                 const gfx::Rect& anchor_rect,
188                                 const gfx::Size& client_size,
189                                 const gfx::Rect& available_bounds);
190 
191   // Adjust the bubble's arrow offsets if the generated window bounds don't fit
192   // in the given available bounds.
193   void OffsetArrowIfOutOfBounds(const gfx::Rect& anchor_rect,
194                                 const gfx::Size& client_size,
195                                 const gfx::Rect& available_bounds);
196 
197   // The width of the frame for the given |client_width|. The result accounts
198   // for the minimum title bar width and includes all insets and possible
199   // snapping. It does not include the border.
200   int GetFrameWidthForClientWidth(int client_width) const;
201 
202   // Calculates the size needed to accommodate the given client area.
203   gfx::Size GetFrameSizeForClientSize(const gfx::Size& client_size) const;
204 
205   // True if the frame has a title area. This is the area affected by
206   // |title_margins_|, including the icon and title text, but not the close
207   // button.
208   bool HasTitle() const;
209 
210   // The insets of the text portion of the title, based on |title_margins_| and
211   // whether there is an icon and/or close button. Note there may be no title,
212   // in which case only insets required for the close button are returned.
213   gfx::Insets GetTitleLabelInsetsFromFrame() const;
214 
215   // The client_view insets (from the frame view) for the given |frame_width|.
216   gfx::Insets GetClientInsetsForFrameWidth(int frame_width) const;
217 
218   // Gets the height of the |header_view_| given a |frame_width|. Returns zero
219   // if there is no header view or if it is not visible.
220   int GetHeaderHeightForFrameWidth(int frame_width) const;
221 
222   // The bubble border.
223   BubbleBorder* bubble_border_ = nullptr;
224 
225   // Margins around the title label.
226   gfx::Insets title_margins_;
227 
228   // Margins between the content and the inside of the border, in pixels.
229   gfx::Insets content_margins_;
230 
231   // Margins between the footnote view and the footnote container.
232   gfx::Insets footnote_margins_;
233 
234   // The optional title icon.
235   ImageView* title_icon_ = nullptr;
236 
237   // One of these fields is used as the dialog title. If SetTitleView is called
238   // the custom title view is stored in |custom_title_| and this class assumes
239   // ownership. Otherwise |default_title_| is used.
240   Label* default_title_ = nullptr;
241   View* custom_title_ = nullptr;
242 
243   // The optional close button (the X).
244   Button* close_ = nullptr;
245 
246   // The optional progress bar. Used to indicate bubble pending state. By
247   // default it is invisible.
248   ProgressBar* progress_indicator_ = nullptr;
249 
250   // The optional header view.
251   View* header_view_ = nullptr;
252 
253   // A view to contain the footnote view, if it exists.
254   FootnoteContainerView* footnote_container_ = nullptr;
255 
256   // Set preference for how the arrow will be adjusted if the window is outside
257   // the available bounds.
258   PreferredArrowAdjustment preferred_arrow_adjustment_ =
259       PreferredArrowAdjustment::kMirror;
260 
261   // If true the view is transparent to all hit tested events (i.e. click and
262   // hover). DEPRECATED: See note above set_hit_test_transparent().
263   bool hit_test_transparent_ = false;
264 
265   InputEventActivationProtector input_protector_;
266 
267   DISALLOW_COPY_AND_ASSIGN(BubbleFrameView);
268 };
269 
270 }  // namespace views
271 
272 #endif  // UI_VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_
273