File: chrome_labs_item_view.cc

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; 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,811; 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 (258 lines) | stat: -rw-r--r-- 11,240 bytes parent folder | download | duplicates (4)
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
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/toolbar/chrome_labs/chrome_labs_item_view.h"

#include <array>

#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "build/build_config.h"
#include "chrome/browser/feedback/show_feedback_page.h"
#include "chrome/browser/flag_descriptions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/toolbar/chrome_labs/chrome_labs_model.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/grit/generated_resources.h"
#include "components/user_education/common/new_badge/new_badge_controller.h"
#include "components/user_education/views/new_badge_label.h"
#include "components/webui/flags/feature_entry.h"
#include "extensions/browser/api/feedback_private/feedback_private_api.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/combobox_model.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/flex_layout_view.h"
#include "ui/views/layout/layout_provider.h"

namespace {

void ShowFeedbackPage(Browser* browser,
                      std::string feedback_category_name,
                      std::u16string visible_name) {
  chrome::ShowFeedbackPage(
      browser, feedback::FeedbackSource::kFeedbackSourceChromeLabs,
      /*description_template=*/std::string(),
      /*description_placeholder_text=*/
      l10n_util::GetStringFUTF8(
          IDS_CHROMELABS_SEND_FEEDBACK_DESCRIPTION_PLACEHOLDER,
          std::move(visible_name)),
      /*category_tag=*/std::move(feedback_category_name),
      /* extra_diagnostics=*/std::string());
}

}  // namespace

class LabsComboboxModel : public ui::ComboboxModel {
 public:
  explicit LabsComboboxModel(const LabInfo& lab,
                             const flags_ui::FeatureEntry* feature_entry,
                             size_t default_index)
      : lab_(lab),
        feature_entry_(feature_entry),
        default_index_(default_index) {}

  // ui::ComboboxModel:
  size_t GetItemCount() const override { return feature_entry_->NumOptions(); }

  // The order in which these descriptions are returned is the same in
  // flags_ui::FeatureEntry::DescriptionForOption(..). If there are changes to
  // this, the same changes must be made in
  // flags_ui::FeatureEntry::DescriptionForOption(..).
  std::u16string GetItemAt(size_t index) const override {
    DCHECK_LT(index, static_cast<size_t>(feature_entry_->NumOptions()));
    int description_translation_id = IDS_CHROMELABS_DEFAULT;
    if (feature_entry_->type ==
        flags_ui::FeatureEntry::FEATURE_WITH_PARAMS_VALUE) {
      if (index == 0) {
        description_translation_id = IDS_CHROMELABS_DEFAULT;
      } else if (index == 1) {
        description_translation_id = IDS_CHROMELABS_ENABLED;
      } else if (index + 1 <
                 static_cast<size_t>(feature_entry_->NumOptions())) {
        // First two options do not have variations params.
        size_t variation_index = index - 2;
        return l10n_util::GetStringFUTF16(
            IDS_CHROMELABS_ENABLED_WITH_VARIATION_NAME,
            lab_->translated_feature_variation_descriptions[variation_index]);
      } else {
        description_translation_id = IDS_CHROMELABS_DISABLED;
      }
    } else {
      static constexpr std::array kEnableDisableDescriptions{
          IDS_CHROMELABS_DEFAULT,
          IDS_CHROMELABS_ENABLED,
          IDS_CHROMELABS_DISABLED,
      };
      description_translation_id = kEnableDisableDescriptions[index];
    }
    return l10n_util::GetStringUTF16(description_translation_id);
  }

  std::optional<size_t> GetDefaultIndex() const override {
    return default_index_;
  }

 private:
  const raw_ref<const LabInfo> lab_;
  raw_ptr<const flags_ui::FeatureEntry> feature_entry_;
  size_t default_index_;
};

