File: bubble_frame_view.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (417 lines) | stat: -rw-r--r-- 17,256 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_
#define UI_VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_

#include <memory>
#include <utility>

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/color/color_variant.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/progress_bar.h"
#include "ui/views/input_event_activation_protector.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/style/typography.h"
#include "ui/views/window/non_client_view.h"

namespace gfx {
class RoundedCornersF;
}

namespace views {

class FootnoteContainerView;
class ImageView;

// The non-client frame view of bubble-styled widgets.
//  +- BubbleFrameView ------------------+
//  | +- ProgressBar ------------------+ |
//  | +-----------------------(-)-(x)-+  |
//  | | HeaderView                    |  |
//  | +-------------------------------+  |
//  | +-------------------------------+  |
//  | | TitleView                     |  |
//  | +-------------------------------+  |
//  | +-- DialogClientView------------+  |
//  | | <<Dialog Contents View>>      |  |
//  | | <<OK and Cancel Buttons>>     |  |
//  | | <<...>>                       |  |
//  | +-------------------------------+  |
//  | +-------------------------------+  |
//  | | FootnoteView                  |  |
//  | +-------------------------------+  |
//  +------------------------------------+
// All views are optional except for DialogClientView. An ImageView
// `main_image` might optionally occupy the top left corner (not
// illustrated above).
// If TitleView exists and HeaderView does not exists, the close
// and the minimize buttons will be positioned at the end of the
// title row. Otherwise, they will be positioned closer to the frame
// edge.
class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView {
  METADATA_HEADER(BubbleFrameView, NonClientFrameView)