ChromeLabsItemView::ChromeLabsItemView(
    const LabInfo& lab,
    int default_index,
    const flags_ui::FeatureEntry* feature_entry,
    base::RepeatingCallback<void(ChromeLabsItemView* item_view)>
        combobox_callback,
    Browser* browser)
    : feature_entry_(feature_entry) {
  SetLayoutManager(std::make_unique<views::FlexLayout>())
      ->SetOrientation(views::LayoutOrientation::kVertical);
  SetBorder(views::CreateEmptyBorder(
      gfx::Insets::VH(ChromeLayoutProvider::Get()->GetDistanceMetric(
                          views::DISTANCE_CONTROL_LIST_VERTICAL),
                      0)));

  experiment_name_ = AddChildView(
      std::make_unique<user_education::NewBadgeLabel>(lab.visible_name));
  experiment_name_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  experiment_name_->SetBadgePlacement(
      user_education::NewBadgeLabel::BadgePlacement::kImmediatelyAfterText);

  views::Label* experiment_description;
  AddChildView(
      views::Builder<views::Label>()
          .CopyAddressTo(&experiment_description)
          .SetText(lab.visible_description)
          .SetTextContext(ChromeTextContext::CONTEXT_DIALOG_BODY_TEXT_SMALL)
          .SetTextStyle(views::style::STYLE_SECONDARY)
          .SetMultiLine(true)
          .SetHorizontalAlignment(gfx::ALIGN_LEFT)
          .SetProperty(views::kFlexBehaviorKey,
                       views::FlexSpecification(
                           views::MinimumFlexSizeRule::kPreferred,
                           views::MaximumFlexSizeRule::kPreferred, true))
          .SetBorder(views::CreateEmptyBorder(
              gfx::Insets::TLBR(0, 0,
                                views::LayoutProvider::Get()->GetDistanceMetric(
                                    views::DISTANCE_RELATED_CONTROL_VERTICAL),
                                0)))
          .Build());

  // It may cause confusion if screen readers read out all experiments and
  // descriptions when the bubble first opens. Experiment name and description
  // will be read out when a user enters the grouping.
  // See crbug.com/1145666 Accessibility review.
  experiment_name_->GetViewAccessibility().SetIsIgnored(true);
  experiment_description->GetViewAccessibility().SetIsIgnored(true);
  GetViewAccessibility().SetRole(ax::mojom::Role::kGroup);
  if (!lab.visible_name.empty()) {
    GetViewAccessibility().SetName(lab.visible_name,
                                   ax::mojom::NameFrom::kAttribute);
  }

  // There is currently a MacOS VoiceOver screen reader bug where VoiceOver
  // does not announce the accessible description for groups
  // (crbug.com/1197159). The MacOS specific code here provides a temporary
  // mitigation for screen reader users and moves announcing the description
  // to when the user interacts with the combobox of that experiment. Don’t
  // add an accessible description for now to prevent the screen reader from
  // announcing the description twice in the time between when the VoiceOver
  // bug is fixed and this code gets removed.
  // TODO(elainechien): Remove MacOS specific code for experiment description
  // when VoiceOver bug is fixed.

#if !BUILDFLAG(IS_MAC)
  if (!lab.visible_description.empty()) {
    GetViewAccessibility().SetDescription(lab.visible_description);
  }
#endif

  AddChildView(
      views::Builder<views::FlexLayoutView>()
          .SetOrientation(views::LayoutOrientation::kHorizontal)
          .AddChildren(
              views::Builder<views::Combobox>()
                  .CopyAddressTo(&lab_state_combobox_)
                  .SetTooltipTextAndAccessibleName(l10n_util::GetStringFUTF16(
                      IDS_TOOLTIP_CHROMELABS_COMBOBOX, lab.visible_name))
#if BUILDFLAG(IS_MAC)
                  .SetAccessibleName(l10n_util::GetStringFUTF16(
                      IDS_ACCNAME_CHROMELABS_COMBOBOX_MAC, lab.visible_name,
                      lab.visible_description))
#else
                  .SetAccessibleName(l10n_util::GetStringFUTF16(
                      IDS_ACCNAME_CHROMELABS_COMBOBOX, lab.visible_name))

#endif
                  .SetOwnedModel(std::make_unique<LabsComboboxModel>(
                      lab, feature_entry_, default_index))
                  .SetCallback(base::BindRepeating(combobox_callback, this))

                  .SetProperty(views::kFlexBehaviorKey,
                               views::FlexSpecification(
                                   views::MinimumFlexSizeRule::kScaleToZero,
                                   views::MaximumFlexSizeRule::kPreferred))

                  .SetSizeToLargestLabel(false),
              views::Builder<views::MdTextButton>()
                  .CopyAddressTo(&feedback_button_)
                  .SetTooltipText(l10n_util::GetStringFUTF16(
                      IDS_TOOLTIP_CHROMELABS_FEEDBACK_BUTTON, lab.visible_name))
                  .SetCallback(base::BindRepeating(&ShowFeedbackPage, browser,
                                                   lab.feedback_category_name,
                                                   lab.visible_name))
                  .SetText(
                      l10n_util::GetStringUTF16(IDS_CHROMELABS_SEND_FEEDBACK))
                  .SetProperty(
                      views::kMarginsKey,
                      gfx::Insets::TLBR(
                          0,
                          views::LayoutProvider::Get()->GetDistanceMetric(
                              views::DISTANCE_RELATED_CONTROL_HORIZONTAL),
                          0, 0))
                  .SetProperty(
                      views::kFlexBehaviorKey,
                      // FlexSpecification has multiple constructors, and if no
                      // direction is specified, the settings will be used in
                      // both horizontal and vertical directions. Therefore, we
                      // must specify the horizontal direction. Otherwise, the
                      // vertical height will be stretched.
                      views::FlexSpecification(
                          views::LayoutOrientation::kHorizontal,
                          views::MinimumFlexSizeRule::kPreferred,
                          views::MaximumFlexSizeRule::kUnbounded)
                          .WithAlignment(views::LayoutAlignment::kEnd)))
          .Build());
}

ChromeLabsItemView::~ChromeLabsItemView() = default;

std::optional<size_t> ChromeLabsItemView::GetSelectedIndex() const {
  return lab_state_combobox_->GetSelectedIndex();
}

// Same as NewBadgeLabel::SetDisplayNewBadge this should only be called before
// the label is shown.
void ChromeLabsItemView::SetShowNewBadge(
    user_education::DisplayNewBadge show_new_badge) {
  experiment_name_->SetDisplayNewBadge(show_new_badge);
}

const flags_ui::FeatureEntry* ChromeLabsItemView::GetFeatureEntry() {
  return feature_entry_;
}

BEGIN_METADATA(ChromeLabsItemView)
ADD_READONLY_PROPERTY_METADATA(std::optional<size_t>, SelectedIndex)
END_METADATA