 public:
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kMinimizeButtonElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kCloseButtonElementId);
  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kProgressIndicatorElementId);

  enum class PreferredArrowAdjustment { kMirror, kOffset };

  BubbleFrameView(const gfx::Insets& title_margins,
                  const gfx::Insets& content_margins);
  BubbleFrameView(const BubbleFrameView&) = delete;
  BubbleFrameView& operator=(BubbleFrameView&) = delete;
  ~BubbleFrameView() override;

  static std::unique_ptr<Label> CreateDefaultTitleLabel(
      const std::u16string& title_text);

  // Creates a close button used in the corner of the dialog.
  static std::unique_ptr<Button> CreateCloseButton(
      Button::PressedCallback callback);

  // Creates a minimize button used in the corner of the dialog.
  static std::unique_ptr<Button> CreateMinimizeButton(
      Button::PressedCallback callback);

  // NonClientFrameView:
  gfx::Rect GetBoundsForClientView() const override;
  gfx::Rect GetWindowBoundsForClientBounds(
      const gfx::Rect& client_bounds) const override;
  bool GetClientMask(const gfx::Size& size, SkPath* path) const override;
  int NonClientHitTest(const gfx::Point& point) override;
  void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override;
  void ResetWindowControls() override;
  void UpdateWindowIcon() override;
  void UpdateWindowTitle() override;
  void InsertClientView(ClientView* client_view) override;
  void UpdateWindowRoundedCorners() override;
  bool HasWindowTitle() const override;
  bool IsWindowTitleVisible() const override;

  // Sets a custom view to be the dialog title instead of the |default_title_|
  // label. If there is an existing title view it will be deleted.
  void SetTitleView(std::unique_ptr<View> title_view);

  // Updates the subtitle label from the BubbleDialogDelegate.
  void UpdateSubtitle();

  // Signals that the main image may have changed and needs to be fetched again.
  void UpdateMainImage();

  // Updates the current progress value of |progress_indicator_|. If progress is
  // absent, hides |the progress_indicator|.
  void SetProgress(std::optional<double> progress);
  // Returns the current progress value of |progress_indicator_| if
  // |progress_indicator_| is visible.
  std::optional<double> GetProgress() const;

  // View:
  gfx::Size CalculatePreferredSize(
      const SizeBounds& available_size) const override;
  gfx::Size GetMinimumSize() const override;
  gfx::Size GetMaximumSize() const override;
  void Layout(PassKey) override;
  void OnPaint(gfx::Canvas* canvas) override;
  void PaintChildren(const PaintInfo& paint_info) override;
  void OnThemeChanged() override;
  void ViewHierarchyChanged(
      const ViewHierarchyChangedDetails& details) override;
  void VisibilityChanged(View* starting_from, bool is_visible) override;

  // Use SetBubbleBorder() not SetBorder().
  void SetBubbleBorder(std::unique_ptr<BubbleBorder> border);

  const View* title() const {
    return custom_title_ ? custom_title_.get() : default_title_.get();
  }
  View* title() {
    return const_cast<View*>(
        static_cast<const BubbleFrameView*>(this)->title());
  }

  Label* default_title() { return default_title_.get(); }

  void SetContentMargins(const gfx::Insets& content_margins);
  gfx::Insets GetContentMargins() const;

  // Sets a custom header view for the dialog. If there is an existing header
  // view it will be deleted. The header view will be inserted above the title,
  // so outside the content bounds. If there is a close button, it will be shown
  // in front of the header view and will overlap with it. The title will be
  // shown below the header and / or the close button, depending on which is
  // lower. An example usage for a header view would be a banner image.
  void SetHeaderView(std::unique_ptr<View> view);

  // Sets a custom footnote view for the dialog. If there is an existing
  // footnote view it will be deleted. The footnote will be rendered at the
  // bottom of the bubble, after the content view. It is separated by a 1 dip
  // line and has a solid background by being embedded in a
  // FootnoteContainerView. An example footnote would be some help text.
  void SetFootnoteView(std::unique_ptr<View> view);
  View* GetFootnoteView() const;
  void SetFootnoteMargins(const gfx::Insets& footnote_margins);
  gfx::Insets GetFootnoteMargins() const;

  void SetPreferredArrowAdjustment(PreferredArrowAdjustment adjustment);
  PreferredArrowAdjustment GetPreferredArrowAdjustment() const;

  // TODO(crbug.com/40100380): remove this in favor of using
  // Widget::InitParams::accept_events. In the mean time, don't add new uses of
  // this flag.
  bool hit_test_transparent() const { return hit_test_transparent_; }
  void set_hit_test_transparent(bool hit_test_transparent) {
    hit_test_transparent_ = hit_test_transparent;
  }

  void set_use_anchor_window_bounds(bool use_anchor_window_bounds) {
    use_anchor_window_bounds_ = use_anchor_window_bounds;
  }

  // Set the corner radius of the bubble border.
  void SetRoundedCorners(const gfx::RoundedCornersF& radii);
  gfx::RoundedCornersF GetRoundedCorners() const;

  // Set the arrow of the bubble border.
  void SetArrow(BubbleBorder::Arrow arrow);
  BubbleBorder::Arrow GetArrow() const;

  // Specify whether the frame should include a visible, caret-shaped arrow.
  void SetDisplayVisibleArrow(bool display_visible_arrow);
  bool GetDisplayVisibleArrow() const;

  // Set the background color of the bubble border.
  void SetBackgroundColor(ui::ColorVariant color);
  ui::ColorVariant background_color() const { return bubble_border_->color(); }

  // For masking reasons, the ClientView is painted to a textured layer. To
  // ensure bubbles that rely on the frame background color continue to work as
  // expected, we must set the background of the ClientView to match that of the
  // BubbleFrameView.
  void UpdateClientViewBackground();

  // Given the size of the contents and the rect to point at, returns the bounds
  // of the bubble window. The bubble's arrow location may change if the bubble
  // does not fit on the monitor or anchor window (if one exists) and
  // |adjust_to_fit_available_bounds| is true.
  gfx::Rect GetUpdatedWindowBounds(const gfx::Rect& anchor_rect,
                                   const BubbleBorder::Arrow arrow,
                                   const gfx::Size& client_size,
                                   bool adjust_to_fit_available_bounds);

  Button* close_button() { return close_; }
  const Button* close_button() const { return close_; }

  View* GetHeaderViewForTesting() const { return header_view_; }

  // Update the |view_shown_time_stamp_| of input protector. A short time
  // from this point onward, input event will be ignored.
  void UpdateInputProtectorTimeStamp();

  // Resets the time when view has been shown. Tests may need to call this
  // method if they use events that could be otherwise treated as unintended.
  // See IsPossiblyUnintendedInteraction().
  void ResetViewShownTimeStampForTesting();

  BubbleBorder* bubble_border() const { return bubble_border_; }

  // Returns the client_view insets from the frame view.
  gfx::Insets GetClientViewInsets() const;

  using HitTestCallback = base::RepeatingCallback<int(const gfx::Point& point)>;
  void set_non_client_hit_test_cb(HitTestCallback non_client_hit_test_cb) {
    non_client_hit_test_cb_ = std::move(non_client_hit_test_cb);
  }

 protected:
  // Returns the available screen bounds if the frame were to show in |rect|.
  virtual gfx::Rect GetAvailableScreenBounds(const gfx::Rect& rect) const;

  // Returns the available anchor window bounds in the screen.
  // This will only be used if `use_anchor_window_bounds_` is true.
  virtual gfx::Rect GetAvailableAnchorWindowBounds() const;

  // Override and return true to allow client view to overlap into the title
  // area when HasTitle() returns false and/or ShouldShowCloseButton() returns
  // true. Returns false by default.
  virtual bool ExtendClientIntoTitle() const;

  bool IsCloseButtonVisible() const;
  gfx::Rect GetCloseButtonMirroredBounds() const;

  // Helper function that gives the corner radius values that should be applied
  // to the BubbleFrameView's client view. These values depend on the amount of
  // inset present on the client view and the presence of header and footer
  // views.
  gfx::RoundedCornersF GetClientCornerRadii() const;

 private:
  FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, RemoveFootnoteView);
  FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, LayoutWithIcon);
  FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, LayoutWithProgressIndicator);
  FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest,
                           IgnorePossiblyUnintendedClicksClose);
  FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest,
                           IgnorePossiblyUnintendedClicksMinimize);
  FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest,
                           IgnorePossiblyUnintendedClicksAnchorBoundsChanged);
  FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CloseReasons);
  FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest, CloseMethods);
  FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest, CreateDelegate);

  // The positioning options for the close button and the minimize button.
  enum class ButtonsPositioning {
    // The buttons are positioned at the end of the title row.
    kInTitleRow,
    // The buttons are positioned on the upper trailing corner of the
    // bubble. The distance between buttons and the frame edge will be shorter
    // than `kInTitleRow`.
    kOnFrameEdge,
  };

  // Mirrors the bubble's arrow location on the |vertical| or horizontal axis,
  // if the generated window bounds don't fit in the given available bounds.
  void MirrorArrowIfOutOfBounds(bool vertical,
                                const gfx::Rect& anchor_rect,
                                const gfx::Size& client_size,
                                const gfx::Rect& available_bounds);

  // Adjust the bubble's arrow offsets if the generated window bounds don't fit
  // in the given available bounds.
  void OffsetArrowIfOutOfBounds(const gfx::Rect& anchor_rect,
                                const gfx::Size& client_size,
                                const gfx::Rect& available_bounds);

  // The width of the frame for the given |client_width|. The result accounts
  // for the minimum title bar width and includes all insets and possible
  // snapping. It does not include the border.
  int GetFrameWidthForClientWidth(int client_width) const;

  // Calculates the size needed to accommodate the given client area.
  gfx::Size GetFrameSizeForClientSize(const gfx::Size& client_size) const;

  // True if the frame has a title area. This is the area affected by
  // |title_margins_|, including the icon and title text, but not the close
  // button.
  bool HasTitle() const;

  // Returns the positioning options for the buttons.
  ButtonsPositioning GetButtonsPositioning() const;

  // Returns true if there're buttons in the title row.
  bool TitleRowHasButtons() const;

  // The insets of the text portion of the title, based on |title_margins_| and
  // whether there is an icon and/or close button. Note there may be no title,
  // in which case only insets required for the close button are returned.
  gfx::Insets GetTitleLabelInsetsFromFrame() const;

  // The client_view insets (from the frame view) for the given |frame_width|.
  gfx::Insets GetClientInsetsForFrameWidth(int frame_width) const;

  // Gets the height of the |header_view_| given a |frame_width|. Returns zero
  // if there is no header view or if it is not visible.
  int GetHeaderHeightForFrameWidth(int frame_width) const;

  // Updates the corner radius of a layer backed client view for MD rounded
  // corners.
  // TODO(tluk): Use this and remove the need for GetClientMask() for clipping
  // client views to the bubble border's bounds.
  void UpdateClientLayerCornerRadius();

  int GetMainImageLeftInsets() const;

  gfx::Point GetButtonAreaTopRight() const;

  gfx::Size GetButtonAreaSize() const;

  // Helper method to create a label with text style
  static std::unique_ptr<Label> CreateLabelWithContextAndStyle(
      const std::u16string& label_text,
      style::TextContext text_context,
      style::TextStyle text_style);

  // Note: The method is defined for meta data framework.
  SkColor GetBackgroundColor() const;

  // The bubble border.
  raw_ptr<BubbleBorder> bubble_border_ = nullptr;

  // Margins around the title label.
  const gfx::Insets title_margins_;

  // Margins between the content and the inside of the border, in pixels.
  gfx::Insets content_margins_;

  // Margins between the footnote view and the footnote container.
  gfx::Insets footnote_margins_;

  // The optional title icon.
  raw_ptr<ImageView> title_icon_ = nullptr;

  // The optional main image.
  raw_ptr<ImageView> main_image_ = nullptr;

  raw_ptr<BoxLayoutView> title_container_ = nullptr;

  // One of these fields is used as the dialog title. If SetTitleView is called
  // the custom title view is stored in `custom_title_` and this class assumes
  // ownership. Otherwise `default_title_` is used.
  raw_ptr<Label> default_title_ = nullptr;
  raw_ptr<View> custom_title_ = nullptr;

  raw_ptr<Label> subtitle_ = nullptr;

  // The optional minimize button (the _).
  raw_ptr<Button> minimize_ = nullptr;

  // The optional close button (the X).
  raw_ptr<Button> close_ = nullptr;

  // The optional progress bar. Used to indicate bubble pending state. By
  // default it is invisible.
  raw_ptr<ProgressBar> progress_indicator_ = nullptr;

  // The optional header view.
  raw_ptr<View> header_view_ = nullptr;

  // A view to contain the footnote view, if it exists.
  raw_ptr<FootnoteContainerView> footnote_container_ = nullptr;

  // Set preference for how the arrow will be adjusted if the window is outside
  // the available bounds.
  PreferredArrowAdjustment preferred_arrow_adjustment_ =
      PreferredArrowAdjustment::kMirror;

  // If true the view is transparent to all hit tested events (i.e. click and
  // hover). DEPRECATED: See note above set_hit_test_transparent().
  bool hit_test_transparent_ = false;

  // If true the bubble will try to stay inside the bounds returned by
  // `GetAvailableAnchorWindowBounds`.
  bool use_anchor_window_bounds_ = true;

  // Set by bubble clients to compose additional non-client hit test rules for
  // their host bubble. HTNOWHERE should be returned to tell the caller to do
  // further processing to determine where in the non-client area the tested
  // point is (if present at all). See NonClientFrameView::NonClientHitTest()
  // for details.
  HitTestCallback non_client_hit_test_cb_;

  InputEventActivationProtector input_protector_;
};

}  // namespace views

#endif  // UI_VIEWS_BUBBLE_BUBBLE_FRAME_VIEW_H_