summaryrefslogtreecommitdiff
path: root/chromium/ui/views/controls
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ui/views/controls')
-rw-r--r--chromium/ui/views/controls/animated_image_view.cc22
-rw-r--r--chromium/ui/views/controls/animated_image_view.h16
-rw-r--r--chromium/ui/views/controls/animated_image_view_unittest.cc8
-rw-r--r--chromium/ui/views/controls/button/label_button_unittest.cc26
-rw-r--r--chromium/ui/views/controls/combobox/combobox.cc389
-rw-r--r--chromium/ui/views/controls/combobox/combobox.h73
-rw-r--r--chromium/ui/views/controls/combobox/combobox_menu_model.cc105
-rw-r--r--chromium/ui/views/controls/combobox/combobox_menu_model.h54
-rw-r--r--chromium/ui/views/controls/combobox/combobox_unittest.cc245
-rw-r--r--chromium/ui/views/controls/combobox/empty_combobox_model.cc8
-rw-r--r--chromium/ui/views/controls/combobox/empty_combobox_model.h6
-rw-r--r--chromium/ui/views/controls/editable_combobox/editable_combobox.cc51
-rw-r--r--chromium/ui/views/controls/editable_combobox/editable_combobox.h8
-rw-r--r--chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc44
-rw-r--r--chromium/ui/views/controls/focus_ring.cc50
-rw-r--r--chromium/ui/views/controls/focus_ring.h14
-rw-r--r--chromium/ui/views/controls/image_view.cc8
-rw-r--r--chromium/ui/views/controls/image_view_unittest.cc10
-rw-r--r--chromium/ui/views/controls/label.cc43
-rw-r--r--chromium/ui/views/controls/label.h12
-rw-r--r--chromium/ui/views/controls/label_unittest.cc36
-rw-r--r--chromium/ui/views/controls/link.cc5
-rw-r--r--chromium/ui/views/controls/link.h5
-rw-r--r--chromium/ui/views/controls/link_fragment.cc88
-rw-r--r--chromium/ui/views/controls/link_fragment.h57
-rw-r--r--chromium/ui/views/controls/link_fragment_unittest.cc140
-rw-r--r--chromium/ui/views/controls/menu/menu_closure_animation_mac.h5
-rw-r--r--chromium/ui/views/controls/menu/menu_controller.cc167
-rw-r--r--chromium/ui/views/controls/menu/menu_controller.h11
-rw-r--r--chromium/ui/views/controls/menu/menu_controller_unittest.cc91
-rw-r--r--chromium/ui/views/controls/menu/menu_insertion_delegate_win.h2
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view.cc48
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view.h22
-rw-r--r--chromium/ui/views/controls/menu/menu_item_view_unittest.cc18
-rw-r--r--chromium/ui/views/controls/menu/menu_model_adapter.cc34
-rw-r--r--chromium/ui/views/controls/menu/menu_model_adapter.h8
-rw-r--r--chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc70
-rw-r--r--chromium/ui/views/controls/menu/menu_pre_target_handler_mac.h3
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm12
-rw-r--r--chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm21
-rw-r--r--chromium/ui/views/controls/menu/native_menu_win.cc40
-rw-r--r--chromium/ui/views/controls/menu/native_menu_win.h16
-rw-r--r--chromium/ui/views/controls/menu/submenu_view.cc21
-rw-r--r--chromium/ui/views/controls/menu/submenu_view.h10
-rw-r--r--chromium/ui/views/controls/native/native_view_host_mac.h5
-rw-r--r--chromium/ui/views/controls/prefix_delegate.h9
-rw-r--r--chromium/ui/views/controls/prefix_selector.cc19
-rw-r--r--chromium/ui/views/controls/prefix_selector.h8
-rw-r--r--chromium/ui/views/controls/prefix_selector_unittest.cc22
-rw-r--r--chromium/ui/views/controls/progress_bar.cc12
-rw-r--r--chromium/ui/views/controls/progress_bar.h7
-rw-r--r--chromium/ui/views/controls/scroll_view.cc1
-rw-r--r--chromium/ui/views/controls/scroll_view_unittest.cc106
-rw-r--r--chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm4
-rw-r--r--chromium/ui/views/controls/separator.cc18
-rw-r--r--chromium/ui/views/controls/separator.h9
-rw-r--r--chromium/ui/views/controls/separator_unittest.cc152
-rw-r--r--chromium/ui/views/controls/styled_label.cc48
-rw-r--r--chromium/ui/views/controls/styled_label.h14
-rw-r--r--chromium/ui/views/controls/styled_label_unittest.cc78
-rw-r--r--chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc60
-rw-r--r--chromium/ui/views/controls/tabbed_pane/tabbed_pane.h8
-rw-r--r--chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm6
-rw-r--r--chromium/ui/views/controls/table/table_grouper.h6
-rw-r--r--chromium/ui/views/controls/table/table_header.cc76
-rw-r--r--chromium/ui/views/controls/table/table_header.h10
-rw-r--r--chromium/ui/views/controls/table/table_utils.cc28
-rw-r--r--chromium/ui/views/controls/table/table_utils.h13
-rw-r--r--chromium/ui/views/controls/table/table_view.cc453
-rw-r--r--chromium/ui/views/controls/table/table_view.h79
-rw-r--r--chromium/ui/views/controls/table/table_view_unittest.cc292
-rw-r--r--chromium/ui/views/controls/table/test_table_model.cc8
-rw-r--r--chromium/ui/views/controls/table/test_table_model.h10
-rw-r--r--chromium/ui/views/controls/textfield/textfield.cc74
-rw-r--r--chromium/ui/views/controls/textfield/textfield.h21
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model.cc8
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model.h2
-rw-r--r--chromium/ui/views/controls/textfield/textfield_model_unittest.cc4
-rw-r--r--chromium/ui/views/controls/textfield/textfield_unittest.cc112
-rw-r--r--chromium/ui/views/controls/tree/tree_view.cc65
-rw-r--r--chromium/ui/views/controls/tree/tree_view.h10
-rw-r--r--chromium/ui/views/controls/tree/tree_view_unittest.cc50
-rw-r--r--chromium/ui/views/controls/webview/webview_unittest.cc18
83 files changed, 2427 insertions, 1620 deletions
diff --git a/chromium/ui/views/controls/animated_image_view.cc b/chromium/ui/views/controls/animated_image_view.cc
index 6bc5217e38f..ba605157123 100644
--- a/chromium/ui/views/controls/animated_image_view.cc
+++ b/chromium/ui/views/controls/animated_image_view.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/check.h"
+#include "base/trace_event/trace_event.h"
#include "cc/paint/skottie_wrapper.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/compositor/compositor.h"
@@ -48,25 +49,24 @@ void AnimatedImageView::SetAnimatedImage(
SchedulePaint();
}
-void AnimatedImageView::Play(lottie::Animation::Style style) {
- DCHECK(animated_image_);
- Play(base::TimeDelta(), animated_image_->GetAnimationDuration(), style);
-}
-
-void AnimatedImageView::Play(base::TimeDelta start_offset,
- base::TimeDelta duration,
- lottie::Animation::Style style) {
+void AnimatedImageView::Play(
+ absl::optional<lottie::Animation::PlaybackConfig> playback_config) {
DCHECK(animated_image_);
if (state_ == State::kPlaying)
return;
state_ = State::kPlaying;
- set_check_active_duration(style != lottie::Animation::Style::kLoop);
+ if (!playback_config) {
+ playback_config =
+ lottie::Animation::PlaybackConfig::CreateDefault(*animated_image_);
+ }
+ set_check_active_duration(playback_config->style !=
+ lottie::Animation::Style::kLoop);
SetCompositorFromWidget();
- animated_image_->StartSubsection(start_offset, duration, style);
+ animated_image_->Start(std::move(playback_config));
}
void AnimatedImageView::Stop() {
@@ -126,6 +126,8 @@ void AnimatedImageView::RemovedFromWidget() {
}
void AnimatedImageView::OnAnimationStep(base::TimeTicks timestamp) {
+ TRACE_EVENT1("views", "AnimatedImageView::OnAnimationStep", "timestamp",
+ timestamp);
previous_timestamp_ = timestamp;
SchedulePaint();
}
diff --git a/chromium/ui/views/controls/animated_image_view.h b/chromium/ui/views/controls/animated_image_view.h
index 04d6dea759c..4c880b7b0d2 100644
--- a/chromium/ui/views/controls/animated_image_view.h
+++ b/chromium/ui/views/controls/animated_image_view.h
@@ -10,6 +10,7 @@
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/compositor/compositor_animation_observer.h"
#include "ui/gfx/geometry/vector2d.h"
@@ -22,10 +23,6 @@ namespace gfx {
class Canvas;
}
-namespace lottie {
-class Animation;
-}
-
namespace ui {
class Compositor;
}
@@ -61,13 +58,12 @@ class VIEWS_EXPORT AnimatedImageView : public ImageViewBase,
// will result in stopping the current animation.
void SetAnimatedImage(std::unique_ptr<lottie::Animation> animated_image);
- // Plays the animation in loop and must only be called when this view has
+ // Plays the animation and must only be called when this view has
// access to a widget.
- void Play(lottie::Animation::Style style = lottie::Animation::Style::kLoop);
- // Version of the above that mirrors lottie::Animation::StartSubsection().
- void Play(base::TimeDelta start_offset,
- base::TimeDelta duration,
- lottie::Animation::Style style = lottie::Animation::Style::kLoop);
+ //
+ // If a null |playback_config| is provided, the default one is used.
+ void Play(absl::optional<lottie::Animation::PlaybackConfig> playback_config =
+ absl::nullopt);
// Stops any animation and resets it to the start frame.
void Stop();
diff --git a/chromium/ui/views/controls/animated_image_view_unittest.cc b/chromium/ui/views/controls/animated_image_view_unittest.cc
index 6659862015d..fa41cc67d1f 100644
--- a/chromium/ui/views/controls/animated_image_view_unittest.cc
+++ b/chromium/ui/views/controls/animated_image_view_unittest.cc
@@ -58,10 +58,8 @@ class AnimatedImageViewTest : public ViewsTestBase {
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget_.Init(std::move(params));
- auto view = std::make_unique<AnimatedImageView>();
- view->SetUseDefaultFillLayout(true);
- view_ = view.get();
- widget_.SetContentsView(std::move(view));
+ view_ = widget_.SetContentsView(std::make_unique<AnimatedImageView>());
+ view_->SetUseDefaultFillLayout(true);
widget_.Show();
}
@@ -97,7 +95,7 @@ TEST_F(AnimatedImageViewTest, PaintsWithAdditionalTranslation) {
view_->SetAnimatedImage(CreateAnimationWithSize(gfx::Size(80, 80)));
view_->SetVerticalAlignment(ImageViewBase::Alignment::kCenter);
view_->SetHorizontalAlignment(ImageViewBase::Alignment::kCenter);
- widget_.GetContentsView()->Layout();
+ RunScheduledLayout(view_);
view_->Play();
static constexpr float kExpectedDefaultOrigin =
diff --git a/chromium/ui/views/controls/button/label_button_unittest.cc b/chromium/ui/views/controls/button/label_button_unittest.cc
index 0524fca9e01..97dedf98af5 100644
--- a/chromium/ui/views/controls/button/label_button_unittest.cc
+++ b/chromium/ui/views/controls/button/label_button_unittest.cc
@@ -90,8 +90,12 @@ class LabelButtonTest : public test::WidgetTest {
// Windows is platform-dependent.
test_widget_->Show();
- button_ = test_widget_->GetContentsView()->AddChildView(
- std::make_unique<TestLabelButton>());
+ // Place the button into a separate container view which itself does no
+ // layouts. This will isolate the button from the client view which does
+ // a fill layout by default.
+ auto* container =
+ test_widget_->client_view()->AddChildView(std::make_unique<View>());
+ button_ = container->AddChildView(std::make_unique<TestLabelButton>());
// Establish the expected text colors for testing changes due to state.
themed_normal_text_color_ =
@@ -425,12 +429,12 @@ TEST_F(LabelButtonTest, ImageAlignmentWithMultilineLabel) {
button_->SetImage(Button::STATE_NORMAL, image);
button_->SetBoundsRect(gfx::Rect(button_->GetPreferredSize()));
- button_->Layout();
+ RunScheduledLayout(button_);
int y_origin_centered = button_->image()->origin().y();
button_->SetBoundsRect(gfx::Rect(button_->GetPreferredSize()));
button_->SetImageCentered(false);
- button_->Layout();
+ RunScheduledLayout(button_);
int y_origin_not_centered = button_->image()->origin().y();
EXPECT_LT(y_origin_not_centered, y_origin_centered);
@@ -463,17 +467,17 @@ TEST_F(LabelButtonTest, LabelAndImage) {
gfx::Size button_size = button_->GetPreferredSize();
button_size.Enlarge(50, 0);
button_->SetSize(button_size);
- button_->Layout();
+ RunScheduledLayout(button_);
EXPECT_LT(button_->image()->bounds().right(), button_->label()->bounds().x());
int left_align_label_midpoint = button_->label()->bounds().CenterPoint().x();
button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
- button_->Layout();
+ RunScheduledLayout(button_);
EXPECT_LT(button_->image()->bounds().right(), button_->label()->bounds().x());
int center_align_label_midpoint =
button_->label()->bounds().CenterPoint().x();
EXPECT_LT(left_align_label_midpoint, center_align_label_midpoint);
button_->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
- button_->Layout();
+ RunScheduledLayout(button_);
EXPECT_LT(button_->label()->bounds().right(), button_->image()->bounds().x());
button_->SetText(std::u16string());
@@ -521,7 +525,7 @@ TEST_F(LabelButtonTest, LabelWrapAndImageAlignment) {
gfx::Size preferred_size = button_->GetPreferredSize();
preferred_size.set_height(button_->GetHeightForWidth(preferred_size.width()));
button_->SetSize(preferred_size);
- button_->Layout();
+ RunScheduledLayout(button_);
EXPECT_EQ(preferred_size.width(),
image.width() + image_spacing + text_wrap_width);
@@ -631,7 +635,7 @@ TEST_F(LabelButtonTest, ChangeTextSize) {
// is increased.
button_->SetText(longer_text);
EXPECT_TRUE(ViewTestApi(button_).needs_layout());
- button_->Layout();
+ RunScheduledLayout(button_);
EXPECT_GT(button_->label()->bounds().width(), original_label_width * 2);
EXPECT_GT(button_->GetPreferredSize().width(), original_width * 2);
@@ -639,7 +643,7 @@ TEST_F(LabelButtonTest, ChangeTextSize) {
// text is restored.
button_->SetText(text);
EXPECT_TRUE(ViewTestApi(button_).needs_layout());
- button_->Layout();
+ RunScheduledLayout(button_);
EXPECT_EQ(original_label_width, button_->label()->bounds().width());
EXPECT_EQ(original_width, button_->GetPreferredSize().width());
}
@@ -723,7 +727,7 @@ TEST_F(LabelButtonTest, ImageOrLabelGetClipped) {
// The border size + the content height is more than button's preferred size.
button_->SetBorder(CreateEmptyBorder(
gfx::Insets::TLBR(image_size / 2, 0, image_size / 2, 0)));
- button_->Layout();
+ RunScheduledLayout(button_);
// Ensure that content (image and label) doesn't get clipped by the border.
EXPECT_GE(button_->image()->height(), image_size);
diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc
index b8d41cfeb67..8f521332e66 100644
--- a/chromium/ui/views/controls/combobox/combobox.cc
+++ b/chromium/ui/views/controls/combobox/combobox.cc
@@ -18,7 +18,6 @@
#include "ui/base/ime/input_method.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
-#include "ui/base/models/menu_model.h"
#include "ui/base/ui_base_types.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
@@ -33,6 +32,7 @@
#include "ui/views/background.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/button_controller.h"
+#include "ui/views/controls/combobox/combobox_menu_model.h"
#include "ui/views/controls/combobox/combobox_util.h"
#include "ui/views/controls/combobox/empty_combobox_model.h"
#include "ui/views/controls/focus_ring.h"
@@ -44,15 +44,13 @@
#include "ui/views/mouse_constants.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/style/typography.h"
+#include "ui/views/view_utils.h"
#include "ui/views/widget/widget.h"
namespace views {
namespace {
-// Used to indicate that no item is currently selected by the user.
-constexpr int kNoSelection = -1;
-
SkColor GetTextColorForEnableState(const Combobox& combobox, bool enabled) {
const int style = enabled ? style::STYLE_PRIMARY : style::STYLE_DISABLED;
return style::GetColor(combobox, style::CONTEXT_TEXTFIELD, style);
@@ -71,6 +69,15 @@ class TransparentButton : public Button {
SetHasInkDropActionOnClick(true);
InkDrop::UseInkDropForSquareRipple(InkDrop::Get(this),
/*highlight_on_hover=*/false);
+ views::InkDrop::Get(this)->SetBaseColorCallback(base::BindRepeating(
+ [](Button* host) {
+ // This button will be used like a LabelButton, so use the same
+ // foreground base color as a label button.
+ return color_utils::DeriveDefaultIconColor(
+ views::style::GetColor(*host, views::style::CONTEXT_BUTTON,
+ views::style::STYLE_PRIMARY));
+ },
+ this));
InkDrop::Get(this)->SetCreateRippleCallback(base::BindRepeating(
[](Button* host) -> std::unique_ptr<views::InkDropRipple> {
return std::make_unique<views::FloodFillInkDropRipple>(
@@ -97,124 +104,23 @@ class TransparentButton : public Button {
double GetAnimationValue() const {
return hover_animation().GetCurrentValue();
}
-};
-
-#if !BUILDFLAG(IS_MAC)
-// Returns the next or previous valid index (depending on |increment|'s value).
-// Skips separator or disabled indices. Returns -1 if there is no valid adjacent
-// index.
-int GetAdjacentIndex(ui::ComboboxModel* model, int increment, int index) {
- DCHECK(increment == -1 || increment == 1);
-
- index += increment;
- while (index >= 0 && index < model->GetItemCount()) {
- if (!model->IsItemSeparatorAt(index) || !model->IsItemEnabledAt(index))
- return index;
- index += increment;
- }
- return kNoSelection;
-}
-#endif
-
-} // namespace
-
-// Adapts a ui::ComboboxModel to a ui::MenuModel.
-class Combobox::ComboboxMenuModel : public ui::MenuModel {
- public:
- ComboboxMenuModel(Combobox* owner, ui::ComboboxModel* model)
- : owner_(owner), model_(model) {}
- ComboboxMenuModel(const ComboboxMenuModel&) = delete;
- ComboboxMenuModel& operator&(const ComboboxMenuModel&) = delete;
- ~ComboboxMenuModel() override = default;
-
- private:
- bool UseCheckmarks() const {
- return MenuConfig::instance().check_selected_combobox_item;
- }
- // Overridden from MenuModel:
- bool HasIcons() const override {
- for (int i = 0; i < GetItemCount(); ++i) {
- if (!GetIconAt(i).IsEmpty())
- return true;
+ void UpdateInkDrop(bool show_on_press_and_hover) {
+ if (show_on_press_and_hover) {
+ // We must use UseInkDropForFloodFillRipple here because
+ // UseInkDropForSquareRipple hides the InkDrop when the ripple effect is
+ // active instead of layering underneath it causing flashing.
+ InkDrop::UseInkDropForFloodFillRipple(InkDrop::Get(this),
+ /*highlight_on_hover=*/true);
+ } else {
+ InkDrop::UseInkDropForSquareRipple(InkDrop::Get(this),
+ /*highlight_on_hover=*/false);
}
- return false;
- }
-
- int GetItemCount() const override { return model_->GetItemCount(); }
-
- ItemType GetTypeAt(int index) const override {
- if (model_->IsItemSeparatorAt(index))
- return TYPE_SEPARATOR;
- return UseCheckmarks() ? TYPE_CHECK : TYPE_COMMAND;
- }
-
- ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override {
- return ui::NORMAL_SEPARATOR;
- }
-
- int GetCommandIdAt(int index) const override {
- // Define the id of the first item in the menu (since it needs to be > 0)
- constexpr int kFirstMenuItemId = 1000;
- return index + kFirstMenuItemId;
- }
-
- std::u16string GetLabelAt(int index) const override {
- // Inserting the Unicode formatting characters if necessary so that the
- // text is displayed correctly in right-to-left UIs.
- std::u16string text = model_->GetDropDownTextAt(index);
- base::i18n::AdjustStringForLocaleDirection(&text);
- return text;
- }
-
- std::u16string GetSecondaryLabelAt(int index) const override {
- std::u16string text = model_->GetDropDownSecondaryTextAt(index);
- base::i18n::AdjustStringForLocaleDirection(&text);
- return text;
}
-
- bool IsItemDynamicAt(int index) const override { return true; }
-
- const gfx::FontList* GetLabelFontListAt(int index) const override {
- return &owner_->GetFontList();
- }
-
- bool GetAcceleratorAt(int index,
- ui::Accelerator* accelerator) const override {
- return false;
- }
-
- bool IsItemCheckedAt(int index) const override {
- return UseCheckmarks() && index == owner_->selected_index_;
- }
-
- int GetGroupIdAt(int index) const override { return -1; }
-
- ui::ImageModel GetIconAt(int index) const override {
- return model_->GetDropDownIconAt(index);
- }
-
- ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override {
- return nullptr;
- }
-
- bool IsEnabledAt(int index) const override {
- return model_->IsItemEnabledAt(index);
- }
-
- void ActivatedAt(int index) override {
- owner_->SetSelectedIndex(index);
- owner_->OnPerformAction();
- }
-
- void ActivatedAt(int index, int event_flags) override { ActivatedAt(index); }
-
- MenuModel* GetSubmenuModelAt(int index) const override { return nullptr; }
-
- raw_ptr<Combobox> owner_; // Weak. Owns this.
- raw_ptr<ui::ComboboxModel> model_; // Weak.
};
+} // namespace
+
////////////////////////////////////////////////////////////////////////////////
// Combobox, public:
@@ -241,9 +147,10 @@ Combobox::Combobox(ui::ComboboxModel* model, int text_context, int text_style)
SetFocusBehavior(FocusBehavior::ALWAYS);
#endif
+ SetBackgroundColorId(ui::kColorTextfieldBackground);
UpdateBorder();
- arrow_button_->SetVisible(true);
+ arrow_button_->SetVisible(should_show_arrow_);
AddChildView(arrow_button_.get());
// A layer is applied to make sure that canvas bounds are snapped to pixel
@@ -265,7 +172,7 @@ const gfx::FontList& Combobox::GetFontList() const {
return style::GetFont(text_context_, text_style_);
}
-void Combobox::SetSelectedIndex(int index) {
+void Combobox::SetSelectedIndex(absl::optional<size_t> index) {
if (selected_index_ == index)
return;
// TODO(pbos): Add (D)CHECKs to validate the selected index.
@@ -284,7 +191,7 @@ base::CallbackListSubscription Combobox::AddSelectedIndexChangedCallback(
}
bool Combobox::SelectValue(const std::u16string& value) {
- for (int i = 0; i < GetModel()->GetItemCount(); ++i) {
+ for (size_t i = 0; i < GetModel()->GetItemCount(); ++i) {
if (value == GetModel()->GetItemAt(i)) {
SetSelectedIndex(i);
return true;
@@ -353,6 +260,22 @@ void Combobox::SetInvalid(bool invalid) {
OnPropertyChanged(&selected_index_, kPropertyEffectsPaint);
}
+void Combobox::SetBorderColorId(ui::ColorId color_id) {
+ border_color_id_ = color_id;
+ UpdateBorder();
+}
+
+void Combobox::SetBackgroundColorId(ui::ColorId color_id) {
+ SetBackground(CreateThemedRoundedRectBackground(
+ color_id, FocusableBorder::kCornerRadiusDp));
+}
+
+void Combobox::SetEventHighlighting(bool should_highlight) {
+ should_highlight_ = should_highlight;
+ AsViewClass<TransparentButton>(arrow_button_)
+ ->UpdateInkDrop(should_highlight);
+}
+
void Combobox::SetSizeToLargestLabel(bool size_to_largest_label) {
if (size_to_largest_label_ == size_to_largest_label)
return;
@@ -368,29 +291,25 @@ bool Combobox::IsMenuRunning() const {
void Combobox::OnThemeChanged() {
View::OnThemeChanged();
- SetBackground(
- CreateBackgroundFromPainter(Painter::CreateSolidRoundRectPainter(
- GetColorProvider()->GetColor(ui::kColorTextfieldBackground),
- FocusableBorder::kCornerRadiusDp)));
OnContentSizeMaybeChanged();
}
-int Combobox::GetRowCount() {
+size_t Combobox::GetRowCount() {
return GetModel()->GetItemCount();
}
-int Combobox::GetSelectedRow() {
+absl::optional<size_t> Combobox::GetSelectedRow() {
return selected_index_;
}
-void Combobox::SetSelectedRow(int row) {
- int prev_index = selected_index_;
+void Combobox::SetSelectedRow(absl::optional<size_t> row) {
+ absl::optional<size_t> prev_index = selected_index_;
SetSelectedIndex(row);
if (selected_index_ != prev_index)
OnPerformAction();
}
-std::u16string Combobox::GetTextForRow(int row) {
+std::u16string Combobox::GetTextForRow(size_t row) {
return GetModel()->IsItemSeparatorAt(row) ? std::u16string()
: GetModel()->GetItemAt(row);
}
@@ -404,11 +323,17 @@ gfx::Size Combobox::CalculatePreferredSize() const {
// The preferred size will drive the local bounds which in turn is used to set
// the minimum width for the dropdown list.
- const int width = std::max(kMinComboboxWidth, content_size_.width()) +
- LayoutProvider::Get()->GetDistanceMetric(
- DISTANCE_TEXTFIELD_HORIZONTAL_TEXT_PADDING) *
- 2 +
- kComboboxArrowContainerWidth + GetInsets().width();
+ int width = std::max(kMinComboboxWidth, content_size_.width()) +
+ LayoutProvider::Get()->GetDistanceMetric(
+ DISTANCE_TEXTFIELD_HORIZONTAL_TEXT_PADDING) *
+ 2 +
+ GetInsets().width();
+
+ // If an arrow is being shown, add extra width to include that arrow.
+ if (should_show_arrow_) {
+ width += kComboboxArrowContainerWidth;
+ }
+
const int height = LayoutProvider::GetControlHeightForFont(
text_context_, text_style_, GetFontList());
return gfx::Size(width, height);
@@ -431,76 +356,86 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) {
// TODO(oshima): handle IME.
DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED);
- // TODO(pbos): Do we need to handle selected_index_ == -1 for unselected here?
- // Ditto on handling an empty model?
- DCHECK_GE(selected_index_, 0);
- DCHECK_LT(selected_index_, GetModel()->GetItemCount());
- if (selected_index_ < 0 || selected_index_ >= GetModel()->GetItemCount())
- SetSelectedIndex(0);
+ DCHECK(selected_index_.has_value());
+ DCHECK_LT(selected_index_.value(), GetModel()->GetItemCount());
- bool show_menu = false;
- int new_index = kNoSelection;
- switch (e.key_code()) {
#if BUILDFLAG(IS_MAC)
- case ui::VKEY_DOWN:
- case ui::VKEY_UP:
- case ui::VKEY_SPACE:
- case ui::VKEY_HOME:
- case ui::VKEY_END:
- // On Mac, navigation keys should always just show the menu first.
- show_menu = true;
- break;
+ if (e.key_code() != ui::VKEY_DOWN && e.key_code() != ui::VKEY_UP &&
+ e.key_code() != ui::VKEY_SPACE && e.key_code() != ui::VKEY_HOME &&
+ e.key_code() != ui::VKEY_END) {
+ return false;
+ }
+ ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
+ return true;
#else
+ const auto index_at_or_after = [](ui::ComboboxModel* model,
+ size_t index) -> absl::optional<size_t> {
+ for (; index < model->GetItemCount(); ++index) {
+ if (!model->IsItemSeparatorAt(index) && model->IsItemEnabledAt(index))
+ return index;
+ }
+ return absl::nullopt;
+ };
+ const auto index_before = [](ui::ComboboxModel* model,
+ size_t index) -> absl::optional<size_t> {
+ for (; index > 0; --index) {
+ const auto prev = index - 1;
+ if (!model->IsItemSeparatorAt(prev) && model->IsItemEnabledAt(prev))
+ return prev;
+ }
+ return absl::nullopt;
+ };
+
+ absl::optional<size_t> new_index;
+ switch (e.key_code()) {
// Show the menu on F4 without modifiers.
case ui::VKEY_F4:
if (e.IsAltDown() || e.IsAltGrDown() || e.IsControlDown())
return false;
- show_menu = true;
- break;
+ ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
+ return true;
// Move to the next item if any, or show the menu on Alt+Down like Windows.
case ui::VKEY_DOWN:
- if (e.IsAltDown())
- show_menu = true;
- else
- new_index = GetAdjacentIndex(GetModel(), 1, selected_index_);
+ if (e.IsAltDown()) {
+ ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
+ return true;
+ }
+ new_index = index_at_or_after(GetModel(), selected_index_.value() + 1);
break;
// Move to the end of the list.
case ui::VKEY_END:
case ui::VKEY_NEXT: // Page down.
- new_index = GetAdjacentIndex(GetModel(), -1, GetModel()->GetItemCount());
+ new_index = index_before(GetModel(), GetModel()->GetItemCount());
break;
// Move to the beginning of the list.
case ui::VKEY_HOME:
case ui::VKEY_PRIOR: // Page up.
- new_index = GetAdjacentIndex(GetModel(), 1, -1);
+ new_index = index_at_or_after(GetModel(), 0);
break;
// Move to the previous item if any.
case ui::VKEY_UP:
- new_index = GetAdjacentIndex(GetModel(), -1, selected_index_);
+ new_index = index_before(GetModel(), selected_index_.value());
break;
case ui::VKEY_RETURN:
case ui::VKEY_SPACE:
- show_menu = true;
- break;
-#endif // BUILDFLAG(IS_MAC)
+ ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
+ return true;
+
default:
return false;
}
- if (show_menu) {
- ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
- } else if (new_index != selected_index_ && new_index != kNoSelection) {
- DCHECK(!GetModel()->IsItemSeparatorAt(new_index));
+ if (new_index.has_value()) {
SetSelectedIndex(new_index);
OnPerformAction();
}
-
return true;
+#endif // BUILDFLAG(IS_MAC)
}
void Combobox::OnPaint(gfx::Canvas* canvas) {
@@ -540,14 +475,14 @@ void Combobox::GetAccessibleNodeData(ui::AXNodeData* node_data) {
}
node_data->SetName(accessible_name_);
- node_data->SetValue(model_->GetItemAt(selected_index_));
+ node_data->SetValue(model_->GetItemAt(selected_index_.value()));
if (GetEnabled()) {
node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kOpen);
}
node_data->AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
- selected_index_);
+ base::checked_cast<int>(selected_index_.value()));
node_data->AddIntAttribute(ax::mojom::IntAttribute::kSetSize,
- model_->GetItemCount());
+ base::checked_cast<int>(model_->GetItemCount()));
}
bool Combobox::HandleAccessibleAction(const ui::AXActionData& action_data) {
@@ -571,7 +506,7 @@ void Combobox::OnComboboxModelChanged(ui::ComboboxModel* model) {
// default index.
if (selected_index_ >= model_->GetItemCount() ||
model_->GetItemCount() == 0 ||
- model_->IsItemSeparatorAt(selected_index_)) {
+ model_->IsItemSeparatorAt(selected_index_.value())) {
SetSelectedIndex(model_->GetDefaultIndex());
}
@@ -592,6 +527,8 @@ const std::unique_ptr<ui::ComboboxModel>& Combobox::GetOwnedModel() const {
void Combobox::UpdateBorder() {
std::unique_ptr<FocusableBorder> border(new FocusableBorder());
+ if (border_color_id_.has_value())
+ border->SetColorId(border_color_id_.value());
if (invalid_)
border->SetColorId(ui::kColorAlertHighSeverity);
SetBorder(std::move(border));
@@ -613,46 +550,53 @@ void Combobox::PaintIconAndText(gfx::Canvas* canvas) {
int y = insets.top();
int contents_height = height() - insets.height();
+ DCHECK(selected_index_.has_value());
+ DCHECK_LT(selected_index_.value(), GetModel()->GetItemCount());
+
// Draw the icon.
- ui::ImageModel icon = GetModel()->GetIconAt(selected_index_);
+ ui::ImageModel icon = GetModel()->GetIconAt(selected_index_.value());
if (!icon.IsEmpty()) {
gfx::ImageSkia icon_skia = icon.Rasterize(GetColorProvider());
int icon_y = y + (contents_height - icon_skia.height()) / 2;
gfx::Rect icon_bounds(x, icon_y, icon_skia.width(), icon_skia.height());
AdjustBoundsForRTLUI(&icon_bounds);
canvas->DrawImageInt(icon_skia, icon_bounds.x(), icon_bounds.y());
- x += icon_skia.width() + LayoutProvider::Get()->GetDistanceMetric(
- DISTANCE_RELATED_LABEL_HORIZONTAL);
+ x += icon_skia.width();
}
// Draw the text.
SkColor text_color = GetTextColorForEnableState(*this, GetEnabled());
- // TODO(pbos): Do we need to handle selected_index_ == -1 for unselected here?
- // Ditto on handling an empty model?
- DCHECK_GE(selected_index_, 0);
- DCHECK_LT(selected_index_, GetModel()->GetItemCount());
- if (selected_index_ < 0 || selected_index_ >= GetModel()->GetItemCount())
- SetSelectedIndex(0);
-
- std::u16string text = GetModel()->GetItemAt(selected_index_);
+ std::u16string text = GetModel()->GetItemAt(selected_index_.value());
+ const gfx::FontList& font_list = GetFontList();
- int disclosure_arrow_offset = width() - kComboboxArrowContainerWidth;
+ // If the text is not empty, add padding between it and the icon. If there
+ // was an empty icon, this padding is not necessary.
+ if (!text.empty() && !icon.IsEmpty()) {
+ x += LayoutProvider::Get()->GetDistanceMetric(
+ DISTANCE_RELATED_LABEL_HORIZONTAL);
+ }
- const gfx::FontList& font_list = GetFontList();
+ // The total width of the text is the minimum of either the string width,
+ // or the available space, accounting for optional arrow.
int text_width = gfx::GetStringWidth(text, font_list);
- text_width =
- std::min(text_width, disclosure_arrow_offset - insets.right() - x);
+ int available_width = width() - x - insets.right();
+ if (should_show_arrow_) {
+ available_width -= kComboboxArrowContainerWidth;
+ }
+ text_width = std::min(text_width, available_width);
gfx::Rect text_bounds(x, y, text_width, contents_height);
AdjustBoundsForRTLUI(&text_bounds);
canvas->DrawStringRect(text, font_list, text_color, text_bounds);
- gfx::Rect arrow_bounds(disclosure_arrow_offset, 0,
- kComboboxArrowContainerWidth, height());
- arrow_bounds.ClampToCenteredSize(ComboboxArrowSize());
- AdjustBoundsForRTLUI(&arrow_bounds);
-
- PaintComboboxArrow(text_color, arrow_bounds, canvas);
+ // Draw the arrow.
+ if (should_show_arrow_) {
+ gfx::Rect arrow_bounds(width() - kComboboxArrowContainerWidth, 0,
+ kComboboxArrowContainerWidth, height());
+ arrow_bounds.ClampToCenteredSize(ComboboxArrowSize());
+ AdjustBoundsForRTLUI(&arrow_bounds);
+ PaintComboboxArrow(text_color, arrow_bounds, canvas);
+ }
}
void Combobox::ArrowButtonPressed(const ui::Event& event) {
@@ -678,6 +622,10 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) {
View::ConvertPointToScreen(this, &menu_position);
gfx::Rect bounds(menu_position, lb.size());
+ // If check marks exist in the combobox, adjust with bounds width to account
+ // for them.
+ if (!size_to_largest_label_)
+ bounds.set_width(MaybeAdjustWidthForCheckmarks(bounds.width()));
Button::ButtonState original_state = arrow_button_->GetState();
arrow_button_->SetState(Button::STATE_PRESSED);
@@ -690,18 +638,34 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) {
base::BindRepeating(&Combobox::OnMenuClosed, base::Unretained(this),
original_state));
}
+ if (should_highlight_) {
+ InkDrop::Get(arrow_button_)
+ ->AnimateToState(InkDropState::ACTIVATED, nullptr);
+ }
menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds,
MenuAnchorPosition::kTopLeft, source_type);
NotifyAccessibilityEvent(ax::mojom::Event::kExpandedChanged, true);
}
void Combobox::OnMenuClosed(Button::ButtonState original_button_state) {
+ if (should_highlight_) {
+ InkDrop::Get(arrow_button_)
+ ->AnimateToState(InkDropState::DEACTIVATED, nullptr);
+ InkDrop::Get(arrow_button_)->GetInkDrop()->SetHovered(IsMouseHovered());
+ }
menu_runner_.reset();
arrow_button_->SetState(original_button_state);
closed_time_ = base::TimeTicks::Now();
NotifyAccessibilityEvent(ax::mojom::Event::kExpandedChanged, true);
}
+void Combobox::MenuSelectionAt(size_t index) {
+ if (!menu_selection_at_callback_ || !menu_selection_at_callback_.Run(index)) {
+ SetSelectedIndex(index);
+ OnPerformAction();
+ }
+}
+
void Combobox::OnPerformAction() {
NotifyAccessibilityEvent(ax::mojom::Event::kValueChanged, true);
SchedulePaint();
@@ -716,33 +680,50 @@ gfx::Size Combobox::GetContentSize() const {
const gfx::FontList& font_list = GetFontList();
int height = font_list.GetHeight();
int width = 0;
- for (int i = 0; i < GetModel()->GetItemCount(); ++i) {
+ for (size_t i = 0; i < GetModel()->GetItemCount(); ++i) {
if (model_->IsItemSeparatorAt(i))
continue;
if (size_to_largest_label_ || i == selected_index_) {
- int item_width = gfx::GetStringWidth(GetModel()->GetItemAt(i), font_list);
+ int item_width = 0;
ui::ImageModel icon = GetModel()->GetIconAt(i);
+ std::u16string text = GetModel()->GetItemAt(i);
if (!icon.IsEmpty()) {
gfx::ImageSkia icon_skia;
if (GetWidget())
icon_skia = icon.Rasterize(GetColorProvider());
- item_width +=
- icon_skia.width() + LayoutProvider::Get()->GetDistanceMetric(
- DISTANCE_RELATED_LABEL_HORIZONTAL);
- if (MenuConfig::instance().check_selected_combobox_item) {
- item_width +=
- kMenuCheckSize + LayoutProvider::Get()->GetDistanceMetric(
- DISTANCE_RELATED_BUTTON_HORIZONTAL);
- }
+ item_width += icon_skia.width();
height = std::max(height, icon_skia.height());
+
+ // If both the text and icon are not empty, include padding between.
+ // We do not include this padding if there is no icon present.
+ if (!text.empty()) {
+ item_width += LayoutProvider::Get()->GetDistanceMetric(
+ DISTANCE_RELATED_LABEL_HORIZONTAL);
+ }
+ }
+
+ // If text is not empty, the content size needs to include the text width
+ if (!text.empty()) {
+ item_width += gfx::GetStringWidth(GetModel()->GetItemAt(i), font_list);
}
+
+ if (size_to_largest_label_)
+ item_width = MaybeAdjustWidthForCheckmarks(item_width);
width = std::max(width, item_width);
}
}
return gfx::Size(width, height);
}
+int Combobox::MaybeAdjustWidthForCheckmarks(int original_width) const {
+ return MenuConfig::instance().check_selected_combobox_item
+ ? original_width + kMenuCheckSize +
+ LayoutProvider::Get()->GetDistanceMetric(
+ DISTANCE_RELATED_BUTTON_HORIZONTAL)
+ : original_width;
+}
+
void Combobox::OnContentSizeMaybeChanged() {
content_size_ = GetContentSize();
PreferredSizeChanged();
@@ -758,7 +739,7 @@ BEGIN_METADATA(Combobox, View)
ADD_PROPERTY_METADATA(base::RepeatingClosure, Callback)
ADD_PROPERTY_METADATA(std::unique_ptr<ui::ComboboxModel>, OwnedModel)
ADD_PROPERTY_METADATA(ui::ComboboxModel*, Model)
-ADD_PROPERTY_METADATA(int, SelectedIndex)
+ADD_PROPERTY_METADATA(absl::optional<size_t>, SelectedIndex)
ADD_PROPERTY_METADATA(bool, Invalid)
ADD_PROPERTY_METADATA(bool, SizeToLargestLabel)
ADD_PROPERTY_METADATA(std::u16string, AccessibleName)
diff --git a/chromium/ui/views/controls/combobox/combobox.h b/chromium/ui/views/controls/combobox/combobox.h
index 1a3f533256e..cebf64d887f 100644
--- a/chromium/ui/views/controls/combobox/combobox.h
+++ b/chromium/ui/views/controls/combobox/combobox.h
@@ -14,6 +14,8 @@
#include "base/time/time.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/models/combobox_model_observer.h"
+#include "ui/base/models/menu_model.h"
+#include "ui/color/color_id.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/prefix_delegate.h"
#include "ui/views/metadata/view_factory.h"
@@ -43,6 +45,8 @@ class VIEWS_EXPORT Combobox : public View,
public:
METADATA_HEADER(Combobox);
+ using MenuSelectionAtCallback = base::RepeatingCallback<bool(size_t index)>;
+
static constexpr int kDefaultComboboxTextContext = style::CONTEXT_BUTTON;
static constexpr int kDefaultComboboxTextStyle = style::STYLE_PRIMARY;
@@ -69,12 +73,20 @@ class VIEWS_EXPORT Combobox : public View,
callback_ = std::move(callback);
}
+ // Set menu model.
+ void SetMenuModel(std::unique_ptr<ui::MenuModel> menu_model) {
+ menu_model_ = std::move(menu_model);
+ }
+
// Gets/Sets the selected index.
- int GetSelectedIndex() const { return selected_index_; }
- void SetSelectedIndex(int index);
+ absl::optional<size_t> GetSelectedIndex() const { return selected_index_; }
+ void SetSelectedIndex(absl::optional<size_t> index);
[[nodiscard]] base::CallbackListSubscription AddSelectedIndexChangedCallback(
views::PropertyChangedCallback callback);
+ // Called when there has been a selection from the menu.
+ void MenuSelectionAt(size_t index);
+
// Looks for the first occurrence of |value| in |model()|. If found, selects
// the found index and returns true. Otherwise simply noops and returns false.
bool SelectValue(const std::u16string& value);
@@ -99,10 +111,25 @@ class VIEWS_EXPORT Combobox : public View,
void SetInvalid(bool invalid);
bool GetInvalid() const { return invalid_; }
+ void SetBorderColorId(ui::ColorId color_id);
+ void SetBackgroundColorId(ui::ColorId color_id);
+
+ // Sets whether there should be ink drop highlighting on hover/press.
+ void SetEventHighlighting(bool should_highlight);
+
// Whether the combobox should use the largest label as the content size.
void SetSizeToLargestLabel(bool size_to_largest_label);
bool GetSizeToLargestLabel() const { return size_to_largest_label_; }
+ void SetMenuSelectionAtCallback(MenuSelectionAtCallback callback) {
+ menu_selection_at_callback_ = std::move(callback);
+ }
+
+ // Set whether the arrow should be shown to the user.
+ void SetShouldShowArrow(bool should_show_arrow) {
+ should_show_arrow_ = should_show_arrow;
+ }
+
// Use the time when combobox was closed in order for parent view to not
// treat a user event already treated by the combobox.
base::TimeTicks GetClosedTime() { return closed_time_; }
@@ -123,10 +150,10 @@ class VIEWS_EXPORT Combobox : public View,
void OnThemeChanged() override;
// Overridden from PrefixDelegate:
- int GetRowCount() override;
- int GetSelectedRow() override;
- void SetSelectedRow(int row) override;
- std::u16string GetTextForRow(int row) override;
+ size_t GetRowCount() override;
+ absl::optional<size_t> GetSelectedRow() override;
+ void SetSelectedRow(absl::optional<size_t> row) override;
+ std::u16string GetTextForRow(size_t row) override;
protected:
// Overridden from ComboboxModelObserver:
@@ -140,8 +167,6 @@ class VIEWS_EXPORT Combobox : public View,
private:
friend class test::ComboboxTestApi;
- class ComboboxMenuModel;
-
// Updates the border according to the current node_data.
void UpdateBorder();
@@ -166,6 +191,10 @@ class VIEWS_EXPORT Combobox : public View,
// Finds the size of the largest menu label.
gfx::Size GetContentSize() const;
+ // Returns the width needed to accommodate the provided width and checkmarks
+ // and padding if checkmarks should be shown.
+ int MaybeAdjustWidthForCheckmarks(int original_width) const;
+
void OnContentSizeMaybeChanged();
// Handles the clicking event.
@@ -191,12 +220,27 @@ class VIEWS_EXPORT Combobox : public View,
// Callback notified when the selected index changes.
base::RepeatingClosure callback_;
- // The current selected index; -1 and means no selection.
- int selected_index_ = -1;
+ // Callback notified when the selected index is triggered to change. If set,
+ // when a selection is made in the combobox this callback is called. If it
+ // returns true no other action is taken, if it returns false then the model
+ // will updated based on the selection.
+ MenuSelectionAtCallback menu_selection_at_callback_;
+
+ // The current selected index; nullopt means no selection.
+ absl::optional<size_t> selected_index_ = absl::nullopt;
// True when the selection is visually denoted as invalid.
bool invalid_ = false;
+ // True when there should be ink drop highlighting on hover and press.
+ bool should_highlight_ = false;
+
+ // True when the combobox should display the arrow during paint.
+ bool should_show_arrow_ = true;
+
+ // Overriding ColorId for the combobox border.
+ absl::optional<ui::ColorId> border_color_id_;
+
// The accessible name of this combobox.
std::u16string accessible_name_;
@@ -226,9 +270,10 @@ class VIEWS_EXPORT Combobox : public View,
// destroyed.
std::unique_ptr<MenuRunner> menu_runner_;
- // When true, the size of contents is defined by the selected label.
- // Otherwise, it's defined by the widest label in the menu. If this is set to
- // true, the parent view must relayout in ChildPreferredSizeChanged().
+ // When true, the size of contents is defined by the widest label in the menu.
+ // If this is set to true, the parent view must relayout in
+ // ChildPreferredSizeChanged(). When false, the size of contents is defined by
+ // the selected label
bool size_to_largest_label_ = true;
base::ScopedObservation<ui::ComboboxModel, ui::ComboboxModelObserver>
@@ -239,7 +284,7 @@ BEGIN_VIEW_BUILDER(VIEWS_EXPORT, Combobox, View)
VIEW_BUILDER_PROPERTY(base::RepeatingClosure, Callback)
VIEW_BUILDER_PROPERTY(std::unique_ptr<ui::ComboboxModel>, OwnedModel)
VIEW_BUILDER_PROPERTY(ui::ComboboxModel*, Model)
-VIEW_BUILDER_PROPERTY(int, SelectedIndex)
+VIEW_BUILDER_PROPERTY(absl::optional<size_t>, SelectedIndex)
VIEW_BUILDER_PROPERTY(bool, Invalid)
VIEW_BUILDER_PROPERTY(bool, SizeToLargestLabel)
VIEW_BUILDER_PROPERTY(std::u16string, AccessibleName)
diff --git a/chromium/ui/views/controls/combobox/combobox_menu_model.cc b/chromium/ui/views/controls/combobox/combobox_menu_model.cc
new file mode 100644
index 00000000000..156c1784882
--- /dev/null
+++ b/chromium/ui/views/controls/combobox/combobox_menu_model.cc
@@ -0,0 +1,105 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/combobox/combobox_menu_model.h"
+
+ComboboxMenuModel::ComboboxMenuModel(views::Combobox* owner,
+ ui::ComboboxModel* model)
+ : owner_(owner), model_(model) {}
+
+ComboboxMenuModel::~ComboboxMenuModel() = default;
+
+bool ComboboxMenuModel::UseCheckmarks() const {
+ return views::MenuConfig::instance().check_selected_combobox_item;
+}
+
+// Overridden from MenuModel:
+bool ComboboxMenuModel::HasIcons() const {
+ for (size_t i = 0; i < GetItemCount(); ++i) {
+ if (!GetIconAt(i).IsEmpty())
+ return true;
+ }
+ return false;
+}
+
+size_t ComboboxMenuModel::GetItemCount() const {
+ return model_->GetItemCount();
+}
+
+ui::MenuModel::ItemType ComboboxMenuModel::GetTypeAt(size_t index) const {
+ if (model_->IsItemSeparatorAt(index))
+ return TYPE_SEPARATOR;
+ return UseCheckmarks() ? TYPE_CHECK : TYPE_COMMAND;
+}
+
+ui::MenuSeparatorType ComboboxMenuModel::GetSeparatorTypeAt(
+ size_t index) const {
+ return ui::NORMAL_SEPARATOR;
+}
+
+int ComboboxMenuModel::GetCommandIdAt(size_t index) const {
+ // Define the id of the first item in the menu (since it needs to be > 0)
+ constexpr int kFirstMenuItemId = 1000;
+ return static_cast<int>(index) + kFirstMenuItemId;
+}
+
+std::u16string ComboboxMenuModel::GetLabelAt(size_t index) const {
+ // Inserting the Unicode formatting characters if necessary so that the
+ // text is displayed correctly in right-to-left UIs.
+ std::u16string text = model_->GetDropDownTextAt(index);
+ base::i18n::AdjustStringForLocaleDirection(&text);
+ return text;
+}
+
+std::u16string ComboboxMenuModel::GetSecondaryLabelAt(size_t index) const {
+ std::u16string text = model_->GetDropDownSecondaryTextAt(index);
+ base::i18n::AdjustStringForLocaleDirection(&text);
+ return text;
+}
+
+bool ComboboxMenuModel::IsItemDynamicAt(size_t index) const {
+ return true;
+}
+
+const gfx::FontList* ComboboxMenuModel::GetLabelFontListAt(size_t index) const {
+ return &owner_->GetFontList();
+}
+
+bool ComboboxMenuModel::GetAcceleratorAt(size_t index,
+ ui::Accelerator* accelerator) const {
+ return false;
+}
+
+bool ComboboxMenuModel::IsItemCheckedAt(size_t index) const {
+ return UseCheckmarks() && index == owner_->GetSelectedIndex();
+}
+
+int ComboboxMenuModel::GetGroupIdAt(size_t index) const {
+ return -1;
+}
+
+ui::ImageModel ComboboxMenuModel::GetIconAt(size_t index) const {
+ return model_->GetDropDownIconAt(index);
+}
+
+ui::ButtonMenuItemModel* ComboboxMenuModel::GetButtonMenuItemAt(
+ size_t index) const {
+ return nullptr;
+}
+
+bool ComboboxMenuModel::IsEnabledAt(size_t index) const {
+ return model_->IsItemEnabledAt(index);
+}
+
+void ComboboxMenuModel::ActivatedAt(size_t index) {
+ owner_->MenuSelectionAt(index);
+}
+
+void ComboboxMenuModel::ActivatedAt(size_t index, int event_flags) {
+ ActivatedAt(index);
+}
+
+ui::MenuModel* ComboboxMenuModel::GetSubmenuModelAt(size_t index) const {
+ return nullptr;
+}
diff --git a/chromium/ui/views/controls/combobox/combobox_menu_model.h b/chromium/ui/views/controls/combobox/combobox_menu_model.h
new file mode 100644
index 00000000000..0656b735a3a
--- /dev/null
+++ b/chromium/ui/views/controls/combobox/combobox_menu_model.h
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_MENU_MODEL_H_
+#define UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_MENU_MODEL_H_
+
+#include "base/i18n/rtl.h"
+#include "ui/base/models/combobox_model.h"
+#include "ui/base/models/image_model.h"
+#include "ui/base/models/menu_model.h"
+#include "ui/views/controls/combobox/combobox.h"
+#include "ui/views/controls/menu/menu_config.h"
+
+// Adapts a ui::ComboboxModel to a ui::MenuModel.
+class VIEWS_EXPORT ComboboxMenuModel : public ui::MenuModel {
+ public:
+ ComboboxMenuModel(views::Combobox* owner, ui::ComboboxModel* model);
+ ComboboxMenuModel(const ComboboxMenuModel&) = delete;
+ ComboboxMenuModel& operator&(const ComboboxMenuModel&) = delete;
+ ~ComboboxMenuModel() override;
+
+ protected:
+ ui::ComboboxModel* GetModel() const { return model_; }
+
+ private:
+ bool UseCheckmarks() const;
+
+ // Overridden from MenuModel:
+ bool HasIcons() const override;
+ size_t GetItemCount() const override;
+ ui::MenuModel::ItemType GetTypeAt(size_t index) const override;
+ ui::MenuSeparatorType GetSeparatorTypeAt(size_t index) const override;
+ int GetCommandIdAt(size_t index) const override;
+ std::u16string GetLabelAt(size_t index) const override;
+ std::u16string GetSecondaryLabelAt(size_t index) const override;
+ bool IsItemDynamicAt(size_t index) const override;
+ const gfx::FontList* GetLabelFontListAt(size_t index) const override;
+ bool GetAcceleratorAt(size_t index,
+ ui::Accelerator* accelerator) const override;
+ bool IsItemCheckedAt(size_t index) const override;
+ int GetGroupIdAt(size_t index) const override;
+ ui::ImageModel GetIconAt(size_t index) const override;
+ ui::ButtonMenuItemModel* GetButtonMenuItemAt(size_t index) const override;
+ bool IsEnabledAt(size_t index) const override;
+ void ActivatedAt(size_t index) override;
+ void ActivatedAt(size_t index, int event_flags) override;
+ ui::MenuModel* GetSubmenuModelAt(size_t index) const override;
+
+ raw_ptr<views::Combobox> owner_; // Weak. Owns this.
+ raw_ptr<ui::ComboboxModel> model_; // Weak.
+};
+
+#endif // UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_MENU_MODEL_H_
diff --git a/chromium/ui/views/controls/combobox/combobox_unittest.cc b/chromium/ui/views/controls/combobox/combobox_unittest.cc
index 91661f33074..43be08d5b9c 100644
--- a/chromium/ui/views/controls/combobox/combobox_unittest.cc
+++ b/chromium/ui/views/controls/combobox/combobox_unittest.cc
@@ -59,24 +59,21 @@ class TestComboboxModel : public ui::ComboboxModel {
~TestComboboxModel() override = default;
- enum { kItemCount = 10 };
+ static constexpr size_t kItemCount = 10;
// ui::ComboboxModel:
- int GetItemCount() const override { return item_count_; }
- std::u16string GetItemAt(int index) const override {
- if (IsItemSeparatorAt(index)) {
- NOTREACHED();
- return u"SEPARATOR";
- }
+ size_t GetItemCount() const override { return item_count_; }
+ std::u16string GetItemAt(size_t index) const override {
+ DCHECK(!IsItemSeparatorAt(index));
return ASCIIToUTF16(index % 2 == 0 ? "PEANUT BUTTER" : "JELLY");
}
- bool IsItemSeparatorAt(int index) const override {
+ bool IsItemSeparatorAt(size_t index) const override {
return separators_.find(index) != separators_.end();
}
- int GetDefaultIndex() const override {
+ absl::optional<size_t> GetDefaultIndex() const override {
// Return the first index that is not a separator.
- for (int index = 0; index < kItemCount; ++index) {
+ for (size_t index = 0; index < kItemCount; ++index) {
if (separators_.find(index) == separators_.end())
return index;
}
@@ -84,12 +81,12 @@ class TestComboboxModel : public ui::ComboboxModel {
return 0;
}
- void SetSeparators(const std::set<int>& separators) {
+ void SetSeparators(const std::set<size_t>& separators) {
separators_ = separators;
OnModelChanged();
}
- void set_item_count(int item_count) {
+ void set_item_count(size_t item_count) {
item_count_ = item_count;
OnModelChanged();
}
@@ -100,8 +97,8 @@ class TestComboboxModel : public ui::ComboboxModel {
observer.OnComboboxModelChanged(this);
}
- std::set<int> separators_;
- int item_count_ = kItemCount;
+ std::set<size_t> separators_;
+ size_t item_count_ = kItemCount;
};
// A combobox model which refers to a vector.
@@ -115,17 +112,19 @@ class VectorComboboxModel : public ui::ComboboxModel {
~VectorComboboxModel() override = default;
- void set_default_index(int default_index) { default_index_ = default_index; }
+ void set_default_index(size_t default_index) {
+ default_index_ = default_index;
+ }
// ui::ComboboxModel:
- int GetItemCount() const override {
- return static_cast<int>(values_->size());
+ size_t GetItemCount() const override { return values_->size(); }
+ std::u16string GetItemAt(size_t index) const override {
+ return ASCIIToUTF16((*values_)[index]);
}
- std::u16string GetItemAt(int index) const override {
- return ASCIIToUTF16(values_->at(index));
+ bool IsItemSeparatorAt(size_t index) const override { return false; }
+ absl::optional<size_t> GetDefaultIndex() const override {
+ return default_index_;
}
- bool IsItemSeparatorAt(int index) const override { return false; }
- int GetDefaultIndex() const override { return default_index_; }
void ValuesChanged() {
for (auto& observer : observers())
@@ -133,7 +132,7 @@ class VectorComboboxModel : public ui::ComboboxModel {
}
private:
- int default_index_ = 0;
+ size_t default_index_ = 0;
const raw_ptr<std::vector<std::string>> values_;
};
@@ -173,7 +172,9 @@ class TestComboboxListener {
actions_performed_++;
}
- int perform_action_index() const { return perform_action_index_; }
+ absl::optional<size_t> perform_action_index() const {
+ return perform_action_index_;
+ }
bool on_perform_action_called() const { return actions_performed_ > 0; }
@@ -181,7 +182,7 @@ class TestComboboxListener {
private:
raw_ptr<Combobox> combobox_;
- int perform_action_index_ = -1;
+ absl::optional<size_t> perform_action_index_ = absl::nullopt;
int actions_performed_ = 0;
};
@@ -199,7 +200,7 @@ class ComboboxTest : public ViewsTestBase {
ViewsTestBase::TearDown();
}
- void InitCombobox(const std::set<int>* separators) {
+ void InitCombobox(const std::set<size_t>* separators) {
model_ = std::make_unique<TestComboboxModel>();
if (separators)
@@ -283,43 +284,43 @@ class ComboboxTest : public ViewsTestBase {
TEST_F(ComboboxTest, KeyTestMac) {
InitCombobox(nullptr);
PressKey(ui::VKEY_END);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(1, menu_show_count_);
PressKey(ui::VKEY_HOME);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(2, menu_show_count_);
PressKey(ui::VKEY_UP, ui::EF_COMMAND_DOWN);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(3, menu_show_count_);
PressKey(ui::VKEY_DOWN, ui::EF_COMMAND_DOWN);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(4, menu_show_count_);
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(5, menu_show_count_);
PressKey(ui::VKEY_RIGHT);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(5, menu_show_count_);
PressKey(ui::VKEY_LEFT);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(5, menu_show_count_);
PressKey(ui::VKEY_UP);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(6, menu_show_count_);
PressKey(ui::VKEY_PRIOR);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(6, menu_show_count_);
PressKey(ui::VKEY_NEXT);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_EQ(6, menu_show_count_);
}
#endif
@@ -358,153 +359,153 @@ TEST_F(ComboboxTest, DisabilityTest) {
TEST_F(ComboboxTest, KeyTest) {
InitCombobox(nullptr);
PressKey(ui::VKEY_END);
- EXPECT_EQ(model_->GetItemCount(), combobox_->GetSelectedIndex() + 1);
+ EXPECT_EQ(model_->GetItemCount() - 1, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_HOME);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_DOWN);
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(2, combobox_->GetSelectedIndex());
+ EXPECT_EQ(2u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_RIGHT);
- EXPECT_EQ(2, combobox_->GetSelectedIndex());
+ EXPECT_EQ(2u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_LEFT);
- EXPECT_EQ(2, combobox_->GetSelectedIndex());
+ EXPECT_EQ(2u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_UP);
- EXPECT_EQ(1, combobox_->GetSelectedIndex());
+ EXPECT_EQ(1u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_PRIOR);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_NEXT);
- EXPECT_EQ(model_->GetItemCount(), combobox_->GetSelectedIndex() + 1);
+ EXPECT_EQ(model_->GetItemCount() - 1, combobox_->GetSelectedIndex());
}
// Verifies that we don't select a separator line in combobox when navigating
// through keyboard.
TEST_F(ComboboxTest, SkipSeparatorSimple) {
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(2);
InitCombobox(&separators);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(1, combobox_->GetSelectedIndex());
+ EXPECT_EQ(1u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(3, combobox_->GetSelectedIndex());
+ EXPECT_EQ(3u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_UP);
- EXPECT_EQ(1, combobox_->GetSelectedIndex());
+ EXPECT_EQ(1u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_HOME);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_PRIOR);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_END);
- EXPECT_EQ(9, combobox_->GetSelectedIndex());
+ EXPECT_EQ(9u, combobox_->GetSelectedIndex());
}
// Verifies that we never select the separator that is in the beginning of the
// combobox list when navigating through keyboard.
TEST_F(ComboboxTest, SkipSeparatorBeginning) {
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(0);
InitCombobox(&separators);
- EXPECT_EQ(1, combobox_->GetSelectedIndex());
+ EXPECT_EQ(1u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(2, combobox_->GetSelectedIndex());
+ EXPECT_EQ(2u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(3, combobox_->GetSelectedIndex());
+ EXPECT_EQ(3u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_UP);
- EXPECT_EQ(2, combobox_->GetSelectedIndex());
+ EXPECT_EQ(2u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_HOME);
- EXPECT_EQ(1, combobox_->GetSelectedIndex());
+ EXPECT_EQ(1u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_PRIOR);
- EXPECT_EQ(1, combobox_->GetSelectedIndex());
+ EXPECT_EQ(1u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_END);
- EXPECT_EQ(9, combobox_->GetSelectedIndex());
+ EXPECT_EQ(9u, combobox_->GetSelectedIndex());
}
// Verifies that we never select the separator that is in the end of the
// combobox list when navigating through keyboard.
TEST_F(ComboboxTest, SkipSeparatorEnd) {
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(TestComboboxModel::kItemCount - 1);
InitCombobox(&separators);
combobox_->SetSelectedIndex(8);
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(8, combobox_->GetSelectedIndex());
+ EXPECT_EQ(8u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_UP);
- EXPECT_EQ(7, combobox_->GetSelectedIndex());
+ EXPECT_EQ(7u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_END);
- EXPECT_EQ(8, combobox_->GetSelectedIndex());
+ EXPECT_EQ(8u, combobox_->GetSelectedIndex());
}
// Verifies that we never select any of the adjacent separators (multiple
// consecutive) that appear in the beginning of the combobox list when
// navigating through keyboard.
TEST_F(ComboboxTest, SkipMultipleSeparatorsAtBeginning) {
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(0);
separators.insert(1);
separators.insert(2);
InitCombobox(&separators);
- EXPECT_EQ(3, combobox_->GetSelectedIndex());
+ EXPECT_EQ(3u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(4, combobox_->GetSelectedIndex());
+ EXPECT_EQ(4u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_UP);
- EXPECT_EQ(3, combobox_->GetSelectedIndex());
+ EXPECT_EQ(3u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_NEXT);
- EXPECT_EQ(9, combobox_->GetSelectedIndex());
+ EXPECT_EQ(9u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_HOME);
- EXPECT_EQ(3, combobox_->GetSelectedIndex());
+ EXPECT_EQ(3u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_END);
- EXPECT_EQ(9, combobox_->GetSelectedIndex());
+ EXPECT_EQ(9u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_PRIOR);
- EXPECT_EQ(3, combobox_->GetSelectedIndex());
+ EXPECT_EQ(3u, combobox_->GetSelectedIndex());
}
// Verifies that we never select any of the adjacent separators (multiple
// consecutive) that appear in the middle of the combobox list when navigating
// through keyboard.
TEST_F(ComboboxTest, SkipMultipleAdjacentSeparatorsAtMiddle) {
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(4);
separators.insert(5);
separators.insert(6);
InitCombobox(&separators);
combobox_->SetSelectedIndex(3);
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(7, combobox_->GetSelectedIndex());
+ EXPECT_EQ(7u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_UP);
- EXPECT_EQ(3, combobox_->GetSelectedIndex());
+ EXPECT_EQ(3u, combobox_->GetSelectedIndex());
}
// Verifies that we never select any of the adjacent separators (multiple
// consecutive) that appear in the end of the combobox list when navigating
// through keyboard.
TEST_F(ComboboxTest, SkipMultipleSeparatorsAtEnd) {
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(7);
separators.insert(8);
separators.insert(9);
InitCombobox(&separators);
combobox_->SetSelectedIndex(6);
PressKey(ui::VKEY_DOWN);
- EXPECT_EQ(6, combobox_->GetSelectedIndex());
+ EXPECT_EQ(6u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_UP);
- EXPECT_EQ(5, combobox_->GetSelectedIndex());
+ EXPECT_EQ(5u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_HOME);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_NEXT);
- EXPECT_EQ(6, combobox_->GetSelectedIndex());
+ EXPECT_EQ(6u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_PRIOR);
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
PressKey(ui::VKEY_END);
- EXPECT_EQ(6, combobox_->GetSelectedIndex());
+ EXPECT_EQ(6u, combobox_->GetSelectedIndex());
}
#endif // !BUILDFLAG(IS_MAC)
TEST_F(ComboboxTest, GetTextForRowTest) {
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(0);
separators.insert(1);
separators.insert(9);
InitCombobox(&separators);
- for (int i = 0; i < combobox_->GetRowCount(); ++i) {
+ for (size_t i = 0; i < combobox_->GetRowCount(); ++i) {
if (separators.count(i) != 0) {
EXPECT_TRUE(combobox_->GetTextForRow(i).empty()) << i;
} else {
@@ -520,11 +521,11 @@ TEST_F(ComboboxTest, SelectValue) {
InitCombobox(nullptr);
ASSERT_EQ(model_->GetDefaultIndex(), combobox_->GetSelectedIndex());
EXPECT_TRUE(combobox_->SelectValue(u"PEANUT BUTTER"));
- EXPECT_EQ(0, combobox_->GetSelectedIndex());
+ EXPECT_EQ(0u, combobox_->GetSelectedIndex());
EXPECT_TRUE(combobox_->SelectValue(u"JELLY"));
- EXPECT_EQ(1, combobox_->GetSelectedIndex());
+ EXPECT_EQ(1u, combobox_->GetSelectedIndex());
EXPECT_FALSE(combobox_->SelectValue(u"BANANAS"));
- EXPECT_EQ(1, combobox_->GetSelectedIndex());
+ EXPECT_EQ(1u, combobox_->GetSelectedIndex());
}
TEST_F(ComboboxTest, ListenerHandlesDelete) {
@@ -541,7 +542,7 @@ TEST_F(ComboboxTest, Click) {
TestComboboxListener listener(combobox_);
combobox_->SetCallback(base::BindRepeating(
&TestComboboxListener::OnPerformAction, base::Unretained(&listener)));
- combobox_->Layout();
+ RunScheduledLayout(combobox_);
// Click the left side. The menu is shown.
EXPECT_EQ(0, menu_show_count_);
@@ -558,7 +559,7 @@ TEST_F(ComboboxTest, ClickButDisabled) {
combobox_->SetCallback(base::BindRepeating(
&TestComboboxListener::OnPerformAction, base::Unretained(&listener)));
- combobox_->Layout();
+ RunScheduledLayout(combobox_);
combobox_->SetEnabled(false);
// Click the left side, but nothing happens since the combobox is disabled.
@@ -636,7 +637,7 @@ TEST_F(ComboboxTest, NotifyOnClickWithMouse) {
combobox_->SetCallback(base::BindRepeating(
&TestComboboxListener::OnPerformAction, base::Unretained(&listener)));
- combobox_->Layout();
+ RunScheduledLayout(combobox_);
// Click the right side (arrow button). The menu is shown.
const gfx::Point right_point(combobox_->x() + combobox_->width() - 1,
@@ -659,7 +660,7 @@ TEST_F(ComboboxTest, NotifyOnClickWithMouse) {
// Both the text and the arrow may toggle the menu.
EXPECT_EQ(2, menu_show_count_);
- EXPECT_EQ(-1, listener.perform_action_index()); // Nothing selected.
+ EXPECT_FALSE(listener.perform_action_index().has_value());
}
TEST_F(ComboboxTest, ConsumingPressKeyEvents) {
@@ -743,39 +744,39 @@ TEST_F(ComboboxTest, ContentWidth) {
TEST_F(ComboboxTest, ModelChanged) {
InitCombobox(nullptr);
- EXPECT_EQ(0, combobox_->GetSelectedRow());
- EXPECT_EQ(10, combobox_->GetRowCount());
+ EXPECT_EQ(0u, combobox_->GetSelectedRow());
+ EXPECT_EQ(10u, combobox_->GetRowCount());
combobox_->SetSelectedIndex(4);
- EXPECT_EQ(4, combobox_->GetSelectedRow());
+ EXPECT_EQ(4u, combobox_->GetSelectedRow());
model_->set_item_count(5);
- EXPECT_EQ(5, combobox_->GetRowCount());
- EXPECT_EQ(4, combobox_->GetSelectedRow()); // Unchanged.
+ EXPECT_EQ(5u, combobox_->GetRowCount());
+ EXPECT_EQ(4u, combobox_->GetSelectedRow()); // Unchanged.
model_->set_item_count(4);
- EXPECT_EQ(4, combobox_->GetRowCount());
- EXPECT_EQ(0, combobox_->GetSelectedRow()); // Resets.
+ EXPECT_EQ(4u, combobox_->GetRowCount());
+ EXPECT_EQ(0u, combobox_->GetSelectedRow()); // Resets.
// Restore a non-zero selection.
combobox_->SetSelectedIndex(2);
- EXPECT_EQ(2, combobox_->GetSelectedRow());
+ EXPECT_EQ(2u, combobox_->GetSelectedRow());
// Make the selected index a separator.
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(2);
model_->SetSeparators(separators);
- EXPECT_EQ(4, combobox_->GetRowCount());
- EXPECT_EQ(0, combobox_->GetSelectedRow()); // Resets.
+ EXPECT_EQ(4u, combobox_->GetRowCount());
+ EXPECT_EQ(0u, combobox_->GetSelectedRow()); // Resets.
// Restore a non-zero selection.
combobox_->SetSelectedIndex(1);
- EXPECT_EQ(1, combobox_->GetSelectedRow());
+ EXPECT_EQ(1u, combobox_->GetSelectedRow());
// Test an empty model.
model_->set_item_count(0);
- EXPECT_EQ(0, combobox_->GetRowCount());
- EXPECT_EQ(0, combobox_->GetSelectedRow()); // Resets.
+ EXPECT_EQ(0u, combobox_->GetRowCount());
+ EXPECT_EQ(0u, combobox_->GetSelectedRow()); // Resets.
}
TEST_F(ComboboxTest, TypingPrefixNotifiesListener) {
@@ -793,7 +794,7 @@ TEST_F(ComboboxTest, TypingPrefixNotifiesListener) {
input_client->InsertChar(key_event);
EXPECT_EQ(1, listener.actions_performed());
- EXPECT_EQ(1, listener.perform_action_index());
+ EXPECT_EQ(1u, listener.perform_action_index());
// Type the second character of "JELLY", item shouldn't change and
// OnPerformAction() shouldn't be re-called.
@@ -802,7 +803,7 @@ TEST_F(ComboboxTest, TypingPrefixNotifiesListener) {
ui::DomKey::FromCharacter('E'), ui::EventTimeForNow());
input_client->InsertChar(key_event);
EXPECT_EQ(1, listener.actions_performed());
- EXPECT_EQ(1, listener.perform_action_index());
+ EXPECT_EQ(1u, listener.perform_action_index());
// Clears the typed text.
combobox_->OnBlur();
@@ -815,13 +816,13 @@ TEST_F(ComboboxTest, TypingPrefixNotifiesListener) {
ui::DomKey::FromCharacter('P'), ui::EventTimeForNow());
input_client->InsertChar(key_event);
EXPECT_EQ(2, listener.actions_performed());
- EXPECT_EQ(2, listener.perform_action_index());
+ EXPECT_EQ(2u, listener.perform_action_index());
}
// Test properties on the Combobox menu model.
TEST_F(ComboboxTest, MenuModel) {
const int kSeparatorIndex = 3;
- std::set<int> separators;
+ std::set<size_t> separators;
separators.insert(kSeparatorIndex);
InitCombobox(&separators);
@@ -898,29 +899,31 @@ class ConfigurableComboboxModel final : public ui::ComboboxModel {
}
// ui::ComboboxModel:
- int GetItemCount() const override { return item_count_; }
- std::u16string GetItemAt(int index) const override {
+ size_t GetItemCount() const override { return item_count_; }
+ std::u16string GetItemAt(size_t index) const override {
DCHECK_LT(index, item_count_);
return base::NumberToString16(index);
}
- int GetDefaultIndex() const override { return default_index_; }
+ absl::optional<size_t> GetDefaultIndex() const override {
+ return default_index_;
+ }
- void SetItemCount(int item_count) { item_count_ = item_count; }
+ void SetItemCount(size_t item_count) { item_count_ = item_count; }
- void SetDefaultIndex(int default_index) { default_index_ = default_index; }
+ void SetDefaultIndex(size_t default_index) { default_index_ = default_index; }
private:
const raw_ptr<bool> destroyed_;
- int item_count_ = 0;
- int default_index_ = -1;
+ size_t item_count_ = 0;
+ absl::optional<size_t> default_index_;
};
} // namespace
TEST_F(ComboboxDefaultTest, Default) {
auto combobox = std::make_unique<Combobox>();
- EXPECT_EQ(0, combobox->GetRowCount());
- EXPECT_EQ(-1, combobox->GetSelectedRow());
+ EXPECT_EQ(0u, combobox->GetRowCount());
+ EXPECT_FALSE(combobox->GetSelectedRow().has_value());
}
TEST_F(ComboboxDefaultTest, SetModel) {
@@ -932,8 +935,8 @@ TEST_F(ComboboxDefaultTest, SetModel) {
{
auto combobox = std::make_unique<Combobox>();
combobox->SetModel(model.get());
- EXPECT_EQ(42, combobox->GetRowCount());
- EXPECT_EQ(27, combobox->GetSelectedRow());
+ EXPECT_EQ(42u, combobox->GetRowCount());
+ EXPECT_EQ(27u, combobox->GetSelectedRow());
}
EXPECT_FALSE(destroyed);
}
@@ -947,8 +950,8 @@ TEST_F(ComboboxDefaultTest, SetOwnedModel) {
{
auto combobox = std::make_unique<Combobox>();
combobox->SetOwnedModel(std::move(model));
- EXPECT_EQ(42, combobox->GetRowCount());
- EXPECT_EQ(27, combobox->GetSelectedRow());
+ EXPECT_EQ(42u, combobox->GetRowCount());
+ EXPECT_EQ(27u, combobox->GetSelectedRow());
}
EXPECT_TRUE(destroyed);
}
diff --git a/chromium/ui/views/controls/combobox/empty_combobox_model.cc b/chromium/ui/views/controls/combobox/empty_combobox_model.cc
index becec4d176b..9c1c3edfd1e 100644
--- a/chromium/ui/views/controls/combobox/empty_combobox_model.cc
+++ b/chromium/ui/views/controls/combobox/empty_combobox_model.cc
@@ -14,17 +14,17 @@ namespace internal {
EmptyComboboxModel::EmptyComboboxModel() = default;
EmptyComboboxModel::~EmptyComboboxModel() = default;
-int EmptyComboboxModel::GetItemCount() const {
+size_t EmptyComboboxModel::GetItemCount() const {
return 0;
}
-std::u16string EmptyComboboxModel::GetItemAt(int index) const {
+std::u16string EmptyComboboxModel::GetItemAt(size_t index) const {
NOTREACHED();
return std::u16string();
}
-int EmptyComboboxModel::GetDefaultIndex() const {
- return -1;
+absl::optional<size_t> EmptyComboboxModel::GetDefaultIndex() const {
+ return absl::nullopt;
}
} // namespace internal
diff --git a/chromium/ui/views/controls/combobox/empty_combobox_model.h b/chromium/ui/views/controls/combobox/empty_combobox_model.h
index d7f5ca6d768..ec1a57db70a 100644
--- a/chromium/ui/views/controls/combobox/empty_combobox_model.h
+++ b/chromium/ui/views/controls/combobox/empty_combobox_model.h
@@ -19,9 +19,9 @@ class EmptyComboboxModel final : public ui::ComboboxModel {
~EmptyComboboxModel() override;
// ui::ComboboxModel:
- int GetItemCount() const override;
- std::u16string GetItemAt(int index) const override;
- int GetDefaultIndex() const override;
+ size_t GetItemCount() const override;
+ std::u16string GetItemAt(size_t index) const override;
+ absl::optional<size_t> GetDefaultIndex() const override;
};
} // namespace internal
diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox.cc b/chromium/ui/views/controls/editable_combobox/editable_combobox.cc
index faac607fefc..2acc0d9f511 100644
--- a/chromium/ui/views/controls/editable_combobox/editable_combobox.cc
+++ b/chromium/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -156,12 +156,11 @@ class EditableCombobox::EditableComboboxMenuModel
return;
items_shown_.clear();
if (show_on_empty_ || !owner_->GetText().empty()) {
- for (int i = 0; i < combobox_model_->GetItemCount(); ++i) {
+ for (size_t i = 0; i < combobox_model_->GetItemCount(); ++i) {
if (!filter_on_edit_ ||
base::StartsWith(combobox_model_->GetItemAt(i), owner_->GetText(),
base::CompareCase::INSENSITIVE_ASCII)) {
- items_shown_.push_back(
- {static_cast<size_t>(i), combobox_model_->IsItemEnabledAt(i)});
+ items_shown_.push_back({i, combobox_model_->IsItemEnabledAt(i)});
}
}
}
@@ -177,8 +176,8 @@ class EditableCombobox::EditableComboboxMenuModel
return MenuConfig::instance().check_selected_combobox_item;
}
- std::u16string GetItemTextAt(int index, bool showing_password_text) const {
- int index_in_model = items_shown_[index].index;
+ std::u16string GetItemTextAt(size_t index, bool showing_password_text) const {
+ size_t index_in_model = items_shown_[index].index;
std::u16string text = combobox_model_->GetItemAt(index_in_model);
return showing_password_text
? text
@@ -186,7 +185,7 @@ class EditableCombobox::EditableComboboxMenuModel
gfx::RenderText::kPasswordReplacementChar);
}
- ui::ImageModel GetIconAt(int index) const override {
+ ui::ImageModel GetIconAt(size_t index) const override {
return combobox_model_->GetDropDownIconAt(items_shown_[index].index);
}
@@ -199,7 +198,7 @@ class EditableCombobox::EditableComboboxMenuModel
observation_.Reset();
}
- int GetItemCount() const override { return items_shown_.size(); }
+ size_t GetItemCount() const override { return items_shown_.size(); }
private:
struct ShownItem {
@@ -207,62 +206,62 @@ class EditableCombobox::EditableComboboxMenuModel
bool enabled;
};
bool HasIcons() const override {
- for (int i = 0; i < GetItemCount(); ++i) {
+ for (size_t i = 0; i < GetItemCount(); ++i) {
if (!GetIconAt(i).IsEmpty())
return true;
}
return false;
}
- ItemType GetTypeAt(int index) const override {
+ ItemType GetTypeAt(size_t index) const override {
return UseCheckmarks() ? TYPE_CHECK : TYPE_COMMAND;
}
- ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override {
+ ui::MenuSeparatorType GetSeparatorTypeAt(size_t index) const override {
return ui::NORMAL_SEPARATOR;
}
- int GetCommandIdAt(int index) const override {
+ int GetCommandIdAt(size_t index) const override {
constexpr int kFirstMenuItemId = 1000;
- return index + kFirstMenuItemId;
+ return static_cast<int>(index) + kFirstMenuItemId;
}
- std::u16string GetLabelAt(int index) const override {
+ std::u16string GetLabelAt(size_t index) const override {
std::u16string text = GetItemTextAt(index, owner_->showing_password_text_);
base::i18n::AdjustStringForLocaleDirection(&text);
return text;
}
- bool IsItemDynamicAt(int index) const override { return false; }
+ bool IsItemDynamicAt(size_t index) const override { return false; }
- const gfx::FontList* GetLabelFontListAt(int index) const override {
+ const gfx::FontList* GetLabelFontListAt(size_t index) const override {
return &owner_->GetFontList();
}
- bool GetAcceleratorAt(int index,
+ bool GetAcceleratorAt(size_t index,
ui::Accelerator* accelerator) const override {
return false;
}
- bool IsItemCheckedAt(int index) const override {
+ bool IsItemCheckedAt(size_t index) const override {
return UseCheckmarks() &&
combobox_model_->GetItemAt(items_shown_[index].index) ==
owner_->GetText();
}
- int GetGroupIdAt(int index) const override { return -1; }
+ int GetGroupIdAt(size_t index) const override { return -1; }
- ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override {
+ ui::ButtonMenuItemModel* GetButtonMenuItemAt(size_t index) const override {
return nullptr;
}
- bool IsEnabledAt(int index) const override {
+ bool IsEnabledAt(size_t index) const override {
return items_shown_[index].enabled;
}
- void ActivatedAt(int index) override { owner_->OnItemSelected(index); }
+ void ActivatedAt(size_t index) override { owner_->OnItemSelected(index); }
- MenuModel* GetSubmenuModelAt(int index) const override { return nullptr; }
+ MenuModel* GetSubmenuModelAt(size_t index) const override { return nullptr; }
raw_ptr<EditableCombobox> owner_; // Weak. Owns |this|.
raw_ptr<ui::ComboboxModel> combobox_model_; // Weak.
@@ -419,15 +418,15 @@ void EditableCombobox::RevealPasswords(bool revealed) {
menu_model_->UpdateItemsShown();
}
-int EditableCombobox::GetItemCountForTest() {
+size_t EditableCombobox::GetItemCountForTest() {
return menu_model_->GetItemCount();
}
-std::u16string EditableCombobox::GetItemForTest(int index) {
+std::u16string EditableCombobox::GetItemForTest(size_t index) {
return menu_model_->GetItemTextAt(index, showing_password_text_);
}
-ui::ImageModel EditableCombobox::GetIconForTest(int index) {
+ui::ImageModel EditableCombobox::GetIconForTest(size_t index) {
return menu_model_->GetIconAt(index);
}
@@ -493,7 +492,7 @@ void EditableCombobox::CloseMenu() {
pre_target_handler_.reset();
}
-void EditableCombobox::OnItemSelected(int index) {
+void EditableCombobox::OnItemSelected(size_t index) {
// |textfield_| can hide the characters on its own so we read the actual
// characters instead of gfx::RenderText::kPasswordReplacementChar characters.
std::u16string selected_item_text =
diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox.h b/chromium/ui/views/controls/editable_combobox/editable_combobox.h
index df5143b72df..ef96793b1bf 100644
--- a/chromium/ui/views/controls/editable_combobox/editable_combobox.h
+++ b/chromium/ui/views/controls/editable_combobox/editable_combobox.h
@@ -110,9 +110,9 @@ class VIEWS_EXPORT EditableCombobox
// Accessors of private members for tests.
ui::ComboboxModel* GetComboboxModelForTest() { return combobox_model_.get(); }
- int GetItemCountForTest();
- std::u16string GetItemForTest(int index);
- ui::ImageModel GetIconForTest(int index);
+ size_t GetItemCountForTest();
+ std::u16string GetItemForTest(size_t index);
+ ui::ImageModel GetIconForTest(size_t index);
MenuRunner* GetMenuRunnerForTest() { return menu_runner_.get(); }
Textfield* GetTextfieldForTest() { return textfield_; }
@@ -123,7 +123,7 @@ class VIEWS_EXPORT EditableCombobox
void CloseMenu();
// Called when an item is selected from the menu.
- void OnItemSelected(int index);
+ void OnItemSelected(size_t index);
// Notifies listener of new content and updates the menu items to show.
void HandleNewContent(const std::u16string& new_content);
diff --git a/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc b/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
index 2a69777a01c..7fe52c54585 100644
--- a/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
+++ b/chromium/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
@@ -655,7 +655,7 @@ TEST_F(EditableComboboxTest, MAYBE_RefocusingReopensMenuBasedOnLatestContent) {
combobox_->GetTextfieldForTest()->RequestFocus();
SendKeyEvent(ui::VKEY_B);
- ASSERT_EQ(3, combobox_->GetItemCountForTest());
+ ASSERT_EQ(3u, combobox_->GetItemCountForTest());
SendKeyEvent(ui::VKEY_DOWN);
SendKeyEvent(ui::VKEY_RETURN);
@@ -669,7 +669,7 @@ TEST_F(EditableComboboxTest, MAYBE_RefocusingReopensMenuBasedOnLatestContent) {
dummy_focusable_view_->RequestFocus();
ClickArrow();
EXPECT_TRUE(IsMenuOpen());
- ASSERT_EQ(2, combobox_->GetItemCountForTest());
+ ASSERT_EQ(2u, combobox_->GetItemCountForTest());
}
TEST_F(EditableComboboxTest, GetItemsWithoutFiltering) {
@@ -677,7 +677,7 @@ TEST_F(EditableComboboxTest, GetItemsWithoutFiltering) {
InitEditableCombobox(items, /*filter_on_edit=*/false, /*show_on_empty=*/true);
combobox_->SetText(u"z");
- ASSERT_EQ(2, combobox_->GetItemCountForTest());
+ ASSERT_EQ(2u, combobox_->GetItemCountForTest());
ASSERT_EQ(u"item0", combobox_->GetItemForTest(0));
ASSERT_EQ(u"item1", combobox_->GetItemForTest(1));
}
@@ -686,22 +686,22 @@ TEST_F(EditableComboboxTest, FilteringEffectOnGetItems) {
std::vector<std::u16string> items = {u"abc", u"abd", u"bac", u"bad"};
InitEditableCombobox(items, /*filter_on_edit=*/true, /*show_on_empty=*/true);
- ASSERT_EQ(4, combobox_->GetItemCountForTest());
+ ASSERT_EQ(4u, combobox_->GetItemCountForTest());
ASSERT_EQ(u"abc", combobox_->GetItemForTest(0));
ASSERT_EQ(u"abd", combobox_->GetItemForTest(1));
ASSERT_EQ(u"bac", combobox_->GetItemForTest(2));
ASSERT_EQ(u"bad", combobox_->GetItemForTest(3));
combobox_->SetText(u"b");
- ASSERT_EQ(2, combobox_->GetItemCountForTest());
+ ASSERT_EQ(2u, combobox_->GetItemCountForTest());
ASSERT_EQ(u"bac", combobox_->GetItemForTest(0));
ASSERT_EQ(u"bad", combobox_->GetItemForTest(1));
combobox_->SetText(u"bc");
- ASSERT_EQ(0, combobox_->GetItemCountForTest());
+ ASSERT_EQ(0u, combobox_->GetItemCountForTest());
combobox_->SetText(std::u16string());
- ASSERT_EQ(4, combobox_->GetItemCountForTest());
+ ASSERT_EQ(4u, combobox_->GetItemCountForTest());
ASSERT_EQ(u"abc", combobox_->GetItemForTest(0));
ASSERT_EQ(u"abd", combobox_->GetItemForTest(1));
ASSERT_EQ(u"bac", combobox_->GetItemForTest(2));
@@ -721,18 +721,18 @@ TEST_F(EditableComboboxTest, FilteringEffectOnIcons) {
/*filter_on_edit=*/true,
/*show_on_empty=*/true);
- ASSERT_EQ(2, combobox_->GetItemCountForTest());
+ ASSERT_EQ(2u, combobox_->GetItemCountForTest());
EXPECT_EQ(16,
combobox_->GetComboboxModelForTest()->GetIconAt(0).Size().width());
EXPECT_EQ(20,
combobox_->GetComboboxModelForTest()->GetIconAt(1).Size().width());
combobox_->SetText(u"a");
- ASSERT_EQ(1, combobox_->GetItemCountForTest());
+ ASSERT_EQ(1u, combobox_->GetItemCountForTest());
EXPECT_EQ(16, combobox_->GetIconForTest(0).Size().width());
combobox_->SetText(u"d");
- ASSERT_EQ(1, combobox_->GetItemCountForTest());
+ ASSERT_EQ(1u, combobox_->GetItemCountForTest());
EXPECT_EQ(20, combobox_->GetIconForTest(0).Size().width());
}
@@ -740,18 +740,18 @@ TEST_F(EditableComboboxTest, FilteringWithMismatchedCase) {
std::vector<std::u16string> items = {u"AbCd", u"aBcD", u"xyz"};
InitEditableCombobox(items, /*filter_on_edit=*/true, /*show_on_empty=*/true);
- ASSERT_EQ(3, combobox_->GetItemCountForTest());
+ ASSERT_EQ(3u, combobox_->GetItemCountForTest());
ASSERT_EQ(u"AbCd", combobox_->GetItemForTest(0));
ASSERT_EQ(u"aBcD", combobox_->GetItemForTest(1));
ASSERT_EQ(u"xyz", combobox_->GetItemForTest(2));
combobox_->SetText(u"abcd");
- ASSERT_EQ(2, combobox_->GetItemCountForTest());
+ ASSERT_EQ(2u, combobox_->GetItemCountForTest());
ASSERT_EQ(u"AbCd", combobox_->GetItemForTest(0));
ASSERT_EQ(u"aBcD", combobox_->GetItemForTest(1));
combobox_->SetText(u"ABCD");
- ASSERT_EQ(2, combobox_->GetItemCountForTest());
+ ASSERT_EQ(2u, combobox_->GetItemCountForTest());
ASSERT_EQ(u"AbCd", combobox_->GetItemForTest(0));
ASSERT_EQ(u"aBcD", combobox_->GetItemForTest(1));
}
@@ -761,9 +761,9 @@ TEST_F(EditableComboboxTest, DontShowOnEmpty) {
InitEditableCombobox(items, /*filter_on_edit=*/false,
/*show_on_empty=*/false);
- ASSERT_EQ(0, combobox_->GetItemCountForTest());
+ ASSERT_EQ(0u, combobox_->GetItemCountForTest());
combobox_->SetText(u"a");
- ASSERT_EQ(2, combobox_->GetItemCountForTest());
+ ASSERT_EQ(2u, combobox_->GetItemCountForTest());
ASSERT_EQ(u"item0", combobox_->GetItemForTest(0));
ASSERT_EQ(u"item1", combobox_->GetItemForTest(1));
}
@@ -797,7 +797,7 @@ TEST_F(EditableComboboxTest, PasswordCanBeHiddenAndRevealed) {
InitEditableCombobox(items, /*filter_on_edit=*/false, /*show_on_empty=*/true,
EditableCombobox::Type::kPassword);
- ASSERT_EQ(2, combobox_->GetItemCountForTest());
+ ASSERT_EQ(2u, combobox_->GetItemCountForTest());
ASSERT_EQ(std::u16string(5, gfx::RenderText::kPasswordReplacementChar),
combobox_->GetItemForTest(0));
ASSERT_EQ(std::u16string(5, gfx::RenderText::kPasswordReplacementChar),
@@ -909,24 +909,24 @@ class ConfigurableComboboxModel final : public ui::ComboboxModel {
}
// ui::ComboboxModel:
- int GetItemCount() const override { return item_count_; }
- std::u16string GetItemAt(int index) const override {
+ size_t GetItemCount() const override { return item_count_; }
+ std::u16string GetItemAt(size_t index) const override {
DCHECK_LT(index, item_count_);
return base::NumberToString16(index);
}
- void SetItemCount(int item_count) { item_count_ = item_count; }
+ void SetItemCount(size_t item_count) { item_count_ = item_count; }
private:
const raw_ptr<bool> destroyed_;
- int item_count_ = 0;
+ size_t item_count_ = 0;
};
} // namespace
TEST_F(EditableComboboxDefaultTest, Default) {
auto combobox = std::make_unique<EditableCombobox>();
- EXPECT_EQ(0, combobox->GetItemCountForTest());
+ EXPECT_EQ(0u, combobox->GetItemCountForTest());
}
TEST_F(EditableComboboxDefaultTest, SetModel) {
@@ -935,7 +935,7 @@ TEST_F(EditableComboboxDefaultTest, SetModel) {
model->SetItemCount(42);
auto combobox = std::make_unique<EditableCombobox>();
combobox->SetModel(std::move(model));
- EXPECT_EQ(42, combobox->GetItemCountForTest());
+ EXPECT_EQ(42u, combobox->GetItemCountForTest());
}
TEST_F(EditableComboboxDefaultTest, SetModelOverwrite) {
diff --git a/chromium/ui/views/controls/focus_ring.cc b/chromium/ui/views/controls/focus_ring.cc
index eb9373cef02..be0388f8f88 100644
--- a/chromium/ui/views/controls/focus_ring.cc
+++ b/chromium/ui/views/controls/focus_ring.cc
@@ -47,11 +47,12 @@ bool IsPathUsable(const SkPath& path) {
path.isRRect(nullptr));
}
-SkColor GetColor(View* focus_ring, bool valid) {
- if (!valid) {
- return focus_ring->GetColorProvider()->GetColor(
- ui::kColorAlertHighSeverity);
- }
+SkColor GetPaintColor(FocusRing* focus_ring, bool valid) {
+ const auto* cp = focus_ring->GetColorProvider();
+ if (!valid)
+ return cp->GetColor(ui::kColorAlertHighSeverity);
+ if (auto color_id = focus_ring->GetColorId(); color_id.has_value())
+ return cp->GetColor(color_id.value());
return GetCascadingAccentColor(focus_ring);
}
@@ -137,19 +138,37 @@ void FocusRing::SetHasFocusPredicate(const ViewPredicate& predicate) {
RefreshLayer();
}
-void FocusRing::SetColor(absl::optional<SkColor> color) {
- color_ = color;
- SchedulePaint();
+absl::optional<ui::ColorId> FocusRing::GetColorId() const {
+ return color_id_;
+}
+
+void FocusRing::SetColorId(absl::optional<ui::ColorId> color_id) {
+ if (color_id_ == color_id)
+ return;
+ color_id_ = color_id;
+ OnPropertyChanged(&color_id_, PropertyEffects::kPropertyEffectsPaint);
+}
+
+float FocusRing::GetHaloThickness() const {
+ return halo_thickness_;
+}
+
+float FocusRing::GetHaloInset() const {
+ return halo_inset_;
}
void FocusRing::SetHaloThickness(float halo_thickness) {
+ if (halo_thickness_ == halo_thickness)
+ return;
halo_thickness_ = halo_thickness;
- SchedulePaint();
+ OnPropertyChanged(&halo_thickness_, PropertyEffects::kPropertyEffectsPaint);
}
void FocusRing::SetHaloInset(float halo_inset) {
+ if (halo_inset_ == halo_inset)
+ return;
halo_inset_ = halo_inset;
- SchedulePaint();
+ OnPropertyChanged(&halo_inset_, PropertyEffects::kPropertyEffectsPaint);
}
void FocusRing::Layout() {
@@ -232,7 +251,7 @@ void FocusRing::OnPaint(gfx::Canvas* canvas) {
canvas->sk_canvas()->drawRRect(ring_rect, paint);
}
- paint.setColor(color_.value_or(GetColor(this, !invalid_)));
+ paint.setColor(GetPaintColor(this, !invalid_));
paint.setStrokeWidth(halo_thickness_);
canvas->sk_canvas()->drawRRect(ring_rect, paint);
}
@@ -273,6 +292,12 @@ void FocusRing::GetAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->AddState(ax::mojom::State::kIgnored);
}
+void FocusRing::OnThemeChanged() {
+ View::OnThemeChanged();
+ if (invalid_ || color_id_.has_value())
+ SchedulePaint();
+}
+
void FocusRing::OnViewFocused(View* view) {
RefreshLayer();
}
@@ -354,6 +379,9 @@ SkPath GetHighlightPath(const View* view, float halo_thickness) {
}
BEGIN_METADATA(FocusRing, View)
+ADD_PROPERTY_METADATA(absl::optional<ui::ColorId>, ColorId)
+ADD_PROPERTY_METADATA(float, HaloInset)
+ADD_PROPERTY_METADATA(float, HaloThickness)
END_METADATA
} // namespace views
diff --git a/chromium/ui/views/controls/focus_ring.h b/chromium/ui/views/controls/focus_ring.h
index 37ad485bc85..e35f2eb8b06 100644
--- a/chromium/ui/views/controls/focus_ring.h
+++ b/chromium/ui/views/controls/focus_ring.h
@@ -9,6 +9,7 @@
#include "base/scoped_observation.h"
#include "ui/base/class_property.h"
+#include "ui/color/color_id.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/controls/focusable_border.h"
#include "ui/views/view.h"
@@ -77,11 +78,11 @@ class VIEWS_EXPORT FocusRing : public View, public ViewObserver {
// focus, but the FocusRing sits on the parent instead of the inner view.
void SetHasFocusPredicate(const ViewPredicate& predicate);
- absl::optional<SkColor> color() const { return color_; }
- void SetColor(absl::optional<SkColor> color);
+ absl::optional<ui::ColorId> GetColorId() const;
+ void SetColorId(absl::optional<ui::ColorId> color_id);
- float halo_thickness() const { return halo_thickness_; }
- float halo_inset() const { return halo_inset_; }
+ float GetHaloThickness() const;
+ float GetHaloInset() const;
void SetHaloThickness(float halo_thickness);
void SetHaloInset(float halo_inset);
@@ -91,6 +92,7 @@ class VIEWS_EXPORT FocusRing : public View, public ViewObserver {
const ViewHierarchyChangedDetails& details) override;
void OnPaint(gfx::Canvas* canvas) override;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+ void OnThemeChanged() override;
// ViewObserver:
void OnViewFocused(View* view) override;
@@ -119,8 +121,8 @@ class VIEWS_EXPORT FocusRing : public View, public ViewObserver {
// the focus ring shows an invalid appearance (usually a different color).
bool invalid_ = false;
- // Overriding color for the focus ring.
- absl::optional<SkColor> color_;
+ // Overriding color_id for the focus ring.
+ absl::optional<ui::ColorId> color_id_;
// The predicate used to determine whether the parent has focus.
absl::optional<ViewPredicate> has_focus_predicate_;
diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc
index 1cbd6988ac6..f9a6b527e45 100644
--- a/chromium/ui/views/controls/image_view.cc
+++ b/chromium/ui/views/controls/image_view.cc
@@ -9,6 +9,7 @@
#include "base/check_op.h"
#include "base/i18n/rtl.h"
#include "base/numerics/safe_conversions.h"
+#include "base/trace_event/trace_event.h"
#include "cc/paint/paint_flags.h"
#include "skia/ext/image_operations.h"
#include "ui/base/metadata/metadata_impl_macros.h"
@@ -85,8 +86,13 @@ gfx::Size ImageView::GetImageSize() const {
}
void ImageView::OnPaint(gfx::Canvas* canvas) {
- View::OnPaint(canvas);
+ // This inlines View::OnPaint in order to OnPaintBorder() after OnPaintImage
+ // so the border can paint over content (for rounded corners that overlap
+ // content).
+ TRACE_EVENT1("views", "ImageView::OnPaint", "class", GetClassName());
+ OnPaintBackground(canvas);
OnPaintImage(canvas);
+ OnPaintBorder(canvas);
}
void ImageView::OnThemeChanged() {
diff --git a/chromium/ui/views/controls/image_view_unittest.cc b/chromium/ui/views/controls/image_view_unittest.cc
index 5efb0842dbd..60ea497767b 100644
--- a/chromium/ui/views/controls/image_view_unittest.cc
+++ b/chromium/ui/views/controls/image_view_unittest.cc
@@ -102,7 +102,7 @@ TEST_P(ImageViewTest, CenterAlignment) {
bitmap.allocN32Pixels(kImageSkiaSize, kImageSkiaSize);
gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
image_view()->SetImage(image_skia);
- widget()->GetContentsView()->Layout();
+ RunScheduledLayout(image_view());
EXPECT_NE(gfx::Size(), image_skia.size());
// With no changes to the size / padding of |image_view|, the origin of
@@ -112,11 +112,11 @@ TEST_P(ImageViewTest, CenterAlignment) {
// Test insets are always respected in LTR and RTL.
constexpr int kInset = 5;
image_view()->SetBorder(CreateEmptyBorder(kInset));
- widget()->GetContentsView()->Layout();
+ RunScheduledLayout(image_view());
EXPECT_EQ(kInset, CurrentImageOriginForParam());
SetRTL(true);
- widget()->GetContentsView()->Layout();
+ RunScheduledLayout(image_view());
EXPECT_EQ(kInset, CurrentImageOriginForParam());
// Check this still holds true when the insets are asymmetrical.
@@ -124,11 +124,11 @@ TEST_P(ImageViewTest, CenterAlignment) {
constexpr int kTrailingInset = 6;
image_view()->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(
kLeadingInset, kLeadingInset, kTrailingInset, kTrailingInset)));
- widget()->GetContentsView()->Layout();
+ RunScheduledLayout(image_view());
EXPECT_EQ(kLeadingInset, CurrentImageOriginForParam());
SetRTL(false);
- widget()->GetContentsView()->Layout();
+ RunScheduledLayout(image_view());
EXPECT_EQ(kLeadingInset, CurrentImageOriginForParam());
}
diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc
index c748b7ed1a9..dacb2199b93 100644
--- a/chromium/ui/views/controls/label.cc
+++ b/chromium/ui/views/controls/label.cc
@@ -350,11 +350,11 @@ void Label::SetMultiLine(bool multi_line) {
OnPropertyChanged(&multi_line_, kPropertyEffectsPreferredSizeChanged);
}
-int Label::GetMaxLines() const {
+size_t Label::GetMaxLines() const {
return max_lines_;
}
-void Label::SetMaxLines(int max_lines) {
+void Label::SetMaxLines(size_t max_lines) {
if (max_lines_ == max_lines)
return;
max_lines_ = max_lines;
@@ -576,6 +576,11 @@ int Label::GetBaseline() const {
}
gfx::Size Label::CalculatePreferredSize() const {
+ return CalculatePreferredSize({width(), {}});
+}
+
+gfx::Size Label::CalculatePreferredSize(
+ const SizeBounds& available_size) const {
// Return a size of (0, 0) if the label is not visible and if the
// |collapse_when_hidden_| flag is set.
// TODO(munjal): This logic probably belongs to the View class. But for now,
@@ -587,7 +592,7 @@ gfx::Size Label::CalculatePreferredSize() const {
if (GetMultiLine() && fixed_width_ != 0 && !GetText().empty())
return gfx::Size(fixed_width_, GetHeightForWidth(fixed_width_));
- gfx::Size size(GetTextSize());
+ gfx::Size size(GetBoundedTextSize(available_size));
const gfx::Insets insets = GetInsets();
size.Enlarge(insets.width(), insets.height());
@@ -638,7 +643,8 @@ int Label::GetHeightForWidth(int w) const {
if (!GetMultiLine() || GetText().empty() || w < 0) {
height = base_line_height;
} else if (w == 0) {
- height = std::max(GetMaxLines(), 1) * base_line_height;
+ height =
+ std::max(base::checked_cast<int>(GetMaxLines()), 1) * base_line_height;
} else {
// SetDisplayRect() has a side effect for later calls of GetStringSize().
// Be careful to invoke full_text_->SetDisplayRect(gfx::Rect()) to
@@ -650,7 +656,9 @@ int Label::GetHeightForWidth(int w) const {
int string_height = full_text_->GetStringSize().height();
// Cap the number of lines to GetMaxLines() if that's set.
height = GetMaxLines() > 0
- ? std::min(GetMaxLines() * base_line_height, string_height)
+ ? std::min(base::checked_cast<int>(GetMaxLines()) *
+ base_line_height,
+ string_height)
: string_height;
}
height -= gfx::ShadowValue::GetMargin(full_text_->shadows()).height();
@@ -711,7 +719,7 @@ std::unique_ptr<gfx::RenderText> Label::CreateRenderText() const {
render_text->set_shadows(GetShadows());
const bool multiline = GetMultiLine();
render_text->SetMultiline(multiline);
- render_text->SetMaxLines(multiline ? GetMaxLines() : 0);
+ render_text->SetMaxLines(multiline ? GetMaxLines() : size_t{0});
render_text->SetWordWrapBehavior(full_text_->word_wrap_behavior());
// Setup render text for selection controller.
@@ -1131,6 +1139,10 @@ void Label::MaybeBuildDisplayText() const {
}
gfx::Size Label::GetTextSize() const {
+ return GetBoundedTextSize({width(), {}});
+}
+
+gfx::Size Label::GetBoundedTextSize(const SizeBounds& available_size) const {
gfx::Size size;
if (GetText().empty()) {
size = gfx::Size(0, GetLineHeight());
@@ -1140,17 +1152,20 @@ gfx::Size Label::GetTextSize() const {
// to report an accurate width given the constraints and how it determines
// to elide the text. If we simply clamp the width to the max after the
// fact, then there may be some empty space left over *after* an ellipsis.
+ // TODO(kerenzhu): `available_size` should be respected, but doing so will
+ // break tests. Fix that.
full_text_->SetDisplayRect(
gfx::Rect(0, 0, max_width_single_line_ - GetInsets().width(), 0));
size = full_text_->GetStringSize();
} else {
- // Cancel the display rect of |full_text_|. The display rect may be
- // specified in GetHeightForWidth(), and specifying empty Rect cancels
- // its effect. See also the comment in GetHeightForWidth().
- // TODO(mukai): use gfx::Rect() to compute the ideal size rather than
- // the current width(). See crbug.com/468494, crbug.com/467526, and
- // the comment for MultilinePreferredSizeTest in label_unittest.cc.
- full_text_->SetDisplayRect(gfx::Rect(0, 0, width(), 0));
+ const int width = available_size.width().is_bounded()
+ ? available_size.width().value()
+ : 0;
+ // SetDisplayRect() has side-effect. The text height will change to respect
+ // width.
+ // TODO(crbug.com/1347330): `width` should respect insets, but doing so
+ // will break LabelTest.MultiLineSizing. Fix that.
+ full_text_->SetDisplayRect(gfx::Rect(0, 0, width, 0));
size = full_text_->GetStringSize();
}
const gfx::Insets shadow_margin = -gfx::ShadowValue::GetMargin(GetShadows());
@@ -1286,7 +1301,7 @@ ADD_PROPERTY_METADATA(gfx::HorizontalAlignment, HorizontalAlignment)
ADD_PROPERTY_METADATA(gfx::VerticalAlignment, VerticalAlignment)
ADD_PROPERTY_METADATA(int, LineHeight)
ADD_PROPERTY_METADATA(bool, MultiLine)
-ADD_PROPERTY_METADATA(int, MaxLines)
+ADD_PROPERTY_METADATA(size_t, MaxLines)
ADD_PROPERTY_METADATA(bool, Obscured)
ADD_PROPERTY_METADATA(bool, AllowCharacterBreak)
ADD_PROPERTY_METADATA(std::u16string, TooltipText)
diff --git a/chromium/ui/views/controls/label.h b/chromium/ui/views/controls/label.h
index 912c7d20eb0..28c9b4fc484 100644
--- a/chromium/ui/views/controls/label.h
+++ b/chromium/ui/views/controls/label.h
@@ -193,8 +193,8 @@ class VIEWS_EXPORT Label : public View,
// If multi-line, a non-zero value will cap the number of lines rendered, and
// elide the rest (currently only ELIDE_TAIL supported). See gfx::RenderText.
- int GetMaxLines() const;
- void SetMaxLines(int max_lines);
+ size_t GetMaxLines() const;
+ void SetMaxLines(size_t max_lines);
// If single-line, a non-zero value will help determine the amount of space
// needed *after* elision, which may be less than the passed |max_width|.
@@ -309,6 +309,8 @@ class VIEWS_EXPORT Label : public View,
// View:
int GetBaseline() const override;
gfx::Size CalculatePreferredSize() const override;
+ gfx::Size CalculatePreferredSize(
+ const SizeBounds& available_size) const override;
gfx::Size GetMinimumSize() const override;
int GetHeightForWidth(int w) const override;
View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
@@ -407,6 +409,10 @@ class VIEWS_EXPORT Label : public View,
// Get the text size for the current layout.
gfx::Size GetTextSize() const;
+ // Get the text size that ignores the current layout and respects
+ // `available_size`.
+ gfx::Size GetBoundedTextSize(const SizeBounds& available_size) const;
+
// Returns the appropriate foreground color to use given the proposed
// |foreground| and |background| colors.
SkColor GetForegroundColor(SkColor foreground, SkColor background) const;
@@ -475,7 +481,7 @@ class VIEWS_EXPORT Label : public View,
bool auto_color_readability_enabled_ = true;
// TODO(mukai): remove |multi_line_| when all RenderText can render multiline.
bool multi_line_ = false;
- int max_lines_ = 0;
+ size_t max_lines_ = 0;
std::u16string tooltip_text_;
bool handles_tooltips_ = true;
// Whether to collapse the label when it's not visible.
diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc
index bc26014c209..b8f0524c2a6 100644
--- a/chromium/ui/views/controls/label_unittest.cc
+++ b/chromium/ui/views/controls/label_unittest.cc
@@ -36,6 +36,7 @@
#include "ui/views/border.h"
#include "ui/views/controls/base_control_test_widget.h"
#include "ui/views/controls/link.h"
+#include "ui/views/layout/layout_types.h"
#include "ui/views/style/typography.h"
#include "ui/views/test/ax_event_counter.h"
#include "ui/views/test/focus_manager_test.h"
@@ -474,8 +475,8 @@ TEST_F(LabelTest, ObscuredSurrogatePair) {
// this behavior, therefore this behavior will have to be kept until the code
// with this assumption is fixed. See http://crbug.com/468494 and
// http://crbug.com/467526.
-// TODO(mukai): fix the code assuming this behavior and then fix Label
-// implementation, and remove this test case.
+// TODO(crbug.com/1346889): convert all callsites of GetPreferredSize() to
+// call GetPreferredSize(SizeBounds) instead.
TEST_F(LabelTest, MultilinePreferredSizeTest) {
label()->SetText(u"This is an example.");
@@ -492,6 +493,37 @@ TEST_F(LabelTest, MultilinePreferredSizeTest) {
EXPECT_LT(multi_line_size.height(), new_size.height());
}
+TEST_F(LabelTest, MultilinePreferredSizeWithConstraintTest) {
+ label()->SetText(u"This is an example.");
+
+ const gfx::Size single_line_size =
+ label()->GetPreferredSize({/* Unbounded */});
+
+ // Test the preferred size when the label is not yet laid out.
+ label()->SetMultiLine(true);
+ const gfx::Size multi_line_size_unbounded =
+ label()->GetPreferredSize({/* Unbounded */});
+ EXPECT_EQ(single_line_size, multi_line_size_unbounded);
+
+ const gfx::Size multi_line_size_bounded = label()->GetPreferredSize(
+ {single_line_size.width() / 2, {/* Unbounded */}});
+ EXPECT_GT(multi_line_size_unbounded.width(), multi_line_size_bounded.width());
+ EXPECT_LT(multi_line_size_unbounded.height(),
+ multi_line_size_bounded.height());
+
+ // Test the preferred size after the label is laid out.
+ // GetPreferredSize(SizeBounds) should ignore the existing bounds.
+ const int layout_width = multi_line_size_unbounded.width() / 3;
+ label()->SetBounds(0, 0, layout_width,
+ label()->GetHeightForWidth(layout_width));
+ const gfx::Size multi_line_size_unbounded2 =
+ label()->GetPreferredSize({/* Unbounded */});
+ const gfx::Size multi_line_size_bounded2 = label()->GetPreferredSize(
+ {single_line_size.width() / 2, {/* Unbounded */}});
+ EXPECT_EQ(multi_line_size_unbounded, multi_line_size_unbounded2);
+ EXPECT_EQ(multi_line_size_bounded, multi_line_size_bounded2);
+}
+
TEST_F(LabelTest, SingleLineGetHeightForWidth) {
// Even an empty label should take one line worth of height.
const int line_height = label()->GetLineHeight();
diff --git a/chromium/ui/views/controls/link.cc b/chromium/ui/views/controls/link.cc
index b8d70208bd8..522d723a454 100644
--- a/chromium/ui/views/controls/link.cc
+++ b/chromium/ui/views/controls/link.cc
@@ -59,6 +59,10 @@ void Link::SetForceUnderline(bool force_underline) {
RecalculateFont();
}
+bool Link::GetForceUnderline() const {
+ return force_underline_;
+}
+
ui::Cursor Link::GetCursor(const ui::MouseEvent& event) {
if (!GetEnabled())
return ui::Cursor();
@@ -231,6 +235,7 @@ void Link::ConfigureFocus() {
BEGIN_METADATA(Link, Label)
ADD_READONLY_PROPERTY_METADATA(SkColor, Color, ui::metadata::SkColorConverter)
+ADD_PROPERTY_METADATA(bool, ForceUnderline)
END_METADATA
} // namespace views
diff --git a/chromium/ui/views/controls/link.h b/chromium/ui/views/controls/link.h
index 405b605e539..115976c55b5 100644
--- a/chromium/ui/views/controls/link.h
+++ b/chromium/ui/views/controls/link.h
@@ -61,6 +61,7 @@ class VIEWS_EXPORT Link : public Label {
SkColor GetColor() const;
void SetForceUnderline(bool force_underline);
+ bool GetForceUnderline() const;
// Label:
ui::Cursor GetCursor(const ui::MouseEvent& event) override;
@@ -84,12 +85,12 @@ class VIEWS_EXPORT Link : public Label {
bool IsSelectionSupported() const override;
private:
+ virtual void RecalculateFont();
+
void SetPressed(bool pressed);
void OnClick(const ui::Event& event);
- void RecalculateFont();
-
void ConfigureFocus();
ClickedCallback callback_;
diff --git a/chromium/ui/views/controls/link_fragment.cc b/chromium/ui/views/controls/link_fragment.cc
new file mode 100644
index 00000000000..9f44db50d9e
--- /dev/null
+++ b/chromium/ui/views/controls/link_fragment.cc
@@ -0,0 +1,88 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/link_fragment.h"
+
+#include <string>
+
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/views/controls/link.h"
+#include "ui/views/style/typography.h"
+
+namespace views {
+
+LinkFragment::LinkFragment(const std::u16string& title,
+ int text_context,
+ int text_style,
+ LinkFragment* other_fragment)
+ : Link(title, text_context, text_style),
+ prev_fragment_(this),
+ next_fragment_(this) {
+ // Connect to the previous fragment if it exists.
+ if (other_fragment)
+ Connect(other_fragment);
+}
+
+LinkFragment::~LinkFragment() {
+ Disconnect();
+}
+
+void LinkFragment::Connect(LinkFragment* other_fragment) {
+ DCHECK(prev_fragment_ == this);
+ DCHECK(next_fragment_ == this);
+ DCHECK(other_fragment);
+
+ next_fragment_ = other_fragment->next_fragment_;
+ other_fragment->next_fragment_->prev_fragment_ = this;
+ prev_fragment_ = other_fragment;
+ other_fragment->next_fragment_ = this;
+}
+
+void LinkFragment::Disconnect() {
+ DCHECK((prev_fragment_ != this) == (next_fragment_ != this));
+ if (prev_fragment_ != this) {
+ prev_fragment_->next_fragment_ = next_fragment_;
+ next_fragment_->prev_fragment_ = prev_fragment_;
+ }
+}
+
+bool LinkFragment::IsUnderlined() const {
+ return GetEnabled() &&
+ (HasFocus() || IsMouseHovered() || GetForceUnderline());
+}
+
+void LinkFragment::RecalculateFont() {
+ // Check whether any link fragment should be underlined.
+ bool should_be_underlined = IsUnderlined();
+ for (LinkFragment* current_fragment = next_fragment_;
+ !should_be_underlined && current_fragment != this;
+ current_fragment = current_fragment->next_fragment_) {
+ should_be_underlined = current_fragment->IsUnderlined();
+ }
+
+ // If the style differs from the current one, update.
+ if ((font_list().GetFontStyle() & gfx::Font::UNDERLINE) !=
+ should_be_underlined) {
+ auto MaybeUpdateStyle = [should_be_underlined](LinkFragment* fragment) {
+ const int style = fragment->font_list().GetFontStyle();
+ const int intended_style = should_be_underlined
+ ? (style | gfx::Font::UNDERLINE)
+ : (style & ~gfx::Font::UNDERLINE);
+ fragment->Label::SetFontList(
+ fragment->font_list().DeriveWithStyle(intended_style));
+ fragment->SchedulePaint();
+ };
+ MaybeUpdateStyle(this);
+ for (LinkFragment* current_fragment = next_fragment_;
+ current_fragment != this;
+ current_fragment = current_fragment->next_fragment_) {
+ MaybeUpdateStyle(current_fragment);
+ }
+ }
+}
+
+BEGIN_METADATA(LinkFragment, Link)
+END_METADATA
+
+} // namespace views
diff --git a/chromium/ui/views/controls/link_fragment.h b/chromium/ui/views/controls/link_fragment.h
new file mode 100644
index 00000000000..20a7b87d534
--- /dev/null
+++ b/chromium/ui/views/controls/link_fragment.h
@@ -0,0 +1,57 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_CONTROLS_LINK_FRAGMENT_H_
+#define UI_VIEWS_CONTROLS_LINK_FRAGMENT_H_
+
+#include "base/memory/raw_ptr.h"
+#include "ui/views/controls/link.h"
+#include "ui/views/metadata/view_factory.h"
+#include "ui/views/style/typography.h"
+
+namespace views {
+
+// A `LinkFragment` can be used to represent a logical link that spans across
+// multiple lines. Connected `LinkFragment`s adjust their style if any single
+// one of them is hovered over of focused.
+class VIEWS_EXPORT LinkFragment : public Link {
+ public:
+ METADATA_HEADER(LinkFragment);
+
+ explicit LinkFragment(const std::u16string& title = std::u16string(),
+ int text_context = style::CONTEXT_LABEL,
+ int text_style = style::STYLE_LINK,
+ LinkFragment* other_fragment = nullptr);
+ ~LinkFragment() override;
+
+ LinkFragment(const LinkFragment&) = delete;
+ LinkFragment& operator=(const LinkFragment&) = delete;
+
+ private:
+ // Returns whether this fragment indicates that the entire link represented
+ // by it should be underlined.
+ bool IsUnderlined() const;
+
+ // Connects `this` to the `other_fragment`.
+ void Connect(LinkFragment* other_fragment);
+
+ // Disconnects `this` from any other fragments that it may be connected to.
+ void Disconnect();
+
+ // Recalculates the font style for this link fragment and, if it is changed,
+ // updates both this fragment and all other that are connected to it.
+ void RecalculateFont() override;
+
+ // Pointers to the previous and the next `LinkFragment` if the logical link
+ // represented by `this` consists of multiple such fragments (e.g. due to
+ // line breaks).
+ // If the logical link is just a single `LinkFragment` component, then these
+ // pointers point to `this`.
+ raw_ptr<LinkFragment> prev_fragment_;
+ raw_ptr<LinkFragment> next_fragment_;
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_LINK_FRAGMENT_H_
diff --git a/chromium/ui/views/controls/link_fragment_unittest.cc b/chromium/ui/views/controls/link_fragment_unittest.cc
new file mode 100644
index 00000000000..99ee66231ad
--- /dev/null
+++ b/chromium/ui/views/controls/link_fragment_unittest.cc
@@ -0,0 +1,140 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/link_fragment.h"
+
+#include <array>
+#include <memory>
+
+#include "base/memory/raw_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/views/controls/base_control_test_widget.h"
+#include "ui/views/test/view_metadata_test_utils.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+
+namespace {
+
+constexpr char16_t kLinkLabel[] = u"Test label";
+
+class LinkFragmentTest : public test::BaseControlTestWidget {
+ public:
+ LinkFragmentTest() {
+ for (auto& fragment : fragments_) {
+ fragment = nullptr;
+ }
+ }
+ ~LinkFragmentTest() override = default;
+
+ void SetUp() override {
+ test::BaseControlTestWidget::SetUp();
+
+ event_generator_ = std::make_unique<ui::test::EventGenerator>(
+ GetContext(), widget()->GetNativeWindow());
+ }
+
+ protected:
+ void CreateWidgetContent(View* container) override {
+ // Fragment 0 is stand-alone.
+ fragments_[0] =
+ container->AddChildView(std::make_unique<LinkFragment>(kLinkLabel));
+ gfx::Rect current_rect =
+ gfx::ScaleToEnclosedRect(container->GetLocalBounds(), 0.3f);
+ fragments_[0]->SetBoundsRect(current_rect);
+ int width = current_rect.width();
+
+ // Fragments 1 and 2 are connected.
+ current_rect.Offset(width, 0);
+ fragments_[1] =
+ container->AddChildView(std::make_unique<LinkFragment>(kLinkLabel));
+ fragments_[1]->SetBoundsRect(current_rect);
+
+ current_rect.Offset(width, 0);
+ fragments_[2] = container->AddChildView(std::make_unique<LinkFragment>(
+ kLinkLabel, style::CONTEXT_LABEL, style::STYLE_LINK, fragment(1)));
+ fragments_[2]->SetBoundsRect(current_rect);
+ }
+
+ LinkFragment* fragment(size_t index) {
+ DCHECK_LT(index, 3u);
+ return fragments_[index];
+ }
+ ui::test::EventGenerator* event_generator() { return event_generator_.get(); }
+
+ // Returns bounds of the fragment.
+ gfx::Rect GetBoundsForFragment(size_t index) {
+ return fragment(index)->GetBoundsInScreen();
+ }
+
+ private:
+ std::array<raw_ptr<LinkFragment>, 3> fragments_;
+ std::unique_ptr<ui::test::EventGenerator> event_generator_;
+};
+
+} // namespace
+
+TEST_F(LinkFragmentTest, Metadata) {
+ for (size_t index = 0; index < 3; ++index) {
+ // Needed to avoid failing DCHECK when setting maximum width.
+ fragment(index)->SetMultiLine(true);
+ test::TestViewMetadata(fragment(index));
+ }
+}
+
+// Tests that hovering and unhovering a link adds and removes an underline
+// under all connected fragments.
+TEST_F(LinkFragmentTest, TestUnderlineOnHover) {
+ // A non-hovered link fragment should not be underlined.
+ const gfx::Point point_outside =
+ GetBoundsForFragment(2).bottom_right() + gfx::Vector2d(1, 1);
+ event_generator()->MoveMouseTo(point_outside);
+ EXPECT_FALSE(fragment(0)->IsMouseHovered());
+
+ const auto is_underlined = [this](size_t index) {
+ return !!(fragment(index)->font_list().GetFontStyle() &
+ gfx::Font::UNDERLINE);
+ };
+ EXPECT_FALSE(is_underlined(0));
+
+ // Hovering the first link fragment underlines it.
+ event_generator()->MoveMouseTo(GetBoundsForFragment(0).CenterPoint());
+ EXPECT_TRUE(fragment(0)->IsMouseHovered());
+ EXPECT_TRUE(is_underlined(0));
+ // The other link fragments stay non-hovered.
+ EXPECT_FALSE(is_underlined(1));
+ EXPECT_FALSE(is_underlined(2));
+
+ // Un-hovering the link removes the underline again.
+ event_generator()->MoveMouseTo(point_outside);
+ EXPECT_FALSE(fragment(0)->IsMouseHovered());
+ EXPECT_FALSE(is_underlined(0));
+ EXPECT_FALSE(is_underlined(1));
+ EXPECT_FALSE(is_underlined(2));
+
+ // Hovering the second link fragment underlines both the second and the
+ // third fragment.
+ event_generator()->MoveMouseTo(GetBoundsForFragment(1).CenterPoint());
+ EXPECT_TRUE(fragment(1)->IsMouseHovered());
+ EXPECT_FALSE(fragment(2)->IsMouseHovered());
+ EXPECT_FALSE(is_underlined(0));
+ EXPECT_TRUE(is_underlined(1));
+ EXPECT_TRUE(is_underlined(2));
+
+ // The same is true for hovering the third fragment.
+ event_generator()->MoveMouseTo(GetBoundsForFragment(2).CenterPoint());
+ EXPECT_TRUE(fragment(2)->IsMouseHovered());
+ EXPECT_FALSE(is_underlined(0));
+ EXPECT_TRUE(is_underlined(1));
+ EXPECT_TRUE(is_underlined(2));
+
+ // Moving outside removes the underline again.
+ event_generator()->MoveMouseTo(point_outside);
+ EXPECT_FALSE(is_underlined(0));
+ EXPECT_FALSE(is_underlined(1));
+ EXPECT_FALSE(is_underlined(2));
+}
+
+} // namespace views
diff --git a/chromium/ui/views/controls/menu/menu_closure_animation_mac.h b/chromium/ui/views/controls/menu/menu_closure_animation_mac.h
index dc5c283b86d..7d6d1f5fd03 100644
--- a/chromium/ui/views/controls/menu/menu_closure_animation_mac.h
+++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "ui/gfx/animation/animation.h"
@@ -83,8 +84,8 @@ class VIEWS_EXPORT MenuClosureAnimationMac
base::OnceClosure callback_;
base::OneShotTimer timer_;
std::unique_ptr<gfx::Animation> fade_animation_;
- MenuItemView* item_;
- SubmenuView* menu_;
+ raw_ptr<MenuItemView> item_;
+ raw_ptr<SubmenuView> menu_;
AnimationStep step_;
};
diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc
index 1f1ea9ec4b5..b46a42d9ee9 100644
--- a/chromium/ui/views/controls/menu/menu_controller.cc
+++ b/chromium/ui/views/controls/menu/menu_controller.cc
@@ -317,7 +317,8 @@ static void RepostEventImpl(const ui::LocatedEvent* event,
window_y = pt.y;
}
- WPARAM target = client_area ? event->native_event().wParam : nc_hit_result;
+ WPARAM target = client_area ? event->native_event().wParam
+ : static_cast<WPARAM>(nc_hit_result);
LPARAM window_coords = MAKELPARAM(window_x, window_y);
PostMessage(target_window, event_type, target, window_coords);
return;
@@ -335,7 +336,7 @@ static void RepostEventImpl(const ui::LocatedEvent* event,
gfx::Point root_loc(screen_loc);
spc->ConvertPointFromScreen(root, &root_loc);
- std::unique_ptr<ui::Event> clone = ui::Event::Clone(*event);
+ std::unique_ptr<ui::Event> clone = event->Clone();
std::unique_ptr<ui::LocatedEvent> located_event(
static_cast<ui::LocatedEvent*>(clone.release()));
located_event->set_location(root_loc);
@@ -434,18 +435,18 @@ struct MenuController::SelectByCharDetails {
SelectByCharDetails() = default;
// Index of the first menu with the specified mnemonic.
- int first_match = -1;
+ absl::optional<size_t> first_match;
// If true there are multiple menu items with the same mnemonic.
bool has_multiple = false;
- // Index of the selected item; may remain -1.
- int index_of_item = -1;
+ // Index of the selected item; may remain nullopt.
+ absl::optional<size_t> index_of_item;
// If there are multiple matches this is the index of the item after the
- // currently selected item whose mnemonic matches. This may remain -1 even
- // though there are matches.
- int next_match = -1;
+ // currently selected item whose mnemonic matches. This may remain nullopt
+ // even though there are matches.
+ absl::optional<size_t> next_match;
};
// MenuController:State ------------------------------------------------------
@@ -937,7 +938,13 @@ void MenuController::OnGestureEvent(SubmenuView* source,
} else if (event->type() == ui::ET_GESTURE_TAP) {
if (!part.is_scroll() && part.menu &&
!(part.should_submenu_show && part.menu->HasSubmenu())) {
- if (part.menu->GetDelegate()->IsTriggerableEvent(part.menu, *event)) {
+ const int command = part.menu->GetCommand();
+ if (part.menu->GetDelegate()->ShouldExecuteCommandWithoutClosingMenu(
+ command, *event)) {
+ item_selected_by_touch_ = true;
+ part.menu->GetDelegate()->ExecuteCommand(command, 0);
+ } else if (part.menu->GetDelegate()->IsTriggerableEvent(part.menu,
+ *event)) {
item_selected_by_touch_ = true;
Accept(part.menu, event->flags());
}
@@ -1605,7 +1612,13 @@ bool MenuController::OnKeyPressed(const ui::KeyEvent& event) {
handled_key_code = true;
if (!SendAcceleratorToHotTrackedView(event.flags()) &&
pending_state_.item->GetEnabled()) {
- Accept(pending_state_.item, event.flags());
+ const int command = pending_state_.item->GetCommand();
+ if (pending_state_.item->GetDelegate()
+ ->ShouldExecuteCommandWithoutClosingMenu(command, event)) {
+ pending_state_.item->GetDelegate()->ExecuteCommand(command, 0);
+ } else {
+ Accept(pending_state_.item, event.flags());
+ }
}
}
}
@@ -2059,14 +2072,14 @@ void MenuController::CommitPendingSelection() {
state_ = pending_state_;
state_.open_leading.swap(pending_open_direction);
- int menu_depth = MenuDepth(state_.item);
+ size_t menu_depth = MenuDepth(state_.item);
if (menu_depth == 0) {
state_.open_leading.clear();
} else {
- int cached_size = static_cast<int>(state_.open_leading.size());
- DCHECK_GE(menu_depth, 0);
- while (cached_size-- >= menu_depth)
+ for (size_t cached_size = state_.open_leading.size();
+ cached_size >= menu_depth; --cached_size) {
state_.open_leading.pop_back();
+ }
}
if (!state_.item) {
@@ -2256,10 +2269,10 @@ void MenuController::BuildPathsAndCalculateDiff(
BuildMenuItemPath(old_item, old_path);
BuildMenuItemPath(new_item, new_path);
- *first_diff_at = std::distance(
+ *first_diff_at = static_cast<size_t>(std::distance(
old_path->cbegin(), std::mismatch(old_path->cbegin(), old_path->cend(),
new_path->cbegin(), new_path->cend())
- .first);
+ .first));
}
void MenuController::BuildMenuItemPath(MenuItemView* item,
@@ -2752,8 +2765,8 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(
}
// static
-int MenuController::MenuDepth(MenuItemView* item) {
- return item ? (MenuDepth(item->GetParentMenuItem()) + 1) : 0;
+size_t MenuController::MenuDepth(MenuItemView* item) {
+ return item ? (MenuDepth(item->GetParentMenuItem()) + 1) : size_t{0};
}
void MenuController::IncrementSelection(
@@ -2813,9 +2826,11 @@ void MenuController::SetSelectionIndices(MenuItemView* parent) {
if (ordering.empty())
return;
- const int set_size = ordering.size();
- for (int i = 0; i < set_size; ++i)
- ordering[i]->GetViewAccessibility().OverridePosInSet(i + 1, set_size);
+ const size_t set_size = ordering.size();
+ for (size_t i = 0; i < set_size; ++i) {
+ ordering[i]->GetViewAccessibility().OverridePosInSet(
+ static_cast<int>(i + 1), static_cast<int>(set_size));
+ }
}
void MenuController::MoveSelectionToFirstOrLastItem(
@@ -2842,38 +2857,20 @@ void MenuController::MoveSelectionToFirstOrLastItem(
MenuItemView* MenuController::FindInitialSelectableMenuItem(
MenuItemView* parent,
SelectionIncrementDirectionType direction) {
- return FindNextSelectableMenuItem(
- parent, direction == INCREMENT_SELECTION_DOWN ? -1 : 0, direction, true);
-}
-
-MenuItemView* MenuController::FindNextSelectableMenuItem(
- MenuItemView* parent,
- int index,
- SelectionIncrementDirectionType direction,
- bool is_initial) {
- int parent_count =
- static_cast<int>(parent->GetSubmenu()->GetMenuItems().size());
- int stop_index = (index + parent_count) % parent_count;
- bool include_all_items =
- (index == -1 && direction == INCREMENT_SELECTION_DOWN) ||
- (index == 0 && direction == INCREMENT_SELECTION_UP);
- int delta = direction == INCREMENT_SELECTION_UP ? -1 : 1;
- // Loop through the menu items skipping any invisible menus. The loop stops
- // when we wrap or find a visible and enabled child.
- do {
- if (!MenuConfig::instance().arrow_key_selection_wraps && !is_initial) {
- if (index == 0 && direction == INCREMENT_SELECTION_UP)
- return nullptr;
- if (index == parent_count - 1 && direction == INCREMENT_SELECTION_DOWN)
- return nullptr;
- }
- index = (index + delta + parent_count) % parent_count;
- if (index == stop_index && !include_all_items)
- return nullptr;
- MenuItemView* child = parent->GetSubmenu()->GetMenuItemAt(index);
- if (child->IsTraversableByKeyboard())
- return child;
- } while (index != stop_index);
+ const size_t parent_count = parent->GetSubmenu()->GetMenuItems().size();
+ if (direction == INCREMENT_SELECTION_DOWN) {
+ for (size_t index = 0; index < parent_count; ++index) {
+ MenuItemView* child = parent->GetSubmenu()->GetMenuItemAt(index);
+ if (child->IsTraversableByKeyboard())
+ return child;
+ }
+ } else {
+ for (size_t index = parent_count; index > 0; --index) {
+ MenuItemView* child = parent->GetSubmenu()->GetMenuItemAt(index - 1);
+ if (child->IsTraversableByKeyboard())
+ return child;
+ }
+ }
return nullptr;
}
@@ -2920,15 +2917,16 @@ MenuController::SelectByCharDetails MenuController::FindChildForMnemonic(
MenuItemView* child = menu_items[i];
if (child->GetEnabled() && child->GetVisible()) {
if (child == pending_state_.item)
- details.index_of_item = static_cast<int>(i);
+ details.index_of_item = i;
if (match_function(child, key)) {
- if (details.first_match == -1)
- details.first_match = static_cast<int>(i);
- else
+ if (!details.first_match.has_value()) {
+ details.first_match = i;
+ } else {
details.has_multiple = true;
- if (details.next_match == -1 && details.index_of_item != -1 &&
- static_cast<int>(i) > details.index_of_item)
- details.next_match = static_cast<int>(i);
+ }
+ if (!details.next_match.has_value() &&
+ details.index_of_item.has_value() && i > details.index_of_item)
+ details.next_match = i;
}
}
}
@@ -2938,23 +2936,25 @@ MenuController::SelectByCharDetails MenuController::FindChildForMnemonic(
void MenuController::AcceptOrSelect(MenuItemView* parent,
const SelectByCharDetails& details) {
// This should only be invoked if there is a match.
- DCHECK_NE(details.first_match, -1);
+ DCHECK(details.first_match.has_value());
DCHECK(parent->HasSubmenu());
SubmenuView* submenu = parent->GetSubmenu();
DCHECK(submenu);
if (!details.has_multiple) {
// There's only one match, activate it (or open if it has a submenu).
- if (submenu->GetMenuItemAt(details.first_match)->HasSubmenu()) {
- SetSelection(submenu->GetMenuItemAt(details.first_match),
+ if (submenu->GetMenuItemAt(details.first_match.value())->HasSubmenu()) {
+ SetSelection(submenu->GetMenuItemAt(details.first_match.value()),
SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
} else {
- Accept(submenu->GetMenuItemAt(details.first_match), 0);
+ Accept(submenu->GetMenuItemAt(details.first_match.value()), 0);
}
- } else if (details.index_of_item == -1 || details.next_match == -1) {
- SetSelection(submenu->GetMenuItemAt(details.first_match),
+ } else if (!details.index_of_item.has_value() ||
+ !details.next_match.has_value()) {
+ SetSelection(submenu->GetMenuItemAt(details.first_match.value()),
SELECTION_DEFAULT);
} else {
- SetSelection(submenu->GetMenuItemAt(details.next_match), SELECTION_DEFAULT);
+ SetSelection(submenu->GetMenuItemAt(details.next_match.value()),
+ SELECTION_DEFAULT);
}
}
@@ -2979,7 +2979,7 @@ void MenuController::SelectByChar(char16_t character) {
// Look for matches based on mnemonic first.
SelectByCharDetails details =
FindChildForMnemonic(item, key, &MatchesMnemonic);
- if (details.first_match != -1) {
+ if (details.first_match.has_value()) {
AcceptOrSelect(item, details);
return;
}
@@ -2992,7 +2992,7 @@ void MenuController::SelectByChar(char16_t character) {
} else {
// If no mnemonics found, look at first character of titles.
details = FindChildForMnemonic(item, key, &TitleMatchesMnemonic);
- if (details.first_match != -1)
+ if (details.first_match.has_value())
AcceptOrSelect(item, details);
}
}
@@ -3317,13 +3317,33 @@ void MenuController::SetNextHotTrackedView(
if (!parent)
return;
const auto menu_items = parent->GetSubmenu()->GetMenuItems();
- if (menu_items.size() <= 1)
+ const size_t num_menu_items = menu_items.size();
+ if (num_menu_items <= 1)
return;
const auto i = std::find(menu_items.cbegin(), menu_items.cend(), item);
DCHECK(i != menu_items.cend());
- MenuItemView* to_select = FindNextSelectableMenuItem(
- parent, std::distance(menu_items.cbegin(), i), direction, false);
- SetInitialHotTrackedView(to_select, direction);
+ auto index = static_cast<size_t>(std::distance(menu_items.cbegin(), i));
+
+ // Loop through the menu items in the desired direction. Assume we can wrap
+ // all the way back to this item.
+ size_t stop_index = index;
+ if (!MenuConfig::instance().arrow_key_selection_wraps) {
+ // Don't want to allow wrapping, so stop as soon as it happens.
+ stop_index = direction == INCREMENT_SELECTION_UP ? (num_menu_items - 1) : 0;
+ }
+ const size_t delta =
+ direction == INCREMENT_SELECTION_UP ? (num_menu_items - 1) : 1;
+ while (true) {
+ index = (index + delta) % num_menu_items;
+ if (index == stop_index)
+ return;
+ // Stop on the next keyboard-traversable item.
+ MenuItemView* child = parent->GetSubmenu()->GetMenuItemAt(index);
+ if (child->IsTraversableByKeyboard()) {
+ SetInitialHotTrackedView(child, direction);
+ return;
+ }
+ }
}
void MenuController::SetHotTrackedButton(Button* new_hot_button) {
@@ -3403,7 +3423,6 @@ void MenuController::SetAnchorParametersForItem(MenuItemView* item,
anchor->anchor_gravity = ui::OwnedWindowAnchorGravity::kBottomRight;
anchor->constraint_adjustment =
ui::OwnedWindowConstraintAdjustment::kAdjustmentSlideX |
- ui::OwnedWindowConstraintAdjustment::kAdjustmentSlideY |
ui::OwnedWindowConstraintAdjustment::kAdjustmentFlipY |
ui::OwnedWindowConstraintAdjustment::kAdjustmentRezizeY;
} else {
diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h
index a4958eb4244..ed374eab8e4 100644
--- a/chromium/ui/views/controls/menu/menu_controller.h
+++ b/chromium/ui/views/controls/menu/menu_controller.h
@@ -521,7 +521,7 @@ class VIEWS_EXPORT MenuController
ui::OwnedWindowAnchor* anchor);
// Returns the depth of the menu.
- static int MenuDepth(MenuItemView* item);
+ static size_t MenuDepth(MenuItemView* item);
// Selects the next or previous (depending on |direction|) menu item.
void IncrementSelection(SelectionIncrementDirectionType direction);
@@ -545,15 +545,6 @@ class VIEWS_EXPORT MenuController
MenuItemView* parent,
SelectionIncrementDirectionType direction);
- // Returns the next or previous selectable child menu item of |parent|
- // starting at |index| and incrementing or decrementing index by 1 depending
- // on |direction|. If there are no more selectable items NULL is returned.
- MenuItemView* FindNextSelectableMenuItem(
- MenuItemView* parent,
- int index,
- SelectionIncrementDirectionType direction,
- bool is_initial);
-
// If the selected item has a submenu and it isn't currently open, the
// the selection is changed such that the menu opens immediately.
void OpenSubmenuChangeSelectionIfCan();
diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc
index 0b68148ce1e..298f2fe71ac 100644
--- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc
@@ -721,17 +721,6 @@ class MenuControllerTest : public ViewsTestBase,
parent, MenuController::INCREMENT_SELECTION_UP);
}
- MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent, int index) {
- return menu_controller_->FindNextSelectableMenuItem(
- parent, index, MenuController::INCREMENT_SELECTION_DOWN, false);
- }
-
- MenuItemView* FindPreviousSelectableMenuItem(MenuItemView* parent,
- int index) {
- return menu_controller_->FindNextSelectableMenuItem(
- parent, index, MenuController::INCREMENT_SELECTION_UP, false);
- }
-
internal::MenuControllerDelegate* GetCurrentDelegate() {
return menu_controller_->delegate_;
}
@@ -768,6 +757,10 @@ class MenuControllerTest : public ViewsTestBase,
// Note that coordinates of events passed to MenuController must be in that of
// the MenuScrollViewContainer.
+ void ProcessGestureEvent(SubmenuView* source, ui::GestureEvent& event) {
+ menu_controller_->OnGestureEvent(source, &event);
+ }
+
void ProcessMousePressed(SubmenuView* source, const ui::MouseEvent& event) {
menu_controller_->OnMousePressed(source, event);
}
@@ -1065,11 +1058,6 @@ TEST_F(MenuControllerTest, InitialSelectedItem) {
ASSERT_NE(nullptr, last_selectable);
EXPECT_EQ(2, last_selectable->GetCommand());
- // There should be no next or previous selectable item since there is only a
- // single enabled item in the menu.
- EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1));
- EXPECT_EQ(nullptr, FindPreviousSelectableMenuItem(menu_item(), 1));
-
// Clear references in menu controller to the menu item that is going away.
ResetSelection();
}
@@ -2839,7 +2827,6 @@ TEST_F(MenuControllerTest, ContextMenuInitializesAuraWindowWhenShown) {
anchor->anchor_position);
EXPECT_EQ(ui::OwnedWindowAnchorGravity::kBottomRight, anchor->anchor_gravity);
EXPECT_EQ((ui::OwnedWindowConstraintAdjustment::kAdjustmentSlideX |
- ui::OwnedWindowConstraintAdjustment::kAdjustmentSlideY |
ui::OwnedWindowConstraintAdjustment::kAdjustmentFlipY |
ui::OwnedWindowConstraintAdjustment::kAdjustmentRezizeY),
anchor->constraint_adjustment);
@@ -3256,5 +3243,75 @@ TEST_F(MenuControllerTest, BrowserHotkeysCancelMenusAndAreRedispatched) {
}
#endif
+class ExecuteCommandWithoutClosingMenuTest : public MenuControllerTest {
+ public:
+ void SetUp() override {
+ MenuControllerTest::SetUp();
+
+ views::test::DisableMenuClosureAnimations();
+ menu_controller()->Run(owner(), nullptr, menu_item(), gfx::Rect(),
+ MenuAnchorPosition::kTopLeft, false, false);
+
+ MenuHost::InitParams params;
+ params.parent = owner();
+ params.bounds = gfx::Rect(0, 0, 100, 100);
+ params.do_capture = false;
+ menu_item()->GetSubmenu()->ShowAt(params);
+
+ menu_delegate()->set_should_execute_command_without_closing_menu(true);
+ }
+};
+
+TEST_F(ExecuteCommandWithoutClosingMenuTest, OnClick) {
+ TestMenuControllerDelegate* delegate = menu_controller_delegate();
+ EXPECT_EQ(0, delegate->on_menu_closed_called());
+
+ MenuItemView* menu_item_view = menu_item()->GetSubmenu()->GetMenuItemAt(0);
+ gfx::Point press_location(menu_item_view->bounds().CenterPoint());
+ ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, press_location,
+ press_location, ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, 0);
+ ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, press_location,
+ press_location, ui::EventTimeForNow(),
+ ui::EF_LEFT_MOUSE_BUTTON, 0);
+ ProcessMousePressed(menu_item()->GetSubmenu(), press_event);
+ ProcessMouseReleased(menu_item()->GetSubmenu(), release_event);
+
+ EXPECT_EQ(0, delegate->on_menu_closed_called());
+ EXPECT_TRUE(IsShowing());
+ EXPECT_EQ(menu_delegate()->execute_command_id(),
+ menu_item_view->GetCommand());
+}
+
+TEST_F(ExecuteCommandWithoutClosingMenuTest, OnTap) {
+ TestMenuControllerDelegate* delegate = menu_controller_delegate();
+ EXPECT_EQ(0, delegate->on_menu_closed_called());
+
+ MenuItemView* menu_item_view = menu_item()->GetSubmenu()->GetMenuItemAt(0);
+ gfx::Point tap_location(menu_item_view->bounds().CenterPoint());
+ ui::GestureEvent event(tap_location.x(), tap_location.y(), 0,
+ ui::EventTimeForNow(),
+ ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+ ProcessGestureEvent(menu_item()->GetSubmenu(), event);
+
+ EXPECT_EQ(0, delegate->on_menu_closed_called());
+ EXPECT_TRUE(IsShowing());
+ EXPECT_EQ(menu_delegate()->execute_command_id(),
+ menu_item_view->GetCommand());
+}
+
+TEST_F(ExecuteCommandWithoutClosingMenuTest, OnReturnKey) {
+ TestMenuControllerDelegate* delegate = menu_controller_delegate();
+ EXPECT_EQ(0, delegate->on_menu_closed_called());
+
+ DispatchKey(ui::VKEY_DOWN);
+ DispatchKey(ui::VKEY_RETURN);
+
+ EXPECT_EQ(0, delegate->on_menu_closed_called());
+ EXPECT_TRUE(IsShowing());
+ EXPECT_EQ(menu_delegate()->execute_command_id(),
+ menu_item()->GetSubmenu()->GetMenuItemAt(0)->GetCommand());
+}
+
} // namespace test
} // namespace views
diff --git a/chromium/ui/views/controls/menu/menu_insertion_delegate_win.h b/chromium/ui/views/controls/menu/menu_insertion_delegate_win.h
index 461620c55e4..235c34e7eee 100644
--- a/chromium/ui/views/controls/menu/menu_insertion_delegate_win.h
+++ b/chromium/ui/views/controls/menu/menu_insertion_delegate_win.h
@@ -12,7 +12,7 @@ namespace views {
class MenuInsertionDelegateWin {
public:
// Returns the index to insert items into the menu at.
- virtual int GetInsertionIndex(HMENU native_menu) = 0;
+ virtual size_t GetInsertionIndex(HMENU native_menu) = 0;
protected:
virtual ~MenuInsertionDelegateWin() = default;
diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc
index 6da48e9716b..854398921a3 100644
--- a/chromium/ui/views/controls/menu/menu_item_view.cc
+++ b/chromium/ui/views/controls/menu/menu_item_view.cc
@@ -12,6 +12,7 @@
#include <numeric>
#include <utility>
+#include "base/auto_reset.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/i18n/case_conversion.h"
@@ -97,9 +98,6 @@ class VerticalSeparator : public Separator {
VerticalSeparator(const VerticalSeparator&) = delete;
VerticalSeparator& operator=(const VerticalSeparator&) = delete;
~VerticalSeparator() override = default;
-
- // Separator:
- void OnThemeChanged() override;
};
VerticalSeparator::VerticalSeparator() {
@@ -109,15 +107,11 @@ VerticalSeparator::VerticalSeparator() {
gfx::Size(config.actionable_submenu_vertical_separator_width,
config.actionable_submenu_vertical_separator_height));
SetCanProcessEventsWithinSubtree(false);
-}
-
-void VerticalSeparator::OnThemeChanged() {
- Separator::OnThemeChanged();
ui::ColorId id = ui::kColorMenuSeparator;
#if BUILDFLAG(IS_CHROMEOS_ASH)
id = ui::kColorAshSystemUIMenuSeparator;
#endif
- SetColor(GetColorProvider()->GetColor(id));
+ SetColorId(id);
}
BEGIN_METADATA(VerticalSeparator, Separator)
@@ -171,8 +165,10 @@ void MenuItemView::ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) {
// Whether the selection is painted may change based on the number of
// children.
- if (details.parent == this)
+ if (details.parent == this &&
+ update_selection_based_state_in_view_herarchy_changed_) {
UpdateSelectionBasedStateIfChanged(PaintMode::kNormal);
+ }
}
std::u16string MenuItemView::GetTooltipText(const gfx::Point& p) const {
@@ -356,7 +352,7 @@ void MenuItemView::Cancel() {
}
MenuItemView* MenuItemView::AddMenuItemAt(
- int index,
+ size_t index,
int item_id,
const std::u16string& label,
const std::u16string& secondary_label,
@@ -366,10 +362,9 @@ MenuItemView* MenuItemView::AddMenuItemAt(
Type type,
ui::MenuSeparatorType separator_style) {
DCHECK_NE(type, Type::kEmpty);
- DCHECK_GE(index, 0);
if (!submenu_)
CreateSubmenu();
- DCHECK_LE(static_cast<size_t>(index), submenu_->children().size());
+ DCHECK_LE(index, submenu_->children().size());
if (type == Type::kSeparator) {
submenu_->AddChildViewAt(std::make_unique<MenuSeparator>(separator_style),
index);
@@ -429,7 +424,7 @@ void MenuItemView::AppendSeparator() {
AppendMenuItemImpl(0, std::u16string(), ui::ImageModel(), Type::kSeparator);
}
-void MenuItemView::AddSeparatorAt(int index) {
+void MenuItemView::AddSeparatorAt(size_t index) {
AddMenuItemAt(index, /*item_id=*/0, /*label=*/std::u16string(),
/*secondary_label=*/std::u16string(),
/*minor_text=*/std::u16string(),
@@ -443,8 +438,7 @@ MenuItemView* MenuItemView::AppendMenuItemImpl(int item_id,
const std::u16string& label,
const ui::ImageModel& icon,
Type type) {
- const int index =
- submenu_ ? static_cast<int>(submenu_->children().size()) : 0;
+ const size_t index = submenu_ ? submenu_->children().size() : size_t{0};
return AddMenuItemAt(index, item_id, label, std::u16string(),
std::u16string(), ui::ImageModel(), icon, type,
ui::NORMAL_SEPARATOR);
@@ -559,13 +553,22 @@ void MenuItemView::SetIcon(const ui::ImageModel& icon) {
}
void MenuItemView::SetIconView(std::unique_ptr<ImageView> icon_view) {
- if (icon_view_) {
- RemoveChildViewT(icon_view_.get());
- icon_view_ = nullptr;
+ {
+ // See comment in `update_selection_based_state_in_view_herarchy_changed_`
+ // as to why setting the field and explicitly calling
+ // UpdateSelectionBasedStateIfChanged() is necessary.
+ base::AutoReset setter(
+ &update_selection_based_state_in_view_herarchy_changed_, false);
+ if (icon_view_) {
+ RemoveChildViewT(icon_view_.get());
+ icon_view_ = nullptr;
+ }
+
+ if (icon_view)
+ icon_view_ = AddChildView(std::move(icon_view));
}
- if (icon_view)
- icon_view_ = AddChildView(std::move(icon_view));
+ UpdateSelectionBasedStateIfChanged(PaintMode::kNormal);
InvalidateLayout();
SchedulePaint();
@@ -1433,6 +1436,11 @@ gfx::Insets MenuItemView::GetContainerMargins() const {
}
int MenuItemView::NonIconChildViewsCount() const {
+ // WARNING: if adding a new field that is checked here you may need to
+ // set `update_selection_based_state_in_view_herarchy_changed_` to false
+ // when setting the field and explicitly call
+ // UpdateSelectionBasedStateIfChanged(). See comment in header
+ // for details.
return static_cast<int>(children().size()) - (icon_view_ ? 1 : 0) -
(radio_check_image_view_ ? 1 : 0) -
(submenu_arrow_image_view_ ? 1 : 0) - (vertical_separator_ ? 1 : 0);
diff --git a/chromium/ui/views/controls/menu/menu_item_view.h b/chromium/ui/views/controls/menu/menu_item_view.h
index 4fb6b50dd06..d531717529e 100644
--- a/chromium/ui/views/controls/menu/menu_item_view.h
+++ b/chromium/ui/views/controls/menu/menu_item_view.h
@@ -154,7 +154,7 @@ class VIEWS_EXPORT MenuItemView : public View {
// Add an item to the menu at a specified index. ChildrenChanged() should
// called after adding menu items if the menu may be active.
- MenuItemView* AddMenuItemAt(int index,
+ MenuItemView* AddMenuItemAt(size_t index,
int item_id,
const std::u16string& label,
const std::u16string& secondary_label,
@@ -194,7 +194,7 @@ class VIEWS_EXPORT MenuItemView : public View {
void AppendSeparator();
// Adds a separator to this menu at the specified position.
- void AddSeparatorAt(int index);
+ void AddSeparatorAt(size_t index);
// All the AppendXXX methods funnel into this.
MenuItemView* AppendMenuItemImpl(int item_id,
@@ -369,7 +369,6 @@ class VIEWS_EXPORT MenuItemView : public View {
bool is_alerted() const { return is_alerted_; }
// Returns whether or not a "new" badge should be shown on this menu item.
- // Takes into account whether the badging feature is enabled.
bool ShouldShowNewBadge() const;
// Returns whether keyboard navigation through the menu should stop on this
@@ -580,8 +579,8 @@ class VIEWS_EXPORT MenuItemView : public View {
// Command id.
int command_ = 0;
- // Whether the menu item should be badged as "New" (if badging is enabled) as
- // a way to highlight a new feature for users.
+ // Whether the menu item should be badged as "New" as a way to highlight a new
+ // feature for users.
bool is_new_ = false;
// Whether the menu item contains user-created text.
@@ -663,6 +662,19 @@ class VIEWS_EXPORT MenuItemView : public View {
// Whether this menu item is rendered differently to draw attention to it.
bool is_alerted_ = false;
+
+ // If true, ViewHierarchyChanged() will call
+ // UpdateSelectionBasedStateIfChanged().
+ // UpdateSelectionBasedStateIfChanged() calls to NonIconChildViewsCount().
+ // NonIconChildViewsCount() accesses fields of type View as part of the
+ // implementation. A common pattern for assigning a field is:
+ // icon_view_ = AddChildView(icon_view);
+ // The problem is ViewHierarchyChanged() is called during AddChildView() and
+ // before `icon_view_` is set. This means NonIconChildViewsCount() may return
+ // the wrong thing. In this case
+ // `update_selection_based_state_in_view_herarchy_changed_` is set to false
+ // and SetIconView() explicitly calls UpdateSelectionBasedStateIfChanged().
+ bool update_selection_based_state_in_view_herarchy_changed_ = true;
};
} // namespace views
diff --git a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc
index e963380320d..419ff6d08d1 100644
--- a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc
@@ -541,6 +541,24 @@ TEST_F(MenuItemViewPaintUnitTest,
EXPECT_FALSE(submenu_child->last_paint_as_selected_for_testing());
}
+TEST_F(MenuItemViewPaintUnitTest, SelectionBasedStateUpdatedWhenIconChanges) {
+ MenuItemView* child_menu_item =
+ menu_item_view()->AppendMenuItem(1, u"menu item");
+
+ menu_runner()->RunMenuAt(widget(), nullptr, gfx::Rect(),
+ MenuAnchorPosition::kTopLeft,
+ ui::MENU_SOURCE_KEYBOARD);
+
+ EXPECT_FALSE(child_menu_item->last_paint_as_selected_for_testing());
+ child_menu_item->SetSelected(true);
+ EXPECT_TRUE(child_menu_item->IsSelected());
+ EXPECT_TRUE(child_menu_item->last_paint_as_selected_for_testing());
+
+ child_menu_item->SetIconView(std::make_unique<ImageView>());
+ EXPECT_TRUE(child_menu_item->IsSelected());
+ EXPECT_TRUE(child_menu_item->last_paint_as_selected_for_testing());
+}
+
// Sets up a custom MenuDelegate that expects functions aren't called. See
// DontAskForFontsWhenAddingSubmenu.
class MenuItemViewAccessTest : public MenuItemViewPaintUnitTest {
diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.cc b/chromium/ui/views/controls/menu/menu_model_adapter.cc
index fb5d5e6a79a..285e4a829ed 100644
--- a/chromium/ui/views/controls/menu/menu_model_adapter.cc
+++ b/chromium/ui/views/controls/menu/menu_model_adapter.cc
@@ -68,9 +68,9 @@ MenuItemView* MenuModelAdapter::CreateMenu() {
// Static.
MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model,
- int model_index,
+ size_t model_index,
MenuItemView* menu,
- int menu_index,
+ size_t menu_index,
int item_id) {
absl::optional<MenuItemView::Type> type;
ui::MenuModel::ItemType menu_type = model->GetTypeAt(model_index);
@@ -133,19 +133,17 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model,
// Static.
MenuItemView* MenuModelAdapter::AppendMenuItemFromModel(ui::MenuModel* model,
- int model_index,
+ size_t model_index,
MenuItemView* menu,
int item_id) {
- const int menu_index =
- menu->HasSubmenu()
- ? static_cast<int>(menu->GetSubmenu()->children().size())
- : 0;
+ const size_t menu_index =
+ menu->HasSubmenu() ? menu->GetSubmenu()->children().size() : size_t{0};
return AddMenuItemFromModelAt(model, model_index, menu, menu_index, item_id);
}
MenuItemView* MenuModelAdapter::AppendMenuItem(MenuItemView* menu,
ui::MenuModel* model,
- int index) {
+ size_t index) {
return AppendMenuItemFromModel(model, index, menu,
model->GetCommandIdAt(index));
}
@@ -154,7 +152,7 @@ MenuItemView* MenuModelAdapter::AppendMenuItem(MenuItemView* menu,
void MenuModelAdapter::ExecuteCommand(int id) {
ui::MenuModel* model = menu_model_;
- int index = 0;
+ size_t index = 0;
if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) {
model->ActivatedAt(index);
return;
@@ -165,7 +163,7 @@ void MenuModelAdapter::ExecuteCommand(int id) {
void MenuModelAdapter::ExecuteCommand(int id, int mouse_event_flags) {
ui::MenuModel* model = menu_model_;
- int index = 0;
+ size_t index = 0;
if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) {
model->ActivatedAt(index, mouse_event_flags);
return;
@@ -184,7 +182,7 @@ bool MenuModelAdapter::IsTriggerableEvent(MenuItemView* source,
bool MenuModelAdapter::GetAccelerator(int id,
ui::Accelerator* accelerator) const {
ui::MenuModel* model = menu_model_;
- int index = 0;
+ size_t index = 0;
if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
return model->GetAcceleratorAt(index, accelerator);
@@ -194,7 +192,7 @@ bool MenuModelAdapter::GetAccelerator(int id,
std::u16string MenuModelAdapter::GetLabel(int id) const {
ui::MenuModel* model = menu_model_;
- int index = 0;
+ size_t index = 0;
if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
return model->GetLabelAt(index);
@@ -204,7 +202,7 @@ std::u16string MenuModelAdapter::GetLabel(int id) const {
const gfx::FontList* MenuModelAdapter::GetLabelFontList(int id) const {
ui::MenuModel* model = menu_model_;
- int index = 0;
+ size_t index = 0;
if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) {
const gfx::FontList* font_list = model->GetLabelFontListAt(index);
if (font_list)
@@ -217,7 +215,7 @@ const gfx::FontList* MenuModelAdapter::GetLabelFontList(int id) const {
bool MenuModelAdapter::IsCommandEnabled(int id) const {
ui::MenuModel* model = menu_model_;
- int index = 0;
+ size_t index = 0;
if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
return model->IsEnabledAt(index);
@@ -227,7 +225,7 @@ bool MenuModelAdapter::IsCommandEnabled(int id) const {
bool MenuModelAdapter::IsCommandVisible(int id) const {
ui::MenuModel* model = menu_model_;
- int index = 0;
+ size_t index = 0;
if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
return model->IsVisibleAt(index);
@@ -237,7 +235,7 @@ bool MenuModelAdapter::IsCommandVisible(int id) const {
bool MenuModelAdapter::IsItemChecked(int id) const {
ui::MenuModel* model = menu_model_;
- int index = 0;
+ size_t index = 0;
if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index))
return model->IsItemCheckedAt(index);
@@ -290,8 +288,8 @@ void MenuModelAdapter::BuildMenuImpl(MenuItemView* menu, ui::MenuModel* model) {
DCHECK(menu);
DCHECK(model);
bool has_icons = model->HasIcons();
- const int item_count = model->GetItemCount();
- for (int i = 0; i < item_count; ++i) {
+ const size_t item_count = model->GetItemCount();
+ for (size_t i = 0; i < item_count; ++i) {
MenuItemView* item = AppendMenuItem(menu, model, i);
if (item) {
// Enabled state should be ignored for titles as they are non-interactive.
diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.h b/chromium/ui/views/controls/menu/menu_model_adapter.h
index b7c7474fb59..ebbc36a305a 100644
--- a/chromium/ui/views/controls/menu/menu_model_adapter.h
+++ b/chromium/ui/views/controls/menu/menu_model_adapter.h
@@ -53,15 +53,15 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate,
// Creates a menu item for the specified entry in the model and adds it as
// a child to |menu| at the specified |menu_index|.
static MenuItemView* AddMenuItemFromModelAt(ui::MenuModel* model,
- int model_index,
+ size_t model_index,
MenuItemView* menu,
- int menu_index,
+ size_t menu_index,
int item_id);
// Creates a menu item for the specified entry in the model and appends it as
// a child to |menu|.
static MenuItemView* AppendMenuItemFromModel(ui::MenuModel* model,
- int model_index,
+ size_t model_index,
MenuItemView* menu,
int item_id);
@@ -76,7 +76,7 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate,
// menu.
virtual MenuItemView* AppendMenuItem(MenuItemView* menu,
ui::MenuModel* model,
- int index);
+ size_t index);
// views::MenuDelegate implementation.
void ExecuteCommand(int id) override;
diff --git a/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc b/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc
index e193f357398..3b2dc294c55 100644
--- a/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc
+++ b/chromium/ui/views/controls/menu/menu_model_adapter_unittest.cc
@@ -27,7 +27,7 @@ constexpr int kActionableSubmenuIdBase = 300;
class MenuModelBase : public ui::MenuModel {
public:
explicit MenuModelBase(int command_id_base)
- : command_id_base_(command_id_base), last_activation_(-1) {}
+ : command_id_base_(command_id_base) {}
MenuModelBase(const MenuModelBase&) = delete;
MenuModelBase& operator=(const MenuModelBase&) = delete;
@@ -38,62 +38,70 @@ class MenuModelBase : public ui::MenuModel {
bool HasIcons() const override { return false; }
- int GetItemCount() const override { return static_cast<int>(items_.size()); }
+ size_t GetItemCount() const override { return items_.size(); }
- ItemType GetTypeAt(int index) const override { return items_[index].type; }
+ ItemType GetTypeAt(size_t index) const override { return items_[index].type; }
- ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override {
+ ui::MenuSeparatorType GetSeparatorTypeAt(size_t index) const override {
return ui::NORMAL_SEPARATOR;
}
- int GetCommandIdAt(int index) const override {
- return index + command_id_base_;
+ int GetCommandIdAt(size_t index) const override {
+ return static_cast<int>(index) + command_id_base_;
}
- std::u16string GetLabelAt(int index) const override {
+ std::u16string GetLabelAt(size_t index) const override {
return items_[index].label;
}
- bool IsItemDynamicAt(int index) const override { return false; }
+ bool IsItemDynamicAt(size_t index) const override { return false; }
- const gfx::FontList* GetLabelFontListAt(int index) const override {
+ const gfx::FontList* GetLabelFontListAt(size_t index) const override {
return nullptr;
}
- bool GetAcceleratorAt(int index,
+ bool GetAcceleratorAt(size_t index,
ui::Accelerator* accelerator) const override {
return false;
}
- bool IsItemCheckedAt(int index) const override { return false; }
+ bool IsItemCheckedAt(size_t index) const override { return false; }
- int GetGroupIdAt(int index) const override { return 0; }
+ int GetGroupIdAt(size_t index) const override { return 0; }
- ui::ImageModel GetIconAt(int index) const override {
+ ui::ImageModel GetIconAt(size_t index) const override {
return ui::ImageModel();
}
- ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override {
+ ui::ButtonMenuItemModel* GetButtonMenuItemAt(size_t index) const override {
return nullptr;
}
- bool IsEnabledAt(int index) const override { return items_[index].enabled; }
+ bool IsEnabledAt(size_t index) const override {
+ return items_[index].enabled;
+ }
- bool IsVisibleAt(int index) const override { return items_[index].visible; }
+ bool IsVisibleAt(size_t index) const override {
+ return items_[index].visible;
+ }
- bool IsAlertedAt(int index) const override { return items_[index].alerted; }
+ bool IsAlertedAt(size_t index) const override {
+ return items_[index].alerted;
+ }
- bool IsNewFeatureAt(int index) const override {
+ bool IsNewFeatureAt(size_t index) const override {
return items_[index].new_feature;
}
- MenuModel* GetSubmenuModelAt(int index) const override {
+ MenuModel* GetSubmenuModelAt(size_t index) const override {
return items_[index].submenu;
}
- void ActivatedAt(int index) override { set_last_activation(index); }
+ void ActivatedAt(size_t index) override { set_last_activation(index); }
- void ActivatedAt(int index, int event_flags) override { ActivatedAt(index); }
+ void ActivatedAt(size_t index, int event_flags) override {
+ ActivatedAt(index);
+ }
void MenuWillShow() override {}
@@ -133,8 +141,8 @@ class MenuModelBase : public ui::MenuModel {
const Item& GetItemDefinition(size_t index) { return items_[index]; }
// Access index argument to ActivatedAt().
- int last_activation() const { return last_activation_; }
- void set_last_activation(int last_activation) {
+ absl::optional<size_t> last_activation() const { return last_activation_; }
+ void set_last_activation(absl::optional<size_t> last_activation) {
last_activation_ = last_activation;
}
@@ -143,7 +151,7 @@ class MenuModelBase : public ui::MenuModel {
private:
int command_id_base_;
- int last_activation_;
+ absl::optional<size_t> last_activation_;
};
class SubmenuModel : public MenuModelBase {
@@ -204,7 +212,7 @@ void CheckSubmenu(const RootModel& model,
views::MenuModelAdapter* delegate,
int submenu_id,
size_t expected_children,
- int submenu_model_index,
+ size_t submenu_model_index,
int id) {
views::MenuItemView* submenu = menu->GetMenuItemByID(submenu_id);
views::SubmenuView* subitem_container = submenu->GetSubmenu();
@@ -222,7 +230,7 @@ void CheckSubmenu(const RootModel& model,
continue;
}
// Check placement.
- EXPECT_EQ(i, static_cast<size_t>(submenu->GetSubmenu()->GetIndexOf(item)));
+ EXPECT_EQ(i, submenu->GetSubmenu()->GetIndexOf(item));
// Check type.
switch (model_item.type) {
@@ -267,8 +275,8 @@ void CheckSubmenu(const RootModel& model,
// Check activation.
static_cast<views::MenuDelegate*>(delegate)->ExecuteCommand(id);
- EXPECT_EQ(i, static_cast<size_t>(submodel->last_activation()));
- submodel->set_last_activation(-1);
+ EXPECT_EQ(i, submodel->last_activation());
+ submodel->set_last_activation(absl::nullopt);
}
}
@@ -306,7 +314,7 @@ TEST_F(MenuModelAdapterTest, BasicTest) {
}
// Check placement.
- EXPECT_EQ(i, static_cast<size_t>(menu->GetSubmenu()->GetIndexOf(item)));
+ EXPECT_EQ(i, menu->GetSubmenu()->GetIndexOf(item));
// Check type.
switch (model_item.type) {
@@ -351,8 +359,8 @@ TEST_F(MenuModelAdapterTest, BasicTest) {
// Check activation.
static_cast<views::MenuDelegate*>(&delegate)->ExecuteCommand(id);
- EXPECT_EQ(i, static_cast<size_t>(model.last_activation()));
- model.set_last_activation(-1);
+ EXPECT_EQ(i, model.last_activation());
+ model.set_last_activation(absl::nullopt);
}
// Check the submenu.
diff --git a/chromium/ui/views/controls/menu/menu_pre_target_handler_mac.h b/chromium/ui/views/controls/menu/menu_pre_target_handler_mac.h
index a4eedef51d7..bd63b29c5f7 100644
--- a/chromium/ui/views/controls/menu/menu_pre_target_handler_mac.h
+++ b/chromium/ui/views/controls/menu/menu_pre_target_handler_mac.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/memory/raw_ptr.h"
#include "ui/views/cocoa/native_widget_mac_ns_window_host.h"
#include "ui/views/controls/menu/menu_pre_target_handler.h"
@@ -34,7 +35,7 @@ class MenuPreTargetHandlerMac : public MenuPreTargetHandler,
bool* was_handled) final;
std::unique_ptr<NativeWidgetMacEventMonitor> monitor_;
- MenuController* const controller_; // Weak. Owns |this|.
+ const raw_ptr<MenuController> controller_; // Weak. Owns |this|.
};
} // namespace views
diff --git a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm
index 06022dd6404..95b0d17154a 100644
--- a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm
+++ b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/raw_ptr.h"
+
#import "ui/views/controls/menu/menu_runner_impl_cocoa.h"
#import <Cocoa/Cocoa.h>
@@ -69,7 +71,7 @@ class TestModel : public ui::SimpleMenuModel {
}
private:
- TestModel* model_;
+ raw_ptr<TestModel> model_;
};
private:
@@ -130,7 +132,7 @@ class MenuRunnerCocoaTest : public ViewsTestBase,
if (runner_) {
runner_->Release();
- runner_ = NULL;
+ runner_ = nullptr;
}
parent_->CloseNow();
@@ -247,8 +249,8 @@ class MenuRunnerCocoaTest : public ViewsTestBase,
protected:
std::unique_ptr<TestModel> menu_;
- internal::MenuRunnerImplInterface* runner_ = nullptr;
- views::Widget* parent_ = nullptr;
+ raw_ptr<internal::MenuRunnerImplInterface> runner_ = nullptr;
+ raw_ptr<views::Widget> parent_ = nullptr;
NSRect last_anchor_frame_ = NSZeroRect;
NSUInteger native_view_subview_count_ = 0;
int menu_close_count_ = 0;
@@ -384,7 +386,7 @@ TEST_P(MenuRunnerCocoaTest, CancelWithoutRunning) {
TEST_P(MenuRunnerCocoaTest, DeleteWithoutRunning) {
runner_->Release();
- runner_ = NULL;
+ runner_ = nullptr;
EXPECT_EQ(0, menu_close_count_);
}
diff --git a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
index 6f92eb0c4f6..a6dec3b24f5 100644
--- a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
+++ b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm
@@ -9,6 +9,7 @@
#include "base/i18n/rtl.h"
#include "base/mac/mac_util.h"
#import "base/message_loop/message_pump_mac.h"
+#include "base/numerics/safe_conversions.h"
#import "skia/ext/skia_utils_mac.h"
#import "ui/base/cocoa/cocoa_base_utils.h"
#import "ui/base/cocoa/menu_controller.h"
@@ -212,7 +213,7 @@ NSImage* IPHDotImage(const ui::ColorProvider* color_provider) {
- (void)controllerWillAddItem:(NSMenuItem*)menuItem
fromModel:(ui::MenuModel*)model
- atIndex:(NSInteger)index
+ atIndex:(size_t)index
withColorProvider:(const ui::ColorProvider*)colorProvider {
if (model->IsNewFeatureAt(index)) {
NSMutableAttributedString* attrTitle = [[[NSMutableAttributedString alloc]
@@ -242,12 +243,12 @@ NSImage* IPHDotImage(const ui::ColorProvider* color_provider) {
}
- (void)controllerWillAddMenu:(NSMenu*)menu fromModel:(ui::MenuModel*)model {
- int alerted_index = -1;
+ absl::optional<size_t> alerted_index;
IdentifierContainer* const element_ids =
[[[IdentifierContainer alloc] init] autorelease];
- for (int i = 0; i < model->GetItemCount(); ++i) {
+ for (size_t i = 0; i < model->GetItemCount(); ++i) {
if (model->IsAlertedAt(i)) {
- DCHECK_LT(alerted_index, 0);
+ DCHECK(!alerted_index.has_value());
alerted_index = i;
}
const ui::ElementIdentifier identifier = model->GetElementIdentifierAt(i);
@@ -255,14 +256,16 @@ NSImage* IPHDotImage(const ui::ColorProvider* color_provider) {
[element_ids ids].push_back(identifier);
}
- if (alerted_index >= 0 || ![element_ids ids].empty()) {
+ if (alerted_index.has_value() || ![element_ids ids].empty()) {
auto shown_callback = ^(NSNotification* note) {
NSMenu* const menu_obj = note.object;
- if (alerted_index >= 0) {
+ if (alerted_index.has_value()) {
if ([menu respondsToSelector:@selector(_menuImpl)]) {
NSCarbonMenuImpl* menuImpl = [menu_obj _menuImpl];
if ([menuImpl respondsToSelector:@selector(highlightItemAtIndex:)]) {
- [menuImpl highlightItemAtIndex:alerted_index];
+ const auto index =
+ base::checked_cast<NSInteger>(alerted_index.value());
+ [menuImpl highlightItemAtIndex:index];
}
}
}
@@ -366,8 +369,10 @@ namespace {
// Returns the first item in |menu_controller|'s menu that will be checked.
NSMenuItem* FirstCheckedItem(MenuControllerCocoa* menu_controller) {
for (NSMenuItem* item in [[menu_controller menu] itemArray]) {
- if ([menu_controller model]->IsItemCheckedAt([item tag]))
+ if ([menu_controller model]->IsItemCheckedAt(
+ base::checked_cast<size_t>([item tag]))) {
return item;
+ }
}
return nil;
}
diff --git a/chromium/ui/views/controls/menu/native_menu_win.cc b/chromium/ui/views/controls/menu/native_menu_win.cc
index 3a62f75fdbf..3a5fdbab314 100644
--- a/chromium/ui/views/controls/menu/native_menu_win.cc
+++ b/chromium/ui/views/controls/menu/native_menu_win.cc
@@ -31,7 +31,7 @@ struct NativeMenuWin::ItemData {
raw_ptr<NativeMenuWin> native_menu_win;
// The index of the item within the menu's model.
- int model_index;
+ size_t model_index;
};
// Returns the NativeMenuWin for a particular HMENU.
@@ -68,10 +68,10 @@ void NativeMenuWin::Rebuild(MenuInsertionDelegateWin* delegate) {
items_.clear();
owner_draw_ = model_->HasIcons() || owner_draw_;
- first_item_index_ = delegate ? delegate->GetInsertionIndex(menu_) : 0;
- for (int model_index = 0; model_index < model_->GetItemCount();
+ first_item_index_ = delegate ? delegate->GetInsertionIndex(menu_) : size_t{0};
+ for (size_t model_index = 0; model_index < model_->GetItemCount();
++model_index) {
- int menu_index = model_index + first_item_index_;
+ size_t menu_index = model_index + first_item_index_;
if (model_->GetTypeAt(model_index) == ui::MenuModel::TYPE_SEPARATOR)
AddSeparatorItemAt(menu_index, model_index);
else
@@ -81,9 +81,9 @@ void NativeMenuWin::Rebuild(MenuInsertionDelegateWin* delegate) {
void NativeMenuWin::UpdateStates() {
// A depth-first walk of the menu items, updating states.
- int model_index = 0;
+ size_t model_index = 0;
for (const auto& item : items_) {
- int menu_index = model_index + first_item_index_;
+ size_t menu_index = model_index + first_item_index_;
SetMenuItemState(menu_index, model_->IsEnabledAt(model_index),
model_->IsItemCheckedAt(model_index), false);
if (model_->IsItemDynamicAt(model_index)) {
@@ -101,7 +101,7 @@ void NativeMenuWin::UpdateStates() {
////////////////////////////////////////////////////////////////////////////////
// NativeMenuWin, private:
-bool NativeMenuWin::IsSeparatorItemAt(int menu_index) const {
+bool NativeMenuWin::IsSeparatorItemAt(size_t menu_index) const {
MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE;
@@ -109,7 +109,7 @@ bool NativeMenuWin::IsSeparatorItemAt(int menu_index) const {
return !!(mii.fType & MF_SEPARATOR);
}
-void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) {
+void NativeMenuWin::AddMenuItemAt(size_t menu_index, size_t model_index) {
MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA;
@@ -131,29 +131,31 @@ void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) {
} else {
if (type == ui::MenuModel::TYPE_RADIO)
mii.fType |= MFT_RADIOCHECK;
- mii.wID = model_->GetCommandIdAt(model_index);
+ mii.wID = static_cast<UINT>(model_->GetCommandIdAt(model_index));
}
item_data->native_menu_win = this;
item_data->model_index = model_index;
mii.dwItemData = reinterpret_cast<ULONG_PTR>(item_data.get());
- items_.insert(items_.begin() + model_index, std::move(item_data));
+ items_.insert(items_.begin() + static_cast<ptrdiff_t>(model_index),
+ std::move(item_data));
UpdateMenuItemInfoForString(&mii, model_index,
model_->GetLabelAt(model_index));
InsertMenuItem(menu_, menu_index, TRUE, &mii);
}
-void NativeMenuWin::AddSeparatorItemAt(int menu_index, int model_index) {
+void NativeMenuWin::AddSeparatorItemAt(size_t menu_index, size_t model_index) {
MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE;
mii.fType = MFT_SEPARATOR;
// Insert a dummy entry into our label list so we can index directly into it
// using item indices if need be.
- items_.insert(items_.begin() + model_index, std::make_unique<ItemData>());
- InsertMenuItem(menu_, menu_index, TRUE, &mii);
+ items_.insert(items_.begin() + static_cast<ptrdiff_t>(model_index),
+ std::make_unique<ItemData>());
+ InsertMenuItem(menu_, static_cast<UINT>(menu_index), TRUE, &mii);
}
-void NativeMenuWin::SetMenuItemState(int menu_index,
+void NativeMenuWin::SetMenuItemState(size_t menu_index,
bool enabled,
bool checked,
bool is_default) {
@@ -170,11 +172,11 @@ void NativeMenuWin::SetMenuItemState(int menu_index,
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
mii.fState = state;
- SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
+ SetMenuItemInfo(menu_, static_cast<UINT>(menu_index), MF_BYPOSITION, &mii);
}
-void NativeMenuWin::SetMenuItemLabel(int menu_index,
- int model_index,
+void NativeMenuWin::SetMenuItemLabel(size_t menu_index,
+ size_t model_index,
const std::u16string& label) {
if (IsSeparatorItemAt(menu_index))
return;
@@ -182,11 +184,11 @@ void NativeMenuWin::SetMenuItemLabel(int menu_index,
MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
UpdateMenuItemInfoForString(&mii, model_index, label);
- SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
+ SetMenuItemInfo(menu_, static_cast<UINT>(menu_index), MF_BYPOSITION, &mii);
}
void NativeMenuWin::UpdateMenuItemInfoForString(MENUITEMINFO* mii,
- int model_index,
+ size_t model_index,
const std::u16string& label) {
std::u16string formatted = label;
ui::MenuModel::ItemType type = model_->GetTypeAt(model_index);
diff --git a/chromium/ui/views/controls/menu/native_menu_win.h b/chromium/ui/views/controls/menu/native_menu_win.h
index c4c8341d3ff..89f78b8f3a6 100644
--- a/chromium/ui/views/controls/menu/native_menu_win.h
+++ b/chromium/ui/views/controls/menu/native_menu_win.h
@@ -51,21 +51,21 @@ class VIEWS_EXPORT NativeMenuWin {
// code in the functions in this class.
// Returns true if the item at the specified index is a separator.
- bool IsSeparatorItemAt(int menu_index) const;
+ bool IsSeparatorItemAt(size_t menu_index) const;
// Add items. See note above about indices.
- void AddMenuItemAt(int menu_index, int model_index);
- void AddSeparatorItemAt(int menu_index, int model_index);
+ void AddMenuItemAt(size_t menu_index, size_t model_index);
+ void AddSeparatorItemAt(size_t menu_index, size_t model_index);
// Sets the state of the item at the specified index.
- void SetMenuItemState(int menu_index,
+ void SetMenuItemState(size_t menu_index,
bool enabled,
bool checked,
bool is_default);
// Sets the label of the item at the specified index.
- void SetMenuItemLabel(int menu_index,
- int model_index,
+ void SetMenuItemLabel(size_t menu_index,
+ size_t model_index,
const std::u16string& label);
// Updates the local data structure with the correctly formatted version of
@@ -73,7 +73,7 @@ class VIEWS_EXPORT NativeMenuWin {
// the menu is not owner-draw. That's a mouthful. This function exists because
// of the peculiarities of the Windows menu API.
void UpdateMenuItemInfoForString(MENUITEMINFO* mii,
- int model_index,
+ size_t model_index,
const std::u16string& label);
// Resets the native menu stored in |menu_| by destroying any old menu then
@@ -99,7 +99,7 @@ class VIEWS_EXPORT NativeMenuWin {
HWND system_menu_for_;
// The index of the first item in the model in the menu.
- int first_item_index_;
+ size_t first_item_index_;
// If we're a submenu, this is our parent.
raw_ptr<NativeMenuWin> parent_;
diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc
index 680a66049fa..ccff3c17e0a 100644
--- a/chromium/ui/views/controls/menu/submenu_view.cc
+++ b/chromium/ui/views/controls/menu/submenu_view.cc
@@ -87,10 +87,9 @@ SubmenuView::MenuItems SubmenuView::GetMenuItems() const {
return menu_items;
}
-MenuItemView* SubmenuView::GetMenuItemAt(int index) {
+MenuItemView* SubmenuView::GetMenuItemAt(size_t index) {
const MenuItems menu_items = GetMenuItems();
- DCHECK_GE(index, 0);
- DCHECK_LT(static_cast<size_t>(index), menu_items.size());
+ DCHECK_LT(index, menu_items.size());
return menu_items[index];
}
@@ -363,24 +362,26 @@ void SubmenuView::OnGestureEvent(ui::GestureEvent* event) {
event->SetHandled();
}
-int SubmenuView::GetRowCount() {
- return static_cast<int>(GetMenuItems().size());
+size_t SubmenuView::GetRowCount() {
+ return GetMenuItems().size();
}
-int SubmenuView::GetSelectedRow() {
+absl::optional<size_t> SubmenuView::GetSelectedRow() {
const auto menu_items = GetMenuItems();
const auto i =
std::find_if(menu_items.cbegin(), menu_items.cend(),
[](const MenuItemView* item) { return item->IsSelected(); });
- return (i == menu_items.cend()) ? -1 : std::distance(menu_items.cbegin(), i);
+ return (i == menu_items.cend()) ? absl::nullopt
+ : absl::make_optional(static_cast<size_t>(
+ std::distance(menu_items.cbegin(), i)));
}
-void SubmenuView::SetSelectedRow(int row) {
+void SubmenuView::SetSelectedRow(absl::optional<size_t> row) {
parent_menu_item_->GetMenuController()->SetSelection(
- GetMenuItemAt(row), MenuController::SELECTION_DEFAULT);
+ GetMenuItemAt(row.value()), MenuController::SELECTION_DEFAULT);
}
-std::u16string SubmenuView::GetTextForRow(int row) {
+std::u16string SubmenuView::GetTextForRow(size_t row) {
return MenuItemView::GetAccessibleNameForMenuItem(
GetMenuItemAt(row)->title(), std::u16string(),
GetMenuItemAt(row)->ShouldShowNewBadge());
diff --git a/chromium/ui/views/controls/menu/submenu_view.h b/chromium/ui/views/controls/menu/submenu_view.h
index cfbf5771ae8..2ee2a6ccbf2 100644
--- a/chromium/ui/views/controls/menu/submenu_view.h
+++ b/chromium/ui/views/controls/menu/submenu_view.h
@@ -72,7 +72,7 @@ class VIEWS_EXPORT SubmenuView : public View,
MenuItems GetMenuItems() const;
// Returns the MenuItemView at the specified index.
- MenuItemView* GetMenuItemAt(int index);
+ MenuItemView* GetMenuItemAt(size_t index);
PrefixSelector* GetPrefixSelector();
@@ -106,10 +106,10 @@ class VIEWS_EXPORT SubmenuView : public View,
void OnGestureEvent(ui::GestureEvent* event) override;
// Overridden from PrefixDelegate.
- int GetRowCount() override;
- int GetSelectedRow() override;
- void SetSelectedRow(int row) override;
- std::u16string GetTextForRow(int row) override;
+ size_t GetRowCount() override;
+ absl::optional<size_t> GetSelectedRow() override;
+ void SetSelectedRow(absl::optional<size_t> row) override;
+ std::u16string GetTextForRow(size_t row) override;
// Returns true if the menu is showing.
virtual bool IsShowing() const;
diff --git a/chromium/ui/views/controls/native/native_view_host_mac.h b/chromium/ui/views/controls/native/native_view_host_mac.h
index 5ec9cfc0d81..32a529b53b5 100644
--- a/chromium/ui/views/controls/native/native_view_host_mac.h
+++ b/chromium/ui/views/controls/native/native_view_host_mac.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/mac/scoped_nsobject.h"
+#include "base/memory/raw_ptr.h"
#include "ui/base/cocoa/views_hostable.h"
#include "ui/views/controls/native/native_view_host_wrapper.h"
#include "ui/views/views_export.h"
@@ -71,7 +72,7 @@ class NativeViewHostMac : public NativeViewHostWrapper,
NativeWidgetMacNSWindowHost* GetNSWindowHost() const;
// Our associated NativeViewHost. Owns this.
- NativeViewHost* host_;
+ raw_ptr<NativeViewHost> host_;
// Retain the native view as it may be destroyed at an unpredictable time.
base::scoped_nsobject<NSView> native_view_;
@@ -79,7 +80,7 @@ class NativeViewHostMac : public NativeViewHostWrapper,
// If |native_view| supports the ViewsHostable protocol, then this is the
// the corresponding ViewsHostableView interface (which is implemeted only
// by WebContents and tests).
- ui::ViewsHostableView* native_view_hostable_ = nullptr;
+ raw_ptr<ui::ViewsHostableView> native_view_hostable_ = nullptr;
};
} // namespace views
diff --git a/chromium/ui/views/controls/prefix_delegate.h b/chromium/ui/views/controls/prefix_delegate.h
index dc37a068b7c..5995386d410 100644
--- a/chromium/ui/views/controls/prefix_delegate.h
+++ b/chromium/ui/views/controls/prefix_delegate.h
@@ -5,6 +5,7 @@
#ifndef UI_VIEWS_CONTROLS_PREFIX_DELEGATE_H_
#define UI_VIEWS_CONTROLS_PREFIX_DELEGATE_H_
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/views/view.h"
#include "ui/views/views_export.h"
@@ -14,17 +15,17 @@ namespace views {
class VIEWS_EXPORT PrefixDelegate {
public:
// Returns the total number of selectable items.
- virtual int GetRowCount() = 0;
+ virtual size_t GetRowCount() = 0;
// Returns the row of the currently selected item, or -1 if no item is
// selected.
- virtual int GetSelectedRow() = 0;
+ virtual absl::optional<size_t> GetSelectedRow() = 0;
// Sets the selection to the specified row.
- virtual void SetSelectedRow(int row) = 0;
+ virtual void SetSelectedRow(absl::optional<size_t> row) = 0;
// Returns the item at the specified row.
- virtual std::u16string GetTextForRow(int row) = 0;
+ virtual std::u16string GetTextForRow(size_t row) = 0;
};
} // namespace views
diff --git a/chromium/ui/views/controls/prefix_selector.cc b/chromium/ui/views/controls/prefix_selector.cc
index 67c49f30b95..a65c9e24918 100644
--- a/chromium/ui/views/controls/prefix_selector.cc
+++ b/chromium/ui/views/controls/prefix_selector.cc
@@ -5,6 +5,7 @@
#include "ui/views/controls/prefix_selector.h"
#include <algorithm>
+#include <limits>
#include "base/i18n/case_conversion.h"
#include "base/time/default_tick_clock.h"
@@ -43,8 +44,8 @@ bool PrefixSelector::ShouldContinueSelection() const {
void PrefixSelector::SetCompositionText(
const ui::CompositionText& composition) {}
-uint32_t PrefixSelector::ConfirmCompositionText(bool keep_selection) {
- return UINT32_MAX;
+size_t PrefixSelector::ConfirmCompositionText(bool keep_selection) {
+ return std::numeric_limits<size_t>::max();
}
void PrefixSelector::ClearCompositionText() {}
@@ -92,7 +93,7 @@ gfx::Rect PrefixSelector::GetSelectionBoundingBox() const {
return gfx::Rect();
}
-bool PrefixSelector::GetCompositionCharacterBounds(uint32_t index,
+bool PrefixSelector::GetCompositionCharacterBounds(size_t index,
gfx::Rect* rect) const {
// TextInputClient::GetCompositionCharacterBounds is expected to fill |rect|
// in screen coordinates and GetCaretBounds returns screen coordinates.
@@ -129,9 +130,11 @@ bool PrefixSelector::SetEditableSelectionRange(const gfx::Range& range) {
return false;
}
+#if BUILDFLAG(IS_MAC)
bool PrefixSelector::DeleteRange(const gfx::Range& range) {
return false;
}
+#endif
bool PrefixSelector::GetTextFromRange(const gfx::Range& range,
std::u16string* text) const {
@@ -220,7 +223,7 @@ void PrefixSelector::OnTextInput(const std::u16string& text) {
(text[0] == L'\t' || text[0] == L'\r' || text[0] == L'\n'))
return;
- const int row_count = prefix_delegate_->GetRowCount();
+ const size_t row_count = prefix_delegate_->GetRowCount();
if (row_count == 0)
return;
@@ -228,17 +231,17 @@ void PrefixSelector::OnTextInput(const std::u16string& text) {
// append |text| to |current_text_| and search for that. If it has been a
// while search after the current row, otherwise search starting from the
// current row.
- int row = std::max(0, prefix_delegate_->GetSelectedRow());
+ size_t row = prefix_delegate_->GetSelectedRow().value_or(0);
if (ShouldContinueSelection()) {
current_text_ += text;
} else {
current_text_ = text;
- if (prefix_delegate_->GetSelectedRow() >= 0)
+ if (prefix_delegate_->GetSelectedRow().has_value())
row = (row + 1) % row_count;
}
time_of_last_key_ = tick_clock_->NowTicks();
- const int start_row = row;
+ const size_t start_row = row;
const std::u16string lower_text(base::i18n::ToLower(current_text_));
do {
if (TextAtRowMatchesText(row, lower_text)) {
@@ -249,7 +252,7 @@ void PrefixSelector::OnTextInput(const std::u16string& text) {
} while (row != start_row);
}
-bool PrefixSelector::TextAtRowMatchesText(int row,
+bool PrefixSelector::TextAtRowMatchesText(size_t row,
const std::u16string& lower_text) {
const std::u16string model_text(
base::i18n::ToLower(prefix_delegate_->GetTextForRow(row)));
diff --git a/chromium/ui/views/controls/prefix_selector.h b/chromium/ui/views/controls/prefix_selector.h
index f132b73ea4c..ce19d4d8d1c 100644
--- a/chromium/ui/views/controls/prefix_selector.h
+++ b/chromium/ui/views/controls/prefix_selector.h
@@ -47,7 +47,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
// ui::TextInputClient:
void SetCompositionText(const ui::CompositionText& composition) override;
- uint32_t ConfirmCompositionText(bool keep_selection) override;
+ size_t ConfirmCompositionText(bool keep_selection) override;
void ClearCompositionText() override;
void InsertText(const std::u16string& text,
InsertTextCursorBehavior cursor_behavior) override;
@@ -59,7 +59,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
bool CanComposeInline() const override;
gfx::Rect GetCaretBounds() const override;
gfx::Rect GetSelectionBoundingBox() const override;
- bool GetCompositionCharacterBounds(uint32_t index,
+ bool GetCompositionCharacterBounds(size_t index,
gfx::Rect* rect) const override;
bool HasCompositionText() const override;
FocusReason GetFocusReason() const override;
@@ -67,7 +67,9 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
bool GetCompositionTextRange(gfx::Range* range) const override;
bool GetEditableSelectionRange(gfx::Range* range) const override;
bool SetEditableSelectionRange(const gfx::Range& range) override;
+#if BUILDFLAG(IS_MAC)
bool DeleteRange(const gfx::Range& range) override;
+#endif
bool GetTextFromRange(const gfx::Range& range,
std::u16string* text) const override;
void OnInputMethodChanged() override;
@@ -115,7 +117,7 @@ class VIEWS_EXPORT PrefixSelector : public ui::TextInputClient {
void OnTextInput(const std::u16string& text);
// Returns true if the text of the node at |row| starts with |lower_text|.
- bool TextAtRowMatchesText(int row, const std::u16string& lower_text);
+ bool TextAtRowMatchesText(size_t row, const std::u16string& lower_text);
// Clears |current_text_| and resets |time_of_last_key_|.
void ClearText();
diff --git a/chromium/ui/views/controls/prefix_selector_unittest.cc b/chromium/ui/views/controls/prefix_selector_unittest.cc
index 1bccca752b8..caa514bf5a1 100644
--- a/chromium/ui/views/controls/prefix_selector_unittest.cc
+++ b/chromium/ui/views/controls/prefix_selector_unittest.cc
@@ -30,17 +30,19 @@ class TestPrefixDelegate : public View, public PrefixDelegate {
~TestPrefixDelegate() override = default;
- int GetRowCount() override { return static_cast<int>(rows_.size()); }
+ size_t GetRowCount() override { return rows_.size(); }
- int GetSelectedRow() override { return selected_row_; }
+ absl::optional<size_t> GetSelectedRow() override { return selected_row_; }
- void SetSelectedRow(int row) override { selected_row_ = row; }
+ void SetSelectedRow(absl::optional<size_t> row) override {
+ selected_row_ = row;
+ }
- std::u16string GetTextForRow(int row) override { return rows_[row]; }
+ std::u16string GetTextForRow(size_t row) override { return rows_[row]; }
private:
std::vector<std::u16string> rows_;
- int selected_row_ = 0;
+ absl::optional<size_t> selected_row_ = 0;
};
class PrefixSelectorTest : public ViewsTestBase {
@@ -67,20 +69,20 @@ TEST_F(PrefixSelectorTest, PrefixSelect) {
selector_->InsertText(
u"an",
ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
- EXPECT_EQ(1, delegate_.GetSelectedRow());
+ EXPECT_EQ(1u, delegate_.GetSelectedRow());
// Invoke OnViewBlur() to reset time.
selector_->OnViewBlur();
selector_->InsertText(
u"a",
ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
- EXPECT_EQ(0, delegate_.GetSelectedRow());
+ EXPECT_EQ(0u, delegate_.GetSelectedRow());
selector_->OnViewBlur();
selector_->InsertText(
u"g",
ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
- EXPECT_EQ(3, delegate_.GetSelectedRow());
+ EXPECT_EQ(3u, delegate_.GetSelectedRow());
selector_->OnViewBlur();
selector_->InsertText(
@@ -89,7 +91,7 @@ TEST_F(PrefixSelectorTest, PrefixSelect) {
selector_->InsertText(
u"a",
ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
- EXPECT_EQ(2, delegate_.GetSelectedRow());
+ EXPECT_EQ(2u, delegate_.GetSelectedRow());
selector_->OnViewBlur();
selector_->InsertText(
@@ -101,7 +103,7 @@ TEST_F(PrefixSelectorTest, PrefixSelect) {
selector_->InsertText(
u"a",
ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
- EXPECT_EQ(2, delegate_.GetSelectedRow());
+ EXPECT_EQ(2u, delegate_.GetSelectedRow());
}
} // namespace views
diff --git a/chromium/ui/views/controls/progress_bar.cc b/chromium/ui/views/controls/progress_bar.cc
index ff59dd87df6..56c9fd01a74 100644
--- a/chromium/ui/views/controls/progress_bar.cc
+++ b/chromium/ui/views/controls/progress_bar.cc
@@ -147,11 +147,20 @@ void ProgressBar::SetValue(double value) {
MaybeNotifyAccessibilityValueChanged();
}
+void ProgressBar::SetPaused(bool is_paused) {
+ if (is_paused_ == is_paused)
+ return;
+
+ is_paused_ = is_paused;
+ OnPropertyChanged(&is_paused_, kPropertyEffectsPaint);
+}
+
SkColor ProgressBar::GetForegroundColor() const {
if (foreground_color_)
return foreground_color_.value();
- return GetColorProvider()->GetColor(ui::kColorProgressBar);
+ return GetColorProvider()->GetColor(GetPaused() ? ui::kColorProgressBarPaused
+ : ui::kColorProgressBar);
}
void ProgressBar::SetForegroundColor(SkColor color) {
@@ -266,6 +275,7 @@ void ProgressBar::MaybeNotifyAccessibilityValueChanged() {
BEGIN_METADATA(ProgressBar, View)
ADD_PROPERTY_METADATA(SkColor, ForegroundColor, ui::metadata::SkColorConverter)
ADD_PROPERTY_METADATA(SkColor, BackgroundColor, ui::metadata::SkColorConverter)
+ADD_PROPERTY_METADATA(bool, Paused)
END_METADATA
} // namespace views
diff --git a/chromium/ui/views/controls/progress_bar.h b/chromium/ui/views/controls/progress_bar.h
index 05b5b708858..2c95b733bf7 100644
--- a/chromium/ui/views/controls/progress_bar.h
+++ b/chromium/ui/views/controls/progress_bar.h
@@ -44,6 +44,9 @@ class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate {
// be displayed with an infinite loading animation.
void SetValue(double value);
+ // Sets whether the progress bar is paused.
+ void SetPaused(bool is_paused);
+
// The color of the progress portion.
SkColor GetForegroundColor() const;
void SetForegroundColor(SkColor color);
@@ -61,6 +64,7 @@ class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate {
void AnimationEnded(const gfx::Animation* animation) override;
bool IsIndeterminate();
+ bool GetPaused() const { return is_paused_; }
void OnPaintIndeterminate(gfx::Canvas* canvas);
// Fire an accessibility event if visible and the progress has changed.
@@ -69,6 +73,9 @@ class VIEWS_EXPORT ProgressBar : public View, public gfx::AnimationDelegate {
// Current progress to display, should be in the range 0.0 to 1.0.
double current_value_ = 0.0;
+ // Is the progress bar paused.
+ bool is_paused_ = false;
+
// In DP, the preferred height of this progress bar.
const int preferred_height_;
diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc
index e594c891e0a..e9fc96a1262 100644
--- a/chromium/ui/views/controls/scroll_view.cc
+++ b/chromium/ui/views/controls/scroll_view.cc
@@ -904,6 +904,7 @@ void ScrollView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
if (!contents_)
return;
+ node_data->role = ax::mojom::Role::kScrollView;
node_data->AddIntAttribute(ax::mojom::IntAttribute::kScrollX,
CurrentOffset().x());
node_data->AddIntAttribute(ax::mojom::IntAttribute::kScrollXMin,
diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc
index 47334b0d0c1..dd0d3386efb 100644
--- a/chromium/ui/views/controls/scroll_view_unittest.cc
+++ b/chromium/ui/views/controls/scroll_view_unittest.cc
@@ -113,7 +113,9 @@ class ObserveViewDeletion : public ViewObserver {
private:
base::ScopedObservation<View, ViewObserver> observer_{this};
- raw_ptr<View> deleted_view_ = nullptr;
+ // TODO(crbug.com/1298696): views_unittests breaks with MTECheckedPtr
+ // enabled. Triage.
+ raw_ptr<View, DegradeToNoOpWhenMTE> deleted_view_ = nullptr;
};
} // namespace test
@@ -343,7 +345,7 @@ class WidgetScrollViewTest : public test::WidgetTest,
ScrollView* scroll_view =
widget_->SetContentsView(std::make_unique<ScrollView>());
scroll_view->SetContents(std::move(contents));
- scroll_view->Layout();
+ RunScheduledLayout(scroll_view);
widget_->GetCompositor()->AddObserver(this);
@@ -486,7 +488,7 @@ std::string UiConfigToString(const testing::TestParamInfo<UiConfig>& info) {
// Verifies the viewport is sized to fit the available space.
TEST_F(ScrollViewTest, ViewportSizedToFit) {
View* contents = InstallContents();
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString());
}
@@ -496,7 +498,7 @@ TEST_F(ScrollViewTest, BoundedViewportSizedToFit) {
View* contents = InstallContents();
scroll_view_->ClipHeightTo(100, 200);
scroll_view_->SetBorder(CreateSolidBorder(2, 0));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ("2,2 96x96", contents->parent()->bounds().ToString());
// Make sure the width of |contents| is set properly not to overflow the
@@ -510,7 +512,7 @@ TEST_F(ScrollViewTest, VerticalScrollbarDoesNotAppearUnnecessarily) {
const gfx::Rect default_outer_bounds(0, 0, 100, 100);
scroll_view_->SetContents(std::make_unique<VerticalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
@@ -522,7 +524,7 @@ TEST_F(ScrollViewTest, HorizontalScrollbarDoesNotAppearIfHidden) {
ScrollView::ScrollBarMode::kHiddenButEnabled);
scroll_view_->SetContents(std::make_unique<VerticalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_FALSE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
@@ -534,7 +536,7 @@ TEST_F(ScrollViewTest, VerticalScrollbarDoesNotAppearIfHidden) {
ScrollView::ScrollBarMode::kHiddenButEnabled);
scroll_view_->SetContents(std::make_unique<HorizontalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_FALSE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
@@ -546,7 +548,7 @@ TEST_F(ScrollViewTest, HorizontalScrollbarDoesNotAppearIfDisabled) {
ScrollView::ScrollBarMode::kDisabled);
scroll_view_->SetContents(std::make_unique<VerticalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_FALSE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
@@ -557,7 +559,7 @@ TEST_F(ScrollViewTest, VerticallScrollbarDoesNotAppearIfDisabled) {
scroll_view_->SetVerticalScrollBarMode(ScrollView::ScrollBarMode::kDisabled);
scroll_view_->SetContents(std::make_unique<HorizontalResizingView>());
scroll_view_->SetBoundsRect(default_outer_bounds);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->GetVisible());
EXPECT_FALSE(scroll_view_->horizontal_scroll_bar()->GetVisible());
}
@@ -569,7 +571,7 @@ TEST_F(ScrollViewTest, ScrollBars) {
// Size the contents such that vertical scrollbar is needed.
contents->SetBounds(0, 0, 50, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
contents->parent()->width());
EXPECT_EQ(100, contents->parent()->height());
@@ -582,7 +584,7 @@ TEST_F(ScrollViewTest, ScrollBars) {
// Size the contents such that horizontal scrollbar is needed.
contents->SetBounds(0, 0, 400, 50);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100, contents->parent()->width());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(),
contents->parent()->height());
@@ -591,7 +593,7 @@ TEST_F(ScrollViewTest, ScrollBars) {
// Both horizontal and vertical.
contents->SetBounds(0, 0, 300, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
contents->parent()->width());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(),
@@ -607,7 +609,7 @@ TEST_F(ScrollViewTest, ScrollBars) {
scroll_view_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(
kTopPadding, kLeftPadding, kBottomPadding, kRightPadding)));
contents->SetBounds(0, 0, 50, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth() - kLeftPadding -
kRightPadding,
contents->parent()->width());
@@ -624,7 +626,7 @@ TEST_F(ScrollViewTest, ScrollBars) {
// Horizontal with border.
contents->SetBounds(0, 0, 400, 50);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100 - kLeftPadding - kRightPadding, contents->parent()->width());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - kTopPadding -
kBottomPadding,
@@ -641,7 +643,7 @@ TEST_F(ScrollViewTest, ScrollBars) {
// Both horizontal and vertical with border.
contents->SetBounds(0, 0, 300, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth() - kLeftPadding -
kRightPadding,
contents->parent()->width());
@@ -722,7 +724,7 @@ TEST_F(ScrollViewTest, ScrollBarsWithHeader) {
// Size the contents such that vertical scrollbar is needed.
contents->SetBounds(0, 0, 50, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(0, contents->parent()->x());
EXPECT_EQ(20, contents->parent()->y());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
@@ -748,7 +750,7 @@ TEST_F(ScrollViewTest, ScrollBarsWithHeader) {
// Size the contents such that horizontal scrollbar is needed.
contents->SetBounds(0, 0, 400, 50);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(0, contents->parent()->x());
EXPECT_EQ(20, contents->parent()->y());
EXPECT_EQ(100, contents->parent()->width());
@@ -765,7 +767,7 @@ TEST_F(ScrollViewTest, ScrollBarsWithHeader) {
// Both horizontal and vertical.
contents->SetBounds(0, 0, 300, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(0, contents->parent()->x());
EXPECT_EQ(20, contents->parent()->y());
EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(),
@@ -819,7 +821,7 @@ TEST_F(ScrollViewTest, ScrollToPositionUpdatesScrollBar) {
// Scroll the horizontal scrollbar, after which, the scroll bar thumb position
// should be updated (i.e. it should be non-zero).
contents->SetBounds(0, 0, 400, 50);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
auto* scroll_bar = test_api.GetScrollBar(HORIZONTAL);
ASSERT_TRUE(scroll_bar);
EXPECT_TRUE(scroll_bar->GetVisible());
@@ -829,7 +831,7 @@ TEST_F(ScrollViewTest, ScrollToPositionUpdatesScrollBar) {
// Scroll the vertical scrollbar.
contents->SetBounds(0, 0, 50, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
scroll_bar = test_api.GetScrollBar(VERTICAL);
ASSERT_TRUE(scroll_bar);
EXPECT_TRUE(scroll_bar->GetVisible());
@@ -847,7 +849,7 @@ TEST_F(ScrollViewTest, ScrollToPositionUpdatesWithHiddenHorizontalScrollBar) {
View* contents = InstallContents();
contents->SetBounds(0, 0, 400, 50);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
auto* scroll_bar = test_api.GetScrollBar(HORIZONTAL);
ASSERT_TRUE(scroll_bar);
EXPECT_FALSE(scroll_bar->GetVisible());
@@ -867,7 +869,7 @@ TEST_F(ScrollViewTest, ScrollToPositionUpdatesWithHiddenVerticalScrollBar) {
View* contents = InstallContents();
contents->SetBounds(0, 0, 50, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
auto* scroll_bar = test_api.GetScrollBar(VERTICAL);
ASSERT_TRUE(scroll_bar);
EXPECT_FALSE(scroll_bar->GetVisible());
@@ -886,7 +888,7 @@ TEST_F(ScrollViewTest, ScrollRectToVisible) {
auto* contents_ptr = scroll_view_->SetContents(std::move(contents));
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(gfx::Vector2d(0, 0), test_api.IntegralViewOffset());
// Scroll to y=405 height=10, this should make the y position of the content
@@ -916,7 +918,7 @@ TEST_F(ScrollViewTest, ScrollRectToVisibleWithHiddenHorizontalScrollbar) {
auto* contents_ptr = scroll_view_->SetContents(std::move(contents));
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(gfx::Vector2d(0, 0), test_api.IntegralViewOffset());
// Scroll to x=305 width=10, this should make the x position of the content
@@ -946,7 +948,7 @@ TEST_F(ScrollViewTest, ScrollRectToVisibleWithHiddenVerticalScrollbar) {
auto* contents_ptr = scroll_view_->SetContents(std::move(contents));
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(gfx::Vector2d(0, 0), test_api.IntegralViewOffset());
// Scroll to y=305 height=10, this should make the y position of the content
@@ -977,7 +979,7 @@ TEST_F(ScrollViewTest, ScrollChildToVisibleOnFocus) {
auto* child_ptr = contents_ptr->AddChildView(std::move(child));
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(gfx::Vector2d(), test_api.IntegralViewOffset());
// Set focus to the child control. This should cause the control to scroll to
@@ -1012,11 +1014,11 @@ TEST_F(ScrollViewTest, ScrollViewToVisibleOnContentsRootFocus) {
inner_scroll_view_ptr->SetContents(std::move(inner_contents));
inner_scroll_view_ptr->SetBoundsRect(gfx::Rect(0, 510, 100, 100));
- inner_scroll_view_ptr->Layout();
+ RunScheduledLayout(inner_scroll_view_ptr);
EXPECT_EQ(gfx::Vector2d(), inner_test_api.IntegralViewOffset());
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 200, 200));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(gfx::Vector2d(), outer_test_api.IntegralViewOffset());
// Scroll the inner scroll view to y=405 height=10. This should make the y
@@ -1056,7 +1058,7 @@ TEST_F(ScrollViewTest, ClipHeightToNormalContentHeight) {
scroll_view_->GetPreferredSize());
scroll_view_->SizeToPreferredSize();
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight),
scroll_view_->contents()->size());
@@ -1076,7 +1078,7 @@ TEST_F(ScrollViewTest, ClipHeightToShortContentHeight) {
EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_->GetPreferredSize());
scroll_view_->SizeToPreferredSize();
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
// Layered scrolling requires the contents to fill the viewport.
if (contents->layer()) {
@@ -1100,7 +1102,7 @@ TEST_F(ScrollViewTest, ClipHeightToTallContentHeight) {
EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_->GetPreferredSize());
scroll_view_->SizeToPreferredSize();
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
// The width may be less than kWidth if the scroll bar takes up some width.
EXPECT_GE(kWidth, scroll_view_->contents()->width());
@@ -1123,7 +1125,7 @@ TEST_F(ScrollViewTest, ClipHeightToScrollbarUsesWidth) {
gfx::Size new_size(kWidth, scroll_view_->GetHeightForWidth(kWidth));
scroll_view_->SetSize(new_size);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
int expected_width = kWidth - scroll_view_->GetScrollBarLayoutWidth();
EXPECT_EQ(scroll_view_->contents()->size().width(), expected_width);
@@ -1155,7 +1157,7 @@ TEST_F(ScrollViewTest, CornerViewVisibility) {
View* corner_view = ScrollViewTestApi(scroll_view_.get()).corner_view();
contents->SetBounds(0, 0, 200, 200);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
// Corner view should not exist if using overlay scrollbars.
if (scroll_view_->vertical_scroll_bar()->OverlapsContent()) {
@@ -1175,22 +1177,22 @@ TEST_F(ScrollViewTest, CornerViewVisibility) {
// Corner view should be removed when only the vertical scrollbar is visible.
contents->SetBounds(0, 0, 50, 200);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_FALSE(corner_view->parent());
// ... or when only the horizontal scrollbar is visible.
contents->SetBounds(0, 0, 200, 50);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_FALSE(corner_view->parent());
// ... or when no scrollbar is visible.
contents->SetBounds(0, 0, 50, 50);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_FALSE(corner_view->parent());
// Corner view should reappear when both scrollbars reappear.
contents->SetBounds(0, 0, 200, 200);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(scroll_view_.get(), corner_view->parent());
EXPECT_TRUE(corner_view->GetVisible());
}
@@ -1392,7 +1394,7 @@ TEST_F(ScrollViewTest, CocoaOverlayScrollBars) {
// Size the contents such that vertical scrollbar is needed.
// Since it is overlaid, the ViewPort size should match the ScrollView.
contents->SetBounds(0, 0, 50, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100, contents->parent()->width());
EXPECT_EQ(100, contents->parent()->height());
EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutWidth());
@@ -1401,7 +1403,7 @@ TEST_F(ScrollViewTest, CocoaOverlayScrollBars) {
// Size the contents such that horizontal scrollbar is needed.
contents->SetBounds(0, 0, 400, 50);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100, contents->parent()->width());
EXPECT_EQ(100, contents->parent()->height());
EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutHeight());
@@ -1410,7 +1412,7 @@ TEST_F(ScrollViewTest, CocoaOverlayScrollBars) {
// Both horizontal and vertical scrollbars.
contents->SetBounds(0, 0, 300, 400);
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100, contents->parent()->width());
EXPECT_EQ(100, contents->parent()->height());
EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutWidth());
@@ -1428,7 +1430,7 @@ TEST_F(ScrollViewTest, CocoaOverlayScrollBars) {
// to be smaller, and ScrollbarWidth and ScrollbarHeight are non-zero.
SetOverlayScrollersEnabled(false);
EXPECT_TRUE(ViewTestApi(scroll_view_.get()).needs_layout());
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(100 - VerticalScrollBarWidth(), contents->parent()->width());
EXPECT_EQ(100 - HorizontalScrollBarHeight(), contents->parent()->height());
EXPECT_NE(0, VerticalScrollBarWidth());
@@ -1557,7 +1559,7 @@ TEST_F(ScrollViewTest, ConstrainScrollToBounds) {
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(gfx::PointF(), test_api.CurrentOffset());
@@ -1568,14 +1570,14 @@ TEST_F(ScrollViewTest, ConstrainScrollToBounds) {
// Making the viewport 55 pixels taller should scroll up the same amount.
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 155));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(fully_scrolled.y() - 55, test_api.CurrentOffset().y());
EXPECT_EQ(fully_scrolled.x(), test_api.CurrentOffset().x());
// And 77 pixels wider should scroll left. Also make it short again: the y-
// offset from the last change should remain.
scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 177, 100));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(fully_scrolled.y() - 55, test_api.CurrentOffset().y());
EXPECT_EQ(fully_scrolled.x() - 77, test_api.CurrentOffset().x());
}
@@ -1597,19 +1599,19 @@ TEST_F(ScrollViewTest, ContentScrollNotResetOnLayout) {
scroll_view_->ScrollToPosition(test_api.GetScrollBar(VERTICAL), 25);
EXPECT_EQ(25, test_api.CurrentOffset().y());
// Call Layout; no change to scroll position.
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(25, test_api.CurrentOffset().y());
// Change contents of |contents|, call Layout; still no change to scroll
// position.
contents->SetPreferredSize(gfx::Size(300, 500));
contents->InvalidateLayout();
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(25, test_api.CurrentOffset().y());
// Change |contents| to be shorter than the ScrollView's clipped height.
// This /will/ change the scroll location due to ConstrainScrollToBounds.
contents->SetPreferredSize(gfx::Size(300, 50));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ(0, test_api.CurrentOffset().y());
}
@@ -2058,7 +2060,7 @@ TEST_F(ScrollViewTest, IgnoreOverlapWithDisabledHorizontalScroll) {
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
gfx::Size expected_size = scroll_view_->size();
expected_size.Enlarge(-kThickness, 0);
@@ -2084,7 +2086,7 @@ TEST_F(ScrollViewTest, IgnoreOverlapWithHiddenHorizontalScroll) {
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
gfx::Size expected_size = scroll_view_->size();
expected_size.Enlarge(-kThickness, 0);
@@ -2109,7 +2111,7 @@ TEST_F(ScrollViewTest, IgnoreOverlapWithDisabledVerticalScroll) {
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
gfx::Size expected_size = scroll_view_->size();
expected_size.Enlarge(0, -kThickness);
@@ -2135,7 +2137,7 @@ TEST_F(ScrollViewTest, IgnoreOverlapWithHiddenVerticalScroll) {
View* contents = InstallContents();
contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300));
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
gfx::Size expected_size = scroll_view_->size();
expected_size.Enlarge(0, -kThickness);
@@ -2147,7 +2149,7 @@ TEST_F(ScrollViewTest, TestSettingContentsToNull) {
test::ObserveViewDeletion view_deletion{contents};
// Make sure the content is installed and working.
- scroll_view_->Layout();
+ RunScheduledLayout(scroll_view_.get());
EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString());
// This should be legal and not DCHECK.
diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
index bab1c941dda..ac6c8ac1166 100644
--- a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
+++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/raw_ptr.h"
+
#import "ui/views/controls/scrollbar/cocoa_scroll_bar.h"
#include "base/bind.h"
@@ -61,7 +63,7 @@ class CocoaScrollBarThumb : public BaseScrollBarThumb {
private:
// The CocoaScrollBar that owns us.
- CocoaScrollBar* cocoa_scroll_bar_; // weak.
+ raw_ptr<CocoaScrollBar> cocoa_scroll_bar_; // weak.
};
CocoaScrollBarThumb::CocoaScrollBarThumb(CocoaScrollBar* scroll_bar)
diff --git a/chromium/ui/views/controls/separator.cc b/chromium/ui/views/controls/separator.cc
index 4051d9ea810..c11ec64da75 100644
--- a/chromium/ui/views/controls/separator.cc
+++ b/chromium/ui/views/controls/separator.cc
@@ -20,16 +20,16 @@ Separator::Separator() = default;
Separator::~Separator() = default;
-SkColor Separator::GetColor() const {
- return overridden_color_.value_or(0);
+ui::ColorId Separator::GetColorId() const {
+ return color_id_;
}
-void Separator::SetColor(SkColor color) {
- if (overridden_color_ == color)
+void Separator::SetColorId(ui::ColorId color_id) {
+ if (color_id_ == color_id)
return;
- overridden_color_ = color;
- OnPropertyChanged(&overridden_color_, kPropertyEffectsPaint);
+ color_id_ = color_id;
+ OnPropertyChanged(&color_id_, kPropertyEffectsPaint);
}
int Separator::GetPreferredLength() const {
@@ -66,9 +66,7 @@ gfx::Size Separator::CalculatePreferredSize() const {
}
void Separator::OnPaint(gfx::Canvas* canvas) {
- const SkColor color = overridden_color_
- ? *overridden_color_
- : GetColorProvider()->GetColor(ui::kColorSeparator);
+ const SkColor color = GetColorProvider()->GetColor(color_id_);
// Paint background and border, if any.
View::OnPaint(canvas);
@@ -106,7 +104,7 @@ void Separator::OnPaint(gfx::Canvas* canvas) {
}
BEGIN_METADATA(Separator, View)
-ADD_PROPERTY_METADATA(SkColor, Color, ui::metadata::SkColorConverter)
+ADD_PROPERTY_METADATA(ui::ColorId, ColorId)
ADD_PROPERTY_METADATA(int, PreferredLength)
ADD_PROPERTY_METADATA(Separator::Orientation, Orientation)
END_METADATA
diff --git a/chromium/ui/views/controls/separator.h b/chromium/ui/views/controls/separator.h
index 60f415ae5b5..bdc341d1884 100644
--- a/chromium/ui/views/controls/separator.h
+++ b/chromium/ui/views/controls/separator.h
@@ -6,6 +6,7 @@
#define UI_VIEWS_CONTROLS_SEPARATOR_H_
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/color/color_id.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/view.h"
@@ -30,8 +31,8 @@ class VIEWS_EXPORT Separator : public View {
~Separator() override;
- SkColor GetColor() const;
- void SetColor(SkColor color);
+ ui::ColorId GetColorId() const;
+ void SetColorId(ui::ColorId color_id);
// Vertical or horizontal extension depending on the orientation. Set to
// `kThickness` by default.
@@ -47,12 +48,12 @@ class VIEWS_EXPORT Separator : public View {
private:
int preferred_length_ = kThickness;
- absl::optional<SkColor> overridden_color_;
+ ui::ColorId color_id_ = ui::kColorSeparator;
Orientation orientation_ = Orientation::kVertical;
};
BEGIN_VIEW_BUILDER(VIEWS_EXPORT, Separator, View)
-VIEW_BUILDER_PROPERTY(SkColor, Color)
+VIEW_BUILDER_PROPERTY(ui::ColorId, ColorId)
VIEW_BUILDER_PROPERTY(int, PreferredLength)
VIEW_BUILDER_PROPERTY(Separator::Orientation, Orientation)
END_VIEW_BUILDER
diff --git a/chromium/ui/views/controls/separator_unittest.cc b/chromium/ui/views/controls/separator_unittest.cc
index a9f608cfcf6..bf34bb52478 100644
--- a/chromium/ui/views/controls/separator_unittest.cc
+++ b/chromium/ui/views/controls/separator_unittest.cc
@@ -7,7 +7,9 @@
#include <memory>
#include "base/memory/raw_ptr.h"
+#include "ui/color/color_id.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/color_palette.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/views/border.h"
#include "ui/views/test/views_test_base.h"
@@ -35,19 +37,23 @@ class SeparatorTest : public ViewsTestBase {
std::unique_ptr<Widget> widget_;
raw_ptr<Separator> separator_;
+ SkColor expected_foreground_color_ = gfx::kPlaceholderColor;
+
static const SkColor kBackgroundColor;
- static const SkColor kForegroundColor;
+ static const ui::ColorId kForegroundColorId;
static const gfx::Size kTestImageSize;
};
const SkColor SeparatorTest::kBackgroundColor = SK_ColorRED;
-const SkColor SeparatorTest::kForegroundColor = SK_ColorGRAY;
+const ui::ColorId SeparatorTest::kForegroundColorId = ui::kColorSeparator;
const gfx::Size SeparatorTest::kTestImageSize{24, 24};
void SeparatorTest::SetUp() {
ViewsTestBase::SetUp();
widget_ = CreateTestWidget();
separator_ = widget_->SetContentsView(std::make_unique<Separator>());
+ expected_foreground_color_ =
+ widget_->GetColorProvider()->GetColor(kForegroundColorId);
}
void SeparatorTest::TearDown() {
@@ -102,201 +108,201 @@ TEST_F(SeparatorTest, ImageScaleBelowOne_HorizontalLine) {
TEST_F(SeparatorTest, Paint_NoInsets_FillsCanvas_Scale100) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
SkBitmap painted = PaintToCanvas(1.0f);
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 9));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 9));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 0));
}
TEST_F(SeparatorTest, Paint_NoInsets_FillsCanvas_Scale125) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
SkBitmap painted = PaintToCanvas(1.25f);
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 12));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 12));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 0));
}
TEST_F(SeparatorTest, Paint_NoInsets_FillsCanvas_Scale150) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
SkBitmap painted = PaintToCanvas(1.5f);
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 14));
- EXPECT_EQ(kForegroundColor, painted.getColor(14, 14));
- EXPECT_EQ(kForegroundColor, painted.getColor(14, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 14));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(14, 14));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(14, 0));
}
TEST_F(SeparatorTest, Paint_TopInset_Scale100) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(1, 0, 0, 0)));
SkBitmap painted = PaintToCanvas(1.0f);
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(9, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 1));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 1));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 9));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 1));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 1));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 9));
}
TEST_F(SeparatorTest, Paint_TopInset_Scale125) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(1, 0, 0, 0)));
SkBitmap painted = PaintToCanvas(1.25f);
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 1));
EXPECT_EQ(kBackgroundColor, painted.getColor(12, 1));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 2));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 2));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 12));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 2));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 2));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 12));
}
TEST_F(SeparatorTest, Paint_LeftInset_Scale100) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(0, 1, 0, 0)));
SkBitmap painted = PaintToCanvas(1.0f);
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 9));
- EXPECT_EQ(kForegroundColor, painted.getColor(1, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(1, 9));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(1, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(1, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 9));
}
TEST_F(SeparatorTest, Paint_LeftInset_Scale125) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(0, 1, 0, 0)));
SkBitmap painted = PaintToCanvas(1.25f);
EXPECT_EQ(kBackgroundColor, painted.getColor(1, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(1, 12));
- EXPECT_EQ(kForegroundColor, painted.getColor(2, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(2, 12));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(2, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(2, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 12));
}
TEST_F(SeparatorTest, Paint_BottomInset_Scale100) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(0, 0, 1, 0)));
SkBitmap painted = PaintToCanvas(1.0f);
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 8));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 8));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 8));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 8));
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 9));
EXPECT_EQ(kBackgroundColor, painted.getColor(9, 9));
}
TEST_F(SeparatorTest, Paint_BottomInset_Scale125) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(0, 0, 1, 0)));
SkBitmap painted = PaintToCanvas(1.25f);
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 10));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 10));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 10));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 10));
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 11));
EXPECT_EQ(kBackgroundColor, painted.getColor(12, 11));
}
TEST_F(SeparatorTest, Paint_RightInset_Scale100) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(0, 0, 0, 1)));
SkBitmap painted = PaintToCanvas(1.0f);
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 9));
- EXPECT_EQ(kForegroundColor, painted.getColor(8, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(8, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(8, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(8, 9));
EXPECT_EQ(kBackgroundColor, painted.getColor(9, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(9, 9));
}
TEST_F(SeparatorTest, Paint_RightInset_Scale125) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(0, 0, 0, 1)));
SkBitmap painted = PaintToCanvas(1.25f);
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 12));
- EXPECT_EQ(kForegroundColor, painted.getColor(10, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(10, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(10, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(10, 12));
EXPECT_EQ(kBackgroundColor, painted.getColor(11, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(11, 12));
}
TEST_F(SeparatorTest, Paint_Vertical_Scale100) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(0, 4, 0, 5)));
SkBitmap painted = PaintToCanvas(1.0f);
EXPECT_EQ(kBackgroundColor, painted.getColor(3, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(3, 9));
- EXPECT_EQ(kForegroundColor, painted.getColor(4, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(4, 9));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(4, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(4, 9));
EXPECT_EQ(kBackgroundColor, painted.getColor(5, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(5, 9));
}
TEST_F(SeparatorTest, Paint_Vertical_Scale125) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(0, 4, 0, 5)));
SkBitmap painted = PaintToCanvas(1.25f);
EXPECT_EQ(kBackgroundColor, painted.getColor(4, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(4, 12));
- EXPECT_EQ(kForegroundColor, painted.getColor(5, 0));
- EXPECT_EQ(kForegroundColor, painted.getColor(5, 12));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(5, 0));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(5, 12));
EXPECT_EQ(kBackgroundColor, painted.getColor(6, 0));
EXPECT_EQ(kBackgroundColor, painted.getColor(6, 12));
}
TEST_F(SeparatorTest, Paint_Horizontal_Scale100) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(4, 0, 5, 0)));
SkBitmap painted = PaintToCanvas(1.0f);
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 3));
EXPECT_EQ(kBackgroundColor, painted.getColor(9, 3));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 4));
- EXPECT_EQ(kForegroundColor, painted.getColor(9, 4));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 4));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(9, 4));
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 5));
EXPECT_EQ(kBackgroundColor, painted.getColor(9, 5));
}
TEST_F(SeparatorTest, Paint_Horizontal_Scale125) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(4, 0, 5, 0)));
SkBitmap painted = PaintToCanvas(1.25f);
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 4));
EXPECT_EQ(kBackgroundColor, painted.getColor(12, 4));
- EXPECT_EQ(kForegroundColor, painted.getColor(0, 5));
- EXPECT_EQ(kForegroundColor, painted.getColor(12, 5));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(0, 5));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(12, 5));
EXPECT_EQ(kBackgroundColor, painted.getColor(0, 6));
EXPECT_EQ(kBackgroundColor, painted.getColor(12, 6));
}
@@ -305,11 +311,11 @@ TEST_F(SeparatorTest, Paint_Horizontal_Scale125) {
// it to zero.
TEST_F(SeparatorTest, Paint_MinimumSize_Scale100) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(5, 5, 5, 5)));
SkBitmap painted = PaintToCanvas(1.0f);
- EXPECT_EQ(kForegroundColor, painted.getColor(5, 5));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(5, 5));
EXPECT_EQ(kBackgroundColor, painted.getColor(4, 5));
EXPECT_EQ(kBackgroundColor, painted.getColor(5, 4));
EXPECT_EQ(kBackgroundColor, painted.getColor(5, 6));
@@ -320,11 +326,11 @@ TEST_F(SeparatorTest, Paint_MinimumSize_Scale100) {
// it to zero (with scale factor > 1).
TEST_F(SeparatorTest, Paint_MinimumSize_Scale125) {
separator_->SetSize({10, 10});
- separator_->SetColor(kForegroundColor);
+ separator_->SetColorId(kForegroundColorId);
separator_->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(5, 5, 5, 5)));
SkBitmap painted = PaintToCanvas(1.25f);
- EXPECT_EQ(kForegroundColor, painted.getColor(7, 7));
+ EXPECT_EQ(expected_foreground_color_, painted.getColor(7, 7));
EXPECT_EQ(kBackgroundColor, painted.getColor(6, 7));
EXPECT_EQ(kBackgroundColor, painted.getColor(7, 6));
EXPECT_EQ(kBackgroundColor, painted.getColor(7, 8));
diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc
index 8f25251d32f..283c51fae60 100644
--- a/chromium/ui/views/controls/styled_label.cc
+++ b/chromium/ui/views/controls/styled_label.cc
@@ -22,6 +22,8 @@
#include "ui/gfx/text_elider.h"
#include "ui/gfx/text_utils.h"
#include "ui/views/controls/label.h"
+#include "ui/views/controls/link.h"
+#include "ui/views/controls/link_fragment.h"
#include "ui/views/view_class_properties.h"
namespace views {
@@ -326,14 +328,18 @@ void StyledLabel::ClearStyleRanges() {
PreferredSizeChanged();
}
-void StyledLabel::ClickLinkForTesting() {
- const auto it =
- base::ranges::find(children(), Link::kViewClassName, &View::GetClassName);
- DCHECK(it != children().cend());
- (*it)->OnKeyPressed(
+void StyledLabel::ClickFirstLinkForTesting() {
+ GetFirstLinkForTesting()->OnKeyPressed( // IN-TEST
ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE));
}
+views::Link* StyledLabel::GetFirstLinkForTesting() {
+ const auto it = base::ranges::find(children(), LinkFragment::kViewClassName,
+ &View::GetClassName);
+ DCHECK(it != children().cend());
+ return static_cast<views::Link*>(*it);
+}
+
int StyledLabel::StartX(int excess_space) const {
int x = GetInsets().left();
if (horizontal_alignment_ == gfx::ALIGN_LEFT)
@@ -361,6 +367,11 @@ void StyledLabel::CalculateLayout(int width) const {
// Try to preserve leading whitespace on the first line.
bool can_trim_leading_whitespace = false;
StyleRanges::const_iterator current_range = style_ranges_.begin();
+
+ // A pointer to the previous link fragment if a logical link consists of
+ // multiple `LinkFragment` elements.
+ LinkFragment* previous_link_fragment = nullptr;
+
for (std::u16string remaining_string = text_;
content_width > 0 && !remaining_string.empty();) {
layout_size_info_.line_sizes.emplace_back(0, line_height);
@@ -471,18 +482,26 @@ void StyledLabel::CalculateLayout(int width) const {
if (chunk.size() > range.end() - position)
chunk = chunk.substr(0, range.end() - position);
- if (!custom_view)
- label = CreateLabel(chunk, style_info, range);
+ if (!custom_view) {
+ label =
+ CreateLabel(chunk, style_info, range, &previous_link_fragment);
+ } else {
+ previous_link_fragment = nullptr;
+ }
- if (position + chunk.size() >= range.end())
+ if (position + chunk.size() >= range.end()) {
++current_range;
+ // Links do not connect across separate style ranges.
+ previous_link_fragment = nullptr;
+ }
} else {
chunk = substrings[0];
if (position + chunk.size() > range.start())
chunk = chunk.substr(0, range.start() - position);
// This chunk is normal text.
- label = CreateLabel(chunk, default_style, range);
+ label =
+ CreateLabel(chunk, default_style, range, &previous_link_fragment);
}
View* child_view = custom_view ? custom_view : label.get();
@@ -529,14 +548,17 @@ void StyledLabel::CalculateLayout(int width) const {
std::unique_ptr<Label> StyledLabel::CreateLabel(
const std::u16string& text,
const RangeStyleInfo& style_info,
- const gfx::Range& range) const {
+ const gfx::Range& range,
+ LinkFragment** previous_link_fragment) const {
std::unique_ptr<Label> result;
if (style_info.text_style == style::STYLE_LINK) {
// Nothing should (and nothing does) use a custom font for links.
DCHECK(!style_info.custom_font);
- // Note this ignores |default_text_style_|, in favor of style::STYLE_LINK.
- auto link = std::make_unique<Link>(text, text_context_);
+ // Note this ignores |default_text_style_|, in favor of `style::STYLE_LINK`.
+ auto link = std::make_unique<LinkFragment>(
+ text, text_context_, style::STYLE_LINK, *previous_link_fragment);
+ *previous_link_fragment = link.get();
link->SetCallback(style_info.callback);
if (!style_info.accessible_name.empty())
link->SetAccessibleName(style_info.accessible_name);
@@ -572,7 +594,7 @@ void StyledLabel::UpdateLabelBackgroundColor() {
// TODO(kylixrd): Should updating the label background color even be
// allowed if there are custom views?
DCHECK((child->GetClassName() == Label::kViewClassName) ||
- (child->GetClassName() == Link::kViewClassName));
+ (child->GetClassName() == LinkFragment::kViewClassName));
static_cast<Label*>(child)->SetBackgroundColor(new_color);
}
}
diff --git a/chromium/ui/views/controls/styled_label.h b/chromium/ui/views/controls/styled_label.h
index bff92d26c7e..3b2bd6670f7 100644
--- a/chromium/ui/views/controls/styled_label.h
+++ b/chromium/ui/views/controls/styled_label.h
@@ -30,6 +30,7 @@ namespace views {
class Label;
class Link;
+class LinkFragment;
// A class which can apply mixed styles to a block of text. Currently, text is
// always multiline. Trailing whitespace in the styled label text is not
@@ -181,7 +182,10 @@ class VIEWS_EXPORT StyledLabel : public View {
// Sends a space keypress to the first child that is a link. Assumes at least
// one such child exists.
- void ClickLinkForTesting();
+ void ClickFirstLinkForTesting();
+
+ // Get the first child that is a link.
+ views::Link* GetFirstLinkForTesting();
private:
struct StyleRange {
@@ -212,9 +216,11 @@ class VIEWS_EXPORT StyledLabel : public View {
void CalculateLayout(int width) const;
// Creates a Label for a given |text|, |style_info|, and |range|.
- std::unique_ptr<Label> CreateLabel(const std::u16string& text,
- const RangeStyleInfo& style_info,
- const gfx::Range& range) const;
+ std::unique_ptr<Label> CreateLabel(
+ const std::u16string& text,
+ const RangeStyleInfo& style_info,
+ const gfx::Range& range,
+ LinkFragment** previous_link_component) const;
// Update the label background color from the theme or
// |displayed_on_background_color_| if set.
diff --git a/chromium/ui/views/controls/styled_label_unittest.cc b/chromium/ui/views/controls/styled_label_unittest.cc
index 2c4bdb0d3a5..dd67683cbcc 100644
--- a/chromium/ui/views/controls/styled_label_unittest.cc
+++ b/chromium/ui/views/controls/styled_label_unittest.cc
@@ -23,6 +23,7 @@
#include "ui/gfx/font_list.h"
#include "ui/views/border.h"
#include "ui/views/controls/link.h"
+#include "ui/views/controls/link_fragment.h"
#include "ui/views/style/typography.h"
#include "ui/views/test/test_layout_provider.h"
#include "ui/views/test/test_views.h"
@@ -117,7 +118,7 @@ TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) {
InitStyledLabel(text);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(1u, styled()->children().size());
EXPECT_EQ(u"This is a test block of text", LabelAt(styled(), 0)->GetText());
@@ -128,7 +129,7 @@ TEST_F(StyledLabelTest, RespectLeadingWhitespace) {
InitStyledLabel(text);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(1u, styled()->children().size());
EXPECT_EQ(u" This is a test block of text",
@@ -140,7 +141,7 @@ TEST_F(StyledLabelTest, RespectLeadingSpacesInNonFirstLine) {
const std::string text(std::string("First line\n") + indented_line);
InitStyledLabel(text);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(2u, styled()->children().size());
EXPECT_EQ(ASCIIToUTF16(indented_line), LabelAt(styled(), 1)->GetText());
}
@@ -154,7 +155,7 @@ TEST_F(StyledLabelTest, CorrectWrapAtNewline) {
gfx::Size label_preferred_size = label.GetPreferredSize();
// Correct handling of \n and label width limit encountered at the same place
styled()->SetBounds(0, 0, label_preferred_size.width(), 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(2u, styled()->children().size());
EXPECT_EQ(ASCIIToUTF16(first_line), LabelAt(styled(), 0)->GetText());
const auto* label_1 = LabelAt(styled(), 1);
@@ -170,7 +171,7 @@ TEST_F(StyledLabelTest, FirstLineNotEmptyWhenLeadingWhitespaceTooLong) {
gfx::Size label_preferred_size = label.GetPreferredSize();
styled()->SetBounds(0, 0, label_preferred_size.width() / 2, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(1u, styled()->children().size());
EXPECT_EQ(u"a", LabelAt(styled(), 0)->GetText());
@@ -192,7 +193,7 @@ TEST_F(StyledLabelTest, BasicWrapping) {
styled()->SetBounds(
0, 0, styled()->GetInsets().width() + label_preferred_size.width(),
styled()->GetInsets().height() + 2 * label_preferred_size.height());
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(2u, styled()->children().size());
EXPECT_EQ(3, styled()->children()[0]->x());
EXPECT_EQ(3, styled()->children()[0]->y());
@@ -206,7 +207,7 @@ TEST_F(StyledLabelTest, AllowEmptyLines) {
const std::string multiline_text("one\n\nthree");
InitStyledLabel(multiline_text);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(3 * default_height, styled()->GetHeightForWidth(1000));
ASSERT_EQ(2u, styled()->children().size());
EXPECT_EQ(styled()->GetHeightForWidth(1000),
@@ -225,7 +226,7 @@ TEST_F(StyledLabelTest, WrapLongWords) {
styled()->SetBounds(
0, 0, styled()->GetInsets().width() + label_preferred_size.width(),
styled()->GetInsets().height() + 2 * label_preferred_size.height());
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(2u, styled()->children().size());
ASSERT_EQ(gfx::Point(), styled()->origin());
@@ -266,7 +267,7 @@ TEST_F(StyledLabelTest, CreateLinks) {
// Verify layout creates the right number of children.
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(7u, styled()->children().size());
}
@@ -283,7 +284,7 @@ TEST_F(StyledLabelTest, StyledRangeCustomFontUnderlined) {
style_info);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(2u, styled()->children().size());
EXPECT_EQ(gfx::Font::UNDERLINE,
@@ -328,11 +329,11 @@ TEST_F(StyledLabelTest, StyledRangeTextStyleBold) {
StyledLabel unstyled;
unstyled.SetText(ASCIIToUTF16(bold_text));
unstyled.SetBounds(0, 0, styled_width, pref_height);
- unstyled.Layout();
+ RunScheduledLayout(&unstyled);
EXPECT_EQ(1u, unstyled.children().size());
styled()->SetBounds(0, 0, styled_width, pref_height);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(3u, styled()->children().size());
@@ -367,10 +368,12 @@ TEST_F(StyledLabelInWidgetTest, Color) {
style_info_link);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
// The code below is not prepared to deal with dark mode.
- widget()->GetNativeTheme()->set_use_dark_colors(false);
+ auto* const native_theme = widget()->GetNativeTheme();
+ native_theme->set_use_dark_colors(false);
+ native_theme->NotifyOnNativeThemeUpdated();
auto* container = widget()->GetContentsView();
// Obtain the default text color for a label.
@@ -385,13 +388,14 @@ TEST_F(StyledLabelInWidgetTest, Color) {
ASSERT_EQ(3u, styled()->children().size());
EXPECT_EQ(SK_ColorBLUE, LabelAt(styled(), 0)->GetEnabledColor());
- EXPECT_EQ(kDefaultLinkColor,
- LabelAt(styled(), 1, Link::kViewClassName)->GetEnabledColor());
+ EXPECT_EQ(
+ kDefaultLinkColor,
+ LabelAt(styled(), 1, LinkFragment::kViewClassName)->GetEnabledColor());
EXPECT_EQ(kDefaultTextColor, LabelAt(styled(), 2)->GetEnabledColor());
// Test adjusted color readability.
styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK);
- styled()->Layout();
+ RunScheduledLayout(styled());
label->SetBackgroundColor(SK_ColorBLACK);
const SkColor kAdjustedTextColor = label->GetEnabledColor();
@@ -428,7 +432,7 @@ TEST_F(StyledLabelTest, StyledRangeWithTooltip) {
pref_height - styled()->GetInsets().height());
styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(label_preferred_size.width(), styled()->width());
@@ -465,7 +469,7 @@ TEST_F(StyledLabelTest, SetTextContextAndDefaultStyle) {
EXPECT_EQ(label.GetPreferredSize().height(), styled()->height());
EXPECT_EQ(label.GetPreferredSize().width(), styled()->width());
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(1u, styled()->children().size());
Label* sublabel = LabelAt(styled(), 0);
EXPECT_EQ(style::CONTEXT_DIALOG_TITLE, sublabel->GetTextContext());
@@ -509,7 +513,7 @@ TEST_F(StyledLabelTest, LineHeightWithLink) {
TEST_F(StyledLabelTest, HandleEmptyLayout) {
const std::string text("This is a test block of text.");
InitStyledLabel(text);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(0u, styled()->children().size());
}
@@ -529,7 +533,7 @@ TEST_F(StyledLabelTest, CacheSize) {
EXPECT_EQ(0u, styled()->children().size());
styled()->SetBounds(0, 0, preferred_width, preferred_height);
- styled()->Layout();
+ RunScheduledLayout(styled());
// controls should be created after layout
// height should be the same as precalculated
@@ -541,7 +545,7 @@ TEST_F(StyledLabelTest, CacheSize) {
EXPECT_EQ(real_height, precalculated_height);
// another call to Layout should not kill and recreate all controls
- styled()->Layout();
+ RunScheduledLayout(styled());
View* first_child_after_second_layout =
styled()->children().empty() ? nullptr : styled()->children().front();
EXPECT_EQ(first_child_after_layout, first_child_after_second_layout);
@@ -564,7 +568,7 @@ TEST_F(StyledLabelTest, Border) {
gfx::Size label_preferred_size = label.GetPreferredSize();
styled()->SetBorder(CreateEmptyBorder(gfx::Insets::TLBR(5, 10, 6, 20)));
styled()->SetBounds(0, 0, 1000, 0);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(
label_preferred_size.height() + 5 /*top border*/ + 6 /*bottom border*/,
styled()->GetPreferredSize().height());
@@ -637,7 +641,7 @@ TEST_F(StyledLabelTest, AlignmentInLTR) {
const std::string text("text");
InitStyledLabel(text);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
const auto& children = styled()->children();
ASSERT_EQ(1u, children.size());
@@ -645,15 +649,15 @@ TEST_F(StyledLabelTest, AlignmentInLTR) {
EXPECT_EQ(0, children.front()->bounds().x());
styled()->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(1000, children.front()->bounds().right());
styled()->SetHorizontalAlignment(gfx::ALIGN_LEFT);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(0, children.front()->bounds().x());
styled()->SetHorizontalAlignment(gfx::ALIGN_CENTER);
- styled()->Layout();
+ RunScheduledLayout(styled());
Label label(ASCIIToUTF16(text));
EXPECT_EQ((1000 - label.GetPreferredSize().width()) / 2,
children.front()->bounds().x());
@@ -670,7 +674,7 @@ TEST_F(StyledLabelTest, AlignmentInRTL) {
const std::string text("text");
InitStyledLabel(text);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
const auto& children = styled()->children();
ASSERT_EQ(1u, children.size());
@@ -680,16 +684,16 @@ TEST_F(StyledLabelTest, AlignmentInRTL) {
// Setting |ALIGN_LEFT| should be flipped to |ALIGN_RIGHT|.
styled()->SetHorizontalAlignment(gfx::ALIGN_LEFT);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(1000, children.front()->bounds().right());
// Setting |ALIGN_RIGHT| should be flipped to |ALIGN_LEFT|.
styled()->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
- styled()->Layout();
+ RunScheduledLayout(styled());
EXPECT_EQ(0, children.front()->bounds().x());
styled()->SetHorizontalAlignment(gfx::ALIGN_CENTER);
- styled()->Layout();
+ RunScheduledLayout(styled());
Label label(ASCIIToUTF16(text));
EXPECT_EQ((1000 - label.GetPreferredSize().width()) / 2,
children.front()->bounds().x());
@@ -716,7 +720,7 @@ TEST_F(StyledLabelTest, ViewsCenteredWithLinkAndCustomView) {
styled()->AddCustomView(std::move(custom_view));
styled()->SetBounds(0, 0, 1000, 500);
- styled()->Layout();
+ RunScheduledLayout(styled());
const int height = styled()->GetPreferredSize().height();
for (const auto* child : styled()->children())
EXPECT_EQ(height / 2, child->bounds().CenterPoint().y());
@@ -738,7 +742,7 @@ TEST_F(StyledLabelTest, ViewsCenteredForEvenAndOddSizes) {
}
styled()->SetBounds(0, 0, kViewWidth * 3, height);
- styled()->Layout();
+ RunScheduledLayout(styled());
for (const auto* child : styled()->children())
EXPECT_EQ(height / 2, child->bounds().CenterPoint().y());
@@ -750,13 +754,13 @@ TEST_F(StyledLabelTest, CacheSizeWithAlignment) {
InitStyledLabel(text);
styled()->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
styled()->SetBounds(0, 0, 1000, 1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(1u, styled()->children().size());
const View* child = styled()->children().front();
EXPECT_EQ(1000, child->bounds().right());
styled()->SetSize({800, 1000});
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(1u, styled()->children().size());
const View* new_child = styled()->children().front();
EXPECT_EQ(child, new_child);
@@ -770,7 +774,7 @@ TEST_F(StyledLabelTest, SizeToFit) {
InitStyledLabel(text);
styled()->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
styled()->SizeToFit(1000);
- styled()->Layout();
+ RunScheduledLayout(styled());
ASSERT_EQ(1u, styled()->children().size());
EXPECT_EQ(1000, styled()->children().front()->bounds().right());
}
@@ -790,7 +794,7 @@ TEST_F(StyledLabelTest, PreferredSizeRespectsWrapping) {
size.set_width(size.width() / 2);
size.set_height(styled()->GetHeightForWidth(size.width()));
styled()->SetSize(size);
- styled()->Layout();
+ RunScheduledLayout(styled());
const gfx::Size new_size = styled()->GetPreferredSize();
EXPECT_LE(new_size.width(), size.width());
EXPECT_EQ(new_size.height(), size.height());
diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
index b7f190f87b2..6e91a7918d8 100644
--- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -331,15 +331,15 @@ void TabStrip::OnSelectedTabChanged(Tab* from_tab, Tab* to_tab, bool animate) {
return;
if (GetOrientation() == TabbedPane::Orientation::kHorizontal) {
- animating_from_ = gfx::Range(from_tab->GetMirroredX(),
- from_tab->GetMirroredX() + from_tab->width());
- animating_to_ = gfx::Range(to_tab->GetMirroredX(),
- to_tab->GetMirroredX() + to_tab->width());
+ animating_from_ = {from_tab->GetMirroredX(),
+ from_tab->GetMirroredX() + from_tab->width()};
+ animating_to_ = {to_tab->GetMirroredX(),
+ to_tab->GetMirroredX() + to_tab->width()};
} else {
- animating_from_ = gfx::Range(from_tab->bounds().y(),
- from_tab->bounds().y() + from_tab->height());
- animating_to_ = gfx::Range(to_tab->bounds().y(),
- to_tab->bounds().y() + to_tab->height());
+ animating_from_ = {from_tab->bounds().y(),
+ from_tab->bounds().y() + from_tab->height()};
+ animating_to_ = {to_tab->bounds().y(),
+ to_tab->bounds().y() + to_tab->height()};
}
contract_animation_->Stop();
@@ -355,10 +355,11 @@ Tab* TabStrip::GetTabAtDeltaFromSelected(int delta) const {
const size_t selected_tab_index = GetSelectedTabIndex();
DCHECK_NE(kNoSelectedTab, selected_tab_index);
const size_t num_children = children().size();
- // Clamping |delta| here ensures that even a large negative |delta| will not
- // cause the addition in the next statement to wrap below 0.
- delta %= static_cast<int>(num_children);
- return GetTabAtIndex((selected_tab_index + num_children + delta) %
+ // Clamping |delta| here ensures that even a large negative |delta| will be
+ // positive after the addition in the next statement.
+ delta %= base::checked_cast<int>(num_children);
+ delta += static_cast<int>(num_children);
+ return GetTabAtIndex((selected_tab_index + static_cast<size_t>(delta)) %
num_children);
}
@@ -438,30 +439,30 @@ void TabStrip::OnPaintBorder(gfx::Canvas* canvas) {
int min_main_axis = 0;
int max_main_axis = 0;
if (expand_animation_->is_animating()) {
- bool animating_leading = animating_to_.start() < animating_from_.start();
+ bool animating_leading = animating_to_.start < animating_from_.start;
double anim_value = gfx::Tween::CalculateValue(
gfx::Tween::FAST_OUT_LINEAR_IN, expand_animation_->GetCurrentValue());
if (animating_leading) {
min_main_axis = gfx::Tween::IntValueBetween(
- anim_value, animating_from_.start(), animating_to_.start());
- max_main_axis = animating_from_.end();
+ anim_value, animating_from_.start, animating_to_.start);
+ max_main_axis = animating_from_.end;
} else {
- min_main_axis = animating_from_.start();
+ min_main_axis = animating_from_.start;
max_main_axis = gfx::Tween::IntValueBetween(
- anim_value, animating_from_.end(), animating_to_.end());
+ anim_value, animating_from_.end, animating_to_.end);
}
} else if (contract_animation_->is_animating()) {
- bool animating_leading = animating_to_.start() < animating_from_.start();
+ bool animating_leading = animating_to_.start < animating_from_.start;
double anim_value = gfx::Tween::CalculateValue(
gfx::Tween::LINEAR_OUT_SLOW_IN, contract_animation_->GetCurrentValue());
if (animating_leading) {
- min_main_axis = animating_to_.start();
+ min_main_axis = animating_to_.start;
max_main_axis = gfx::Tween::IntValueBetween(
- anim_value, animating_from_.end(), animating_to_.end());
+ anim_value, animating_from_.end, animating_to_.end);
} else {
min_main_axis = gfx::Tween::IntValueBetween(
- anim_value, animating_from_.start(), animating_to_.start());
- max_main_axis = animating_to_.end();
+ anim_value, animating_from_.start, animating_to_.start);
+ max_main_axis = animating_to_.end;
}
} else if (is_horizontal) {
min_main_axis = tab->GetMirroredX();
@@ -483,7 +484,7 @@ void TabStrip::OnPaintBorder(gfx::Canvas* canvas) {
}
BEGIN_METADATA(TabStrip, View)
-ADD_READONLY_PROPERTY_METADATA(int, SelectedTabIndex)
+ADD_READONLY_PROPERTY_METADATA(size_t, SelectedTabIndex)
ADD_READONLY_PROPERTY_METADATA(TabbedPane::Orientation, Orientation)
ADD_READONLY_PROPERTY_METADATA(TabbedPane::TabStripStyle, Style)
END_METADATA
@@ -525,12 +526,13 @@ void TabbedPane::AddTabInternal(size_t index,
std::unique_ptr<View> contents) {
DCHECK_LE(index, GetTabCount());
contents->SetVisible(false);
- contents->GetViewAccessibility().OverrideName(title);
contents->GetViewAccessibility().OverrideRole(ax::mojom::Role::kTabPanel);
+ if (!title.empty())
+ contents->GetViewAccessibility().OverrideName(title);
tab_strip_->AddChildViewAt(std::make_unique<Tab>(this, title, contents.get()),
- static_cast<int>(index));
- contents_->AddChildViewAt(std::move(contents), static_cast<int>(index));
+ index);
+ contents_->AddChildViewAt(std::move(contents), index);
if (!GetSelectedTab())
SelectTabAt(index);
@@ -564,8 +566,10 @@ void TabbedPane::SelectTab(Tab* new_selected_tab, bool animate) {
focus_manager->SetFocusedView(new_selected_tab->contents());
}
- if (listener())
- listener()->TabSelectedAt(tab_strip_->GetIndexOf(new_selected_tab));
+ if (listener()) {
+ listener()->TabSelectedAt(base::checked_cast<int>(
+ tab_strip_->GetIndexOf(new_selected_tab).value()));
+ }
}
void TabbedPane::SelectTabAt(size_t index, bool animate) {
diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h
index cc9622c0659..d8bfb3cdec3 100644
--- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h
+++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.h
@@ -237,6 +237,10 @@ class TabStrip : public View, public gfx::AnimationDelegate {
void OnPaintBorder(gfx::Canvas* canvas) override;
private:
+ struct Coordinates {
+ int start, end;
+ };
+
// The orientation of the tab alignment.
const TabbedPane::Orientation orientation_;
@@ -254,8 +258,8 @@ class TabStrip : public View, public gfx::AnimationDelegate {
std::make_unique<gfx::LinearAnimation>(this);
// The x-coordinate ranges of the old selection and the new selection.
- gfx::Range animating_from_;
- gfx::Range animating_to_;
+ Coordinates animating_from_;
+ Coordinates animating_to_;
};
} // namespace views
diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm
index 690b68cee63..24b3e1a320b 100644
--- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm
+++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_accessibility_mac_unittest.mm
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/raw_ptr.h"
+
#import <Cocoa/Cocoa.h>
#import "base/mac/foundation_util.h"
@@ -89,8 +91,8 @@ class TabbedPaneAccessibilityMacTest : public WidgetTest {
}
protected:
- Widget* widget_ = nullptr;
- TabbedPane* tabbed_pane_ = nullptr;
+ raw_ptr<Widget> widget_ = nullptr;
+ raw_ptr<TabbedPane> tabbed_pane_ = nullptr;
};
// Test the Tab's a11y information compared to a Cocoa NSTabViewItem.
diff --git a/chromium/ui/views/controls/table/table_grouper.h b/chromium/ui/views/controls/table/table_grouper.h
index af2fc0f272f..73e233a95d3 100644
--- a/chromium/ui/views/controls/table/table_grouper.h
+++ b/chromium/ui/views/controls/table/table_grouper.h
@@ -10,8 +10,8 @@
namespace views {
struct VIEWS_EXPORT GroupRange {
- int start;
- int length;
+ size_t start;
+ size_t length;
};
// TableGrouper is used by TableView to group a set of rows and treat them
@@ -19,7 +19,7 @@ struct VIEWS_EXPORT GroupRange {
// together.
class VIEWS_EXPORT TableGrouper {
public:
- virtual void GetGroupRange(int model_index, GroupRange* range) = 0;
+ virtual void GetGroupRange(size_t model_index, GroupRange* range) = 0;
protected:
virtual ~TableGrouper() = default;
diff --git a/chromium/ui/views/controls/table/table_header.cc b/chromium/ui/views/controls/table/table_header.cc
index d0e89665257..14183ea4f11 100644
--- a/chromium/ui/views/controls/table/table_header.cc
+++ b/chromium/ui/views/controls/table/table_header.cc
@@ -54,6 +54,7 @@ constexpr int kSortIndicatorSize = 8;
// static
const int TableHeader::kHorizontalPadding = 7;
+
// static
const int TableHeader::kSortIndicatorWidth =
kSortIndicatorSize + TableHeader::kHorizontalPadding * 2;
@@ -139,16 +140,15 @@ void TableHeader::OnPaint(gfx::Canvas* canvas) {
(column.column.id == sorted_column_id &&
title_width + kSortIndicatorWidth <= width);
- if (paint_sort_indicator &&
- column.column.alignment == ui::TableColumn::RIGHT) {
+ if (paint_sort_indicator)
width -= kSortIndicatorWidth;
- }
canvas->DrawStringRectWithFlags(
column.column.title, font_list_, text_color,
gfx::Rect(GetMirroredXWithWidthInView(x, width), kVerticalPadding,
width, height() - kVerticalPadding * 2),
- TableColumnAlignmentToCanvasAlignment(column.column.alignment));
+ TableColumnAlignmentToCanvasAlignment(
+ GetMirroredTableColumnAlignment(column.column.alignment)));
if (paint_sort_indicator) {
cc::PaintFlags flags;
@@ -157,19 +157,12 @@ void TableHeader::OnPaint(gfx::Canvas* canvas) {
flags.setAntiAlias(true);
int indicator_x = 0;
- ui::TableColumn::Alignment alignment = column.column.alignment;
- if (base::i18n::IsRTL()) {
- if (alignment == ui::TableColumn::LEFT)
- alignment = ui::TableColumn::RIGHT;
- else if (alignment == ui::TableColumn::RIGHT)
- alignment = ui::TableColumn::LEFT;
- }
- switch (alignment) {
+ switch (column.column.alignment) {
case ui::TableColumn::LEFT:
indicator_x = x + title_width;
break;
case ui::TableColumn::CENTER:
- indicator_x = x + width / 2;
+ indicator_x = x + width / 2 + title_width / 2;
break;
case ui::TableColumn::RIGHT:
indicator_x = x + width;
@@ -227,7 +220,7 @@ void TableHeader::AddedToWidget() {
}
ui::Cursor TableHeader::GetCursor(const ui::MouseEvent& event) {
- return GetResizeColumn(GetMirroredXInView(event.x())) != -1
+ return GetResizeColumn(GetMirroredXInView(event.x())).has_value()
? ui::mojom::CursorType::kColumnResize
: View::GetCursor(event);
}
@@ -290,9 +283,8 @@ void TableHeader::OnThemeChanged() {
}
void TableHeader::ResizeColumnViaKeyboard(
- int index,
+ size_t index,
TableView::AdvanceDirection direction) {
- DCHECK_GE(index, 0);
const TableView::VisibleColumn& column = table_->GetVisibleColumn(index);
const int needed_for_title =
gfx::GetStringWidth(column.column.title, font_list_) +
@@ -317,30 +309,32 @@ bool TableHeader::GetHeaderRowHasFocus() const {
}
gfx::Rect TableHeader::GetActiveHeaderCellBounds() const {
- const int active_index = table_->GetActiveVisibleColumnIndex();
- DCHECK_NE(ui::ListSelectionModel::kUnselectedIndex, active_index);
+ const absl::optional<size_t> active_index =
+ table_->GetActiveVisibleColumnIndex();
+ DCHECK(active_index.has_value());
const TableView::VisibleColumn& column =
- table_->GetVisibleColumn(active_index);
+ table_->GetVisibleColumn(active_index.value());
return gfx::Rect(column.x, 0, column.width, height());
}
bool TableHeader::HasFocusIndicator() const {
- return table_->GetActiveVisibleColumnIndex() !=
- ui::ListSelectionModel::kUnselectedIndex;
+ return table_->GetActiveVisibleColumnIndex().has_value();
}
bool TableHeader::StartResize(const ui::LocatedEvent& event) {
if (is_resizing())
return false;
- const int index = GetResizeColumn(GetMirroredXInView(event.x()));
- if (index == -1)
+ const absl::optional<size_t> index =
+ GetResizeColumn(GetMirroredXInView(event.x()));
+ if (!index.has_value())
return false;
resize_details_ = std::make_unique<ColumnResizeDetails>();
- resize_details_->column_index = index;
+ resize_details_->column_index = index.value();
resize_details_->initial_x = event.root_location().x();
- resize_details_->initial_width = table_->GetVisibleColumn(index).width;
+ resize_details_->initial_width =
+ table_->GetVisibleColumn(index.value()).width;
return true;
}
@@ -367,28 +361,34 @@ void TableHeader::ToggleSortOrder(const ui::LocatedEvent& event) {
return;
const int x = GetMirroredXInView(event.x());
- const int index = GetClosestVisibleColumnIndex(table_, x);
- const TableView::VisibleColumn& column(table_->GetVisibleColumn(index));
+ const absl::optional<size_t> index = GetClosestVisibleColumnIndex(table_, x);
+ if (!index.has_value())
+ return;
+ const TableView::VisibleColumn& column(
+ table_->GetVisibleColumn(index.value()));
if (x >= column.x && x < column.x + column.width && event.y() >= 0 &&
- event.y() < height())
- table_->ToggleSortOrder(index);
+ event.y() < height()) {
+ table_->ToggleSortOrder(index.value());
+ }
}
-int TableHeader::GetResizeColumn(int x) const {
+absl::optional<size_t> TableHeader::GetResizeColumn(int x) const {
const Columns& columns(table_->visible_columns());
if (columns.empty())
- return -1;
+ return absl::nullopt;
- const int index = GetClosestVisibleColumnIndex(table_, x);
- DCHECK_NE(-1, index);
- const TableView::VisibleColumn& column(table_->GetVisibleColumn(index));
- if (index > 0 && x >= column.x - kResizePadding &&
+ const absl::optional<size_t> index = GetClosestVisibleColumnIndex(table_, x);
+ DCHECK(index.has_value());
+ const TableView::VisibleColumn& column(
+ table_->GetVisibleColumn(index.value()));
+ if (index.value() > 0 && x >= column.x - kResizePadding &&
x <= column.x + kResizePadding) {
- return index - 1;
+ return index.value() - 1;
}
const int max_x = column.x + column.width;
- return (x >= max_x - kResizePadding && x <= max_x + kResizePadding) ? index
- : -1;
+ return (x >= max_x - kResizePadding && x <= max_x + kResizePadding)
+ ? absl::make_optional(index.value())
+ : absl::nullopt;
}
BEGIN_METADATA(TableHeader, View)
END_METADATA
diff --git a/chromium/ui/views/controls/table/table_header.h b/chromium/ui/views/controls/table/table_header.h
index a435e5d3f3f..60693a4442d 100644
--- a/chromium/ui/views/controls/table/table_header.h
+++ b/chromium/ui/views/controls/table/table_header.h
@@ -34,7 +34,7 @@ class VIEWS_EXPORT TableHeader : public views::View {
const gfx::FontList& font_list() const { return font_list_; }
- void ResizeColumnViaKeyboard(int index,
+ void ResizeColumnViaKeyboard(size_t index,
TableView::AdvanceDirection direction);
// Call to update TableHeader objects that rely on the focus state of its
@@ -63,7 +63,7 @@ class VIEWS_EXPORT TableHeader : public views::View {
ColumnResizeDetails() = default;
// Index into table_->visible_columns() that is being resized.
- int column_index = 0;
+ size_t column_index = 0;
// X-coordinate of the mouse at the time the resize started.
int initial_x = 0;
@@ -92,9 +92,9 @@ class VIEWS_EXPORT TableHeader : public views::View {
// Toggles the sort order of the column at the location in |event|.
void ToggleSortOrder(const ui::LocatedEvent& event);
- // Returns the column to resize given the specified x-coordinate, or -1 if |x|
- // is not in the resize range of any columns.
- int GetResizeColumn(int x) const;
+ // Returns the column to resize given the specified x-coordinate, or nullopt
+ // if |x| is not in the resize range of any columns.
+ absl::optional<size_t> GetResizeColumn(int x) const;
bool is_resizing() const { return resize_details_.get() != nullptr; }
diff --git a/chromium/ui/views/controls/table/table_utils.cc b/chromium/ui/views/controls/table/table_utils.cc
index d6ac523212e..7681fecf6ce 100644
--- a/chromium/ui/views/controls/table/table_utils.cc
+++ b/chromium/ui/views/controls/table/table_utils.cc
@@ -8,7 +8,9 @@
#include <algorithm>
+#include "base/i18n/rtl.h"
#include "base/notreached.h"
+#include "ui/base/models/table_model.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/text_utils.h"
@@ -29,7 +31,7 @@ int WidthForContent(const gfx::FontList& header_font_list,
width =
gfx::GetStringWidth(column.title, header_font_list) + header_padding;
- for (int i = 0, row_count = model->RowCount(); i < row_count; ++i) {
+ for (size_t i = 0, row_count = model->RowCount(); i < row_count; ++i) {
const int cell_width =
gfx::GetStringWidth(model->GetText(i, column.id), content_font_list);
width = std::max(width, cell_width);
@@ -109,14 +111,32 @@ int TableColumnAlignmentToCanvasAlignment(
return gfx::Canvas::TEXT_ALIGN_LEFT;
}
-int GetClosestVisibleColumnIndex(const TableView* table, int x) {
+absl::optional<size_t> GetClosestVisibleColumnIndex(const TableView* table,
+ int x) {
const std::vector<TableView::VisibleColumn>& columns(
table->visible_columns());
+ if (columns.empty())
+ return absl::nullopt;
for (size_t i = 0; i < columns.size(); ++i) {
if (x <= columns[i].x + columns[i].width)
- return static_cast<int>(i);
+ return i;
+ }
+ return columns.size() - 1;
+}
+
+ui::TableColumn::Alignment GetMirroredTableColumnAlignment(
+ ui::TableColumn::Alignment alignment) {
+ if (!base::i18n::IsRTL())
+ return alignment;
+
+ switch (alignment) {
+ case ui::TableColumn::LEFT:
+ return ui::TableColumn::RIGHT;
+ case ui::TableColumn::RIGHT:
+ return ui::TableColumn::LEFT;
+ case ui::TableColumn::CENTER:
+ return ui::TableColumn::CENTER;
}
- return static_cast<int>(columns.size()) - 1;
}
} // namespace views
diff --git a/chromium/ui/views/controls/table/table_utils.h b/chromium/ui/views/controls/table/table_utils.h
index ec387102e69..08a342fd284 100644
--- a/chromium/ui/views/controls/table/table_utils.h
+++ b/chromium/ui/views/controls/table/table_utils.h
@@ -7,6 +7,7 @@
#include <vector>
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/models/table_model.h"
#include "ui/views/views_export.h"
@@ -49,8 +50,16 @@ VIEWS_EXPORT std::vector<int> CalculateTableColumnSizes(
int TableColumnAlignmentToCanvasAlignment(ui::TableColumn::Alignment alignment);
// Returns the index of the closest visible column index to |x|. Return value is
-// in terms of table->visible_columns().
-int GetClosestVisibleColumnIndex(const TableView* table, int x);
+// in terms of table->visible_columns(). Returns nullopt if there are no visible
+// columns.
+absl::optional<size_t> GetClosestVisibleColumnIndex(const TableView* table,
+ int x);
+
+// Returns the mirror of the table column alignment if the layout is
+// right-to-left. If the layout is left-to-right, the same alignment is
+// returned.
+ui::TableColumn::Alignment GetMirroredTableColumnAlignment(
+ ui::TableColumn::Alignment alignment);
} // namespace views
diff --git a/chromium/ui/views/controls/table/table_view.cc b/chromium/ui/views/controls/table/table_view.cc
index 266c7a98fcd..b1da527d0b5 100644
--- a/chromium/ui/views/controls/table/table_view.cc
+++ b/chromium/ui/views/controls/table/table_view.cc
@@ -31,6 +31,7 @@
#include "ui/color/color_provider.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/skia_conversions.h"
@@ -63,14 +64,15 @@ int SwapCompareResult(int result, bool ascending) {
}
// Populates |model_index_to_range_start| based on the |grouper|.
-void GetModelIndexToRangeStart(TableGrouper* grouper,
- int row_count,
- std::map<int, int>* model_index_to_range_start) {
- for (int model_index = 0; model_index < row_count;) {
+void GetModelIndexToRangeStart(
+ TableGrouper* grouper,
+ size_t row_count,
+ std::map<size_t, size_t>* model_index_to_range_start) {
+ for (size_t model_index = 0; model_index < row_count;) {
GroupRange range;
grouper->GetGroupRange(model_index, &range);
- DCHECK_GT(range.length, 0);
- for (int i = model_index; i < model_index + range.length; ++i)
+ DCHECK_GT(range.length, 0u);
+ for (size_t i = model_index; i < model_index + range.length; ++i)
(*model_index_to_range_start)[i] = model_index;
model_index += range.length;
}
@@ -104,7 +106,7 @@ bool IsCmdOrCtrl(const ui::Event& event) {
struct TableView::SortHelper {
explicit SortHelper(TableView* table) : table(table) {}
- bool operator()(int model_index1, int model_index2) {
+ bool operator()(size_t model_index1, size_t model_index2) {
return table->CompareRows(model_index1, model_index2) < 0;
}
@@ -117,9 +119,9 @@ struct TableView::SortHelper {
struct TableView::GroupSortHelper {
explicit GroupSortHelper(TableView* table) : table(table) {}
- bool operator()(int model_index1, int model_index2) {
- const int range1 = model_index_to_range_start[model_index1];
- const int range2 = model_index_to_range_start[model_index2];
+ bool operator()(size_t model_index1, size_t model_index2) {
+ const size_t range1 = model_index_to_range_start[model_index1];
+ const size_t range2 = model_index_to_range_start[model_index2];
if (range1 == range2) {
// The two rows are in the same group, sort so that items in the same
// group always appear in the same order.
@@ -129,7 +131,7 @@ struct TableView::GroupSortHelper {
}
raw_ptr<TableView> table;
- std::map<int, int> model_index_to_range_start;
+ std::map<size_t, size_t> model_index_to_range_start;
};
TableView::VisibleColumn::VisibleColumn() = default;
@@ -287,15 +289,17 @@ void TableView::SetGrouper(TableGrouper* grouper) {
SortItemsAndUpdateMapping(/*schedule_paint=*/true);
}
-int TableView::GetRowCount() const {
+size_t TableView::GetRowCount() const {
return model_ ? model_->RowCount() : 0;
}
-void TableView::Select(int model_row) {
+void TableView::Select(absl::optional<size_t> model_row) {
if (!model_)
return;
- SelectByViewIndex(model_row == -1 ? -1 : ModelToView(model_row));
+ SelectByViewIndex(model_row.has_value()
+ ? absl::make_optional(ModelToView(model_row.value()))
+ : absl::nullopt);
}
void TableView::SetSelectionAll(bool select) {
@@ -313,10 +317,11 @@ void TableView::SetSelectionAll(bool select) {
SetSelectionModel(std::move(selection_model));
}
-int TableView::GetFirstSelectedRow() const {
+absl::optional<size_t> TableView::GetFirstSelectedRow() const {
return selection_model_.empty()
- ? -1
- : *selection_model_.selected_indices().begin();
+ ? absl::nullopt
+ : absl::make_optional(
+ *selection_model_.selected_indices().begin());
}
// TODO(dpenning) : Prevent the last column from being closed. See
@@ -335,10 +340,12 @@ void TableView::SetColumnVisibility(int id, bool is_visible) {
[id](const auto& column) { return column.column.id == id; });
if (i != visible_columns_.end()) {
visible_columns_.erase(i);
- if (active_visible_column_index_ >=
- static_cast<int>(visible_columns_.size()))
- SetActiveVisibleColumnIndex(static_cast<int>(visible_columns_.size()) -
- 1);
+ if (active_visible_column_index_.has_value() &&
+ active_visible_column_index_.value() >= visible_columns_.size())
+ SetActiveVisibleColumnIndex(
+ visible_columns_.empty()
+ ? absl::nullopt
+ : absl::make_optional(visible_columns_.size() - 1));
}
}
@@ -354,9 +361,8 @@ void TableView::SetColumnVisibility(int id, bool is_visible) {
RebuildVirtualAccessibilityChildren();
}
-void TableView::ToggleSortOrder(int visible_column_index) {
- DCHECK(visible_column_index >= 0 &&
- visible_column_index < static_cast<int>(visible_columns_.size()));
+void TableView::ToggleSortOrder(size_t visible_column_index) {
+ DCHECK(visible_column_index < visible_columns_.size());
const ui::TableColumn& column = visible_columns_[visible_column_index].column;
if (!column.sortable)
return;
@@ -411,10 +417,8 @@ bool TableView::HasColumn(int id) const {
}
bool TableView::GetHasFocusIndicator() const {
- int active_row = selection_model_.active();
- return active_row != ui::ListSelectionModel::kUnselectedIndex &&
- active_visible_column_index_ !=
- ui::ListSelectionModel::kUnselectedIndex;
+ return selection_model_.active().has_value() &&
+ active_visible_column_index_.has_value();
}
void TableView::SetObserver(TableViewObserver* observer) {
@@ -428,13 +432,13 @@ TableViewObserver* TableView::GetObserver() const {
return observer_;
}
-const TableView::VisibleColumn& TableView::GetVisibleColumn(int index) {
- DCHECK(index >= 0 && index < static_cast<int>(visible_columns_.size()));
+const TableView::VisibleColumn& TableView::GetVisibleColumn(size_t index) {
+ DCHECK(index < visible_columns_.size());
return visible_columns_[index];
}
-void TableView::SetVisibleColumnWidth(int index, int width) {
- DCHECK(index >= 0 && index < static_cast<int>(visible_columns_.size()));
+void TableView::SetVisibleColumnWidth(size_t index, int width) {
+ DCHECK(index < visible_columns_.size());
if (visible_columns_[index].width == width)
return;
base::AutoReset<bool> reseter(&in_set_visible_column_width_, true);
@@ -448,21 +452,19 @@ void TableView::SetVisibleColumnWidth(int index, int width) {
UpdateVirtualAccessibilityChildrenBounds();
}
-int TableView::ModelToView(int model_index) const {
- DCHECK_GE(model_index, 0) << " negative model_index " << model_index;
+size_t TableView::ModelToView(size_t model_index) const {
if (!GetIsSorted())
return model_index;
- DCHECK_LT(model_index, static_cast<int>(model_to_view_.size()))
+ DCHECK_LT(model_index, model_to_view_.size())
<< " out of bounds model_index " << model_index;
return model_to_view_[model_index];
}
-int TableView::ViewToModel(int view_index) const {
- DCHECK_GE(view_index, 0) << " negative view_index " << view_index;
+size_t TableView::ViewToModel(size_t view_index) const {
DCHECK_LT(view_index, GetRowCount());
if (!GetIsSorted())
return view_index;
- DCHECK_LT(view_index, static_cast<int>(view_to_model_.size()))
+ DCHECK_LT(view_index, view_to_model_.size())
<< " out of bounds view_index " << view_index;
return view_to_model_[view_index];
}
@@ -535,7 +537,7 @@ gfx::Size TableView::CalculatePreferredSize() const {
int width = 50;
if (header_ && !visible_columns_.empty())
width = visible_columns_.back().x + visible_columns_.back().width;
- return gfx::Size(width, GetRowCount() * row_height_);
+ return gfx::Size(width, static_cast<int>(GetRowCount()) * row_height_);
}
bool TableView::GetNeedsNotificationWhenVisibleBoundsChange() const {
@@ -565,7 +567,7 @@ bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
if (header_row_is_active_)
break;
if (GetRowCount())
- SelectByViewIndex(0);
+ SelectByViewIndex(size_t{0});
return true;
case ui::VKEY_END:
@@ -579,7 +581,7 @@ bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
#if BUILDFLAG(IS_MAC)
if (event.IsAltDown()) {
if (GetRowCount())
- SelectByViewIndex(0);
+ SelectByViewIndex(size_t{0});
} else {
AdvanceSelection(AdvanceDirection::kDecrement);
}
@@ -607,9 +609,9 @@ bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
? AdvanceDirection::kIncrement
: AdvanceDirection::kDecrement;
if (IsCmdOrCtrl(event)) {
- if (active_visible_column_index_ != -1 && header_) {
- header_->ResizeColumnViaKeyboard(active_visible_column_index_,
- direction);
+ if (active_visible_column_index_.has_value() && header_) {
+ header_->ResizeColumnViaKeyboard(
+ active_visible_column_index_.value(), direction);
UpdateFocusRings();
}
} else {
@@ -628,9 +630,9 @@ bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
? AdvanceDirection::kDecrement
: AdvanceDirection::kIncrement;
if (IsCmdOrCtrl(event)) {
- if (active_visible_column_index_ != -1 && header_) {
- header_->ResizeColumnViaKeyboard(active_visible_column_index_,
- direction);
+ if (active_visible_column_index_.has_value() && header_) {
+ header_->ResizeColumnViaKeyboard(
+ active_visible_column_index_.value(), direction);
UpdateFocusRings();
}
} else {
@@ -646,16 +648,16 @@ bool TableView::OnKeyPressed(const ui::KeyEvent& event) {
// when the table header is active.
case ui::VKEY_RETURN:
if (PlatformStyle::kTableViewSupportsKeyboardNavigationByCell &&
- active_visible_column_index_ != -1 && header_row_is_active_) {
- ToggleSortOrder(active_visible_column_index_);
+ active_visible_column_index_.has_value() && header_row_is_active_) {
+ ToggleSortOrder(active_visible_column_index_.value());
return true;
}
break;
case ui::VKEY_SPACE:
if (PlatformStyle::kTableViewSupportsKeyboardNavigationByCell &&
- active_visible_column_index_ != -1) {
- ToggleSortOrder(active_visible_column_index_);
+ active_visible_column_index_.has_value()) {
+ ToggleSortOrder(active_visible_column_index_.value());
return true;
}
break;
@@ -675,11 +677,11 @@ bool TableView::OnMousePressed(const ui::MouseEvent& event) {
return true;
const int row = event.y() / row_height_;
- if (row < 0 || row >= GetRowCount())
+ if (row < 0 || static_cast<size_t>(row) >= GetRowCount())
return true;
if (event.GetClickCount() == 2) {
- SelectByViewIndex(row);
+ SelectByViewIndex(static_cast<size_t>(row));
if (observer_)
observer_->OnDoubleClick();
} else if (event.GetClickCount() == 1) {
@@ -698,7 +700,7 @@ void TableView::OnGestureEvent(ui::GestureEvent* event) {
RequestFocus();
const int row = event->y() / row_height_;
- if (row < 0 || row >= GetRowCount())
+ if (row < 0 || static_cast<size_t>(row) >= GetRowCount())
return;
event->StopPropagation();
@@ -709,19 +711,23 @@ void TableView::OnGestureEvent(ui::GestureEvent* event) {
std::u16string TableView::GetTooltipText(const gfx::Point& p) const {
const int row = p.y() / row_height_;
- if (row < 0 || row >= GetRowCount() || visible_columns_.empty())
+ if (row < 0 || static_cast<size_t>(row) >= GetRowCount() ||
+ visible_columns_.empty()) {
return std::u16string();
+ }
const int x = GetMirroredXInView(p.x());
- const int column = GetClosestVisibleColumnIndex(this, x);
- if (x < visible_columns_[column].x ||
- x > (visible_columns_[column].x + visible_columns_[column].width))
+ const absl::optional<size_t> column = GetClosestVisibleColumnIndex(this, x);
+ if (!column.has_value() || x < visible_columns_[column.value()].x ||
+ x > (visible_columns_[column.value()].x +
+ visible_columns_[column.value()].width)) {
return std::u16string();
+ }
- const int model_row = ViewToModel(row);
- if (column == 0 && !model_->GetTooltip(model_row).empty())
+ const size_t model_row = ViewToModel(static_cast<size_t>(row));
+ if (column.value() == 0 && !model_->GetTooltip(model_row).empty())
return model_->GetTooltip(model_row);
- return model_->GetText(model_row, visible_columns_[column].column.id);
+ return model_->GetText(model_row, visible_columns_[column.value()].column.id);
}
void TableView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
@@ -740,7 +746,7 @@ void TableView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
}
bool TableView::HandleAccessibleAction(const ui::AXActionData& action_data) {
- const int row_count = GetRowCount();
+ const size_t row_count = GetRowCount();
if (!row_count)
return false;
@@ -750,9 +756,7 @@ bool TableView::HandleAccessibleAction(const ui::AXActionData& action_data) {
bool focus_on_row =
ax_view ? ax_view->GetData().role == ax::mojom::Role::kRow : false;
- int active_row = selection_model_.active();
- if (active_row == ui::ListSelectionModel::kUnselectedIndex)
- active_row = ModelToView(0);
+ size_t active_row = selection_model_.active().value_or(ModelToView(0));
switch (action_data.action) {
case ax::mojom::Action::kDoDefault:
@@ -760,8 +764,9 @@ bool TableView::HandleAccessibleAction(const ui::AXActionData& action_data) {
if (focus_on_row) {
// If the ax focus is on a row, select this row.
DCHECK(ax_view);
- int row_index = ax_view->GetData().GetIntAttribute(
- ax::mojom::IntAttribute::kTableRowIndex);
+ size_t row_index =
+ base::checked_cast<size_t>(ax_view->GetData().GetIntAttribute(
+ ax::mojom::IntAttribute::kTableRowIndex));
SelectByViewIndex(row_index);
GetViewAccessibility().AnnounceText(l10n_util::GetStringFUTF16(
IDS_TABLE_VIEW_AX_ANNOUNCE_ROW_SELECTED,
@@ -780,7 +785,7 @@ bool TableView::HandleAccessibleAction(const ui::AXActionData& action_data) {
RequestFocus();
// Setting focus should not affect the current selection.
if (selection_model_.empty())
- SelectByViewIndex(0);
+ SelectByViewIndex(size_t{0});
break;
case ax::mojom::Action::kScrollRight: {
@@ -827,23 +832,21 @@ void TableView::OnModelChanged() {
PreferredSizeChanged();
}
-void TableView::OnItemsChanged(int start, int length) {
+void TableView::OnItemsChanged(size_t start, size_t length) {
SortItemsAndUpdateMapping(/*schedule_paint=*/true);
}
-void TableView::OnItemsAdded(int start, int length) {
- DCHECK_GE(start, 0);
- DCHECK_GE(length, 0);
+void TableView::OnItemsAdded(size_t start, size_t length) {
DCHECK_LE(start + length, GetRowCount());
- for (int i = 0; i < length; ++i) {
+ for (size_t i = 0; i < length; ++i) {
// Increment selection model counter at start.
selection_model_.IncrementFrom(start);
// Append new virtual row to accessibility view.
- const int virtual_children_count =
+ const size_t virtual_children_count =
GetViewAccessibility().virtual_children().size();
- const int next_index =
+ const size_t next_index =
header_ ? virtual_children_count - 1 : virtual_children_count;
GetViewAccessibility().AddVirtualChildView(
CreateRowAccessibilityView(next_index));
@@ -854,24 +857,25 @@ void TableView::OnItemsAdded(int start, int length) {
NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, true);
}
-void TableView::OnItemsMoved(int old_start, int length, int new_start) {
+void TableView::OnItemsMoved(size_t old_start,
+ size_t length,
+ size_t new_start) {
selection_model_.Move(old_start, new_start, length);
SortItemsAndUpdateMapping(/*schedule_paint=*/true);
}
-void TableView::OnItemsRemoved(int start, int length) {
- DCHECK_GE(start, 0);
- DCHECK_GE(length, 0);
-
+void TableView::OnItemsRemoved(size_t start, size_t length) {
// Determine the currently selected index in terms of the view. We inline the
// implementation here since ViewToModel() has DCHECKs that fail since the
// model has changed but |model_to_view_| has not been updated yet.
- const int previously_selected_model_index = GetFirstSelectedRow();
- int previously_selected_view_index = previously_selected_model_index;
- if (previously_selected_model_index != -1 && GetIsSorted())
+ const absl::optional<size_t> previously_selected_model_index =
+ GetFirstSelectedRow();
+ absl::optional<size_t> previously_selected_view_index =
+ previously_selected_model_index;
+ if (previously_selected_model_index.has_value() && GetIsSorted())
previously_selected_view_index =
- model_to_view_[previously_selected_model_index];
- for (int i = 0; i < length; ++i)
+ model_to_view_[previously_selected_model_index.value()];
+ for (size_t i = 0; i < length; ++i)
selection_model_.DecrementFrom(start);
// Update the `view_to_model_` and `model_to_view_` mappings prior to updating
@@ -883,25 +887,29 @@ void TableView::OnItemsRemoved(int start, int length) {
// See (https://crbug.com/1173373).
SortItemsAndUpdateMapping(/*schedule_paint=*/true);
if (GetIsSorted()) {
- DCHECK_EQ(GetRowCount(), static_cast<int>(view_to_model_.size()));
- DCHECK_EQ(GetRowCount(), static_cast<int>(model_to_view_.size()));
+ DCHECK_EQ(GetRowCount(), view_to_model_.size());
+ DCHECK_EQ(GetRowCount(), model_to_view_.size());
}
// If the selection was empty and is no longer empty select the same visual
// index.
- if (selection_model_.empty() && previously_selected_view_index != -1 &&
+ if (selection_model_.empty() && previously_selected_view_index.has_value() &&
GetRowCount() && select_on_remove_) {
selection_model_.SetSelectedIndex(ViewToModel(
- std::min(GetRowCount() - 1, previously_selected_view_index)));
+ std::min(GetRowCount() - 1, previously_selected_view_index.value())));
+ }
+ if (!selection_model_.empty()) {
+ const size_t selected_model_index =
+ *selection_model_.selected_indices().begin();
+ if (!selection_model_.active().has_value())
+ selection_model_.set_active(selected_model_index);
+ if (!selection_model_.anchor().has_value())
+ selection_model_.set_anchor(selected_model_index);
}
- if (!selection_model_.empty() && selection_model_.active() == -1)
- selection_model_.set_active(GetFirstSelectedRow());
- if (!selection_model_.empty() && selection_model_.anchor() == -1)
- selection_model_.set_anchor(GetFirstSelectedRow());
// Remove the virtual views that are no longer needed.
auto& virtual_children = GetViewAccessibility().virtual_children();
- for (int i = start; !virtual_children.empty() && i < start + length; i++)
+ for (size_t i = start; !virtual_children.empty() && i < start + length; i++)
virtual_children[virtual_children.size() - 1]->RemoveFromParentView();
UpdateVirtualAccessibilityChildrenBounds();
@@ -912,11 +920,11 @@ void TableView::OnItemsRemoved(int start, int length) {
}
gfx::Point TableView::GetKeyboardContextMenuLocation() {
- int first_selected = GetFirstSelectedRow();
+ absl::optional<size_t> first_selected = GetFirstSelectedRow();
gfx::Rect vis_bounds(GetVisibleBounds());
int y = vis_bounds.height() / 2;
- if (first_selected != -1) {
- gfx::Rect cell_bounds(GetRowBounds(first_selected));
+ if (first_selected.has_value()) {
+ gfx::Rect cell_bounds(GetRowBounds(first_selected.value()));
if (cell_bounds.bottom() >= vis_bounds.y() &&
cell_bounds.bottom() < vis_bounds.bottom()) {
y = cell_bounds.bottom();
@@ -960,7 +968,7 @@ void TableView::OnPaintImpl(gfx::Canvas* canvas) {
return;
const PaintRegion region(GetPaintRegion(GetPaintBounds(canvas)));
- if (region.min_column == -1)
+ if (region.min_column == visible_columns_.size())
return; // No need to paint anything.
const SkColor selected_bg_color =
@@ -972,46 +980,50 @@ void TableView::OnPaintImpl(gfx::Canvas* canvas) {
color_provider->GetColor(ui::kColorTableBackgroundAlternate);
const int cell_margin = GetCellMargin();
const int cell_element_spacing = GetCellElementSpacing();
- for (int i = region.min_row; i < region.max_row; ++i) {
- const int model_index = ViewToModel(i);
+ for (size_t i = region.min_row; i < region.max_row; ++i) {
+ const size_t model_index = ViewToModel(i);
const bool is_selected = selection_model_.IsSelected(model_index);
if (is_selected)
canvas->FillRect(GetRowBounds(i), selected_bg_color);
else if (alternate_bg_color != default_bg_color && (i % 2))
canvas->FillRect(GetRowBounds(i), alternate_bg_color);
- for (int j = region.min_column; j < region.max_column; ++j) {
- const gfx::Rect cell_bounds(GetCellBounds(i, j));
- int text_x = cell_margin + cell_bounds.x();
+ for (size_t j = region.min_column; j < region.max_column; ++j) {
+ const gfx::Rect cell_bounds = GetCellBounds(i, j);
+ gfx::Rect text_bounds = cell_bounds;
+ text_bounds.Inset(gfx::Insets::VH(0, cell_margin));
// Provide space for the grouping indicator, but draw it separately.
- if (j == 0 && grouper_)
- text_x += kGroupingIndicatorSize + cell_element_spacing;
+ if (j == 0 && grouper_) {
+ text_bounds.Inset(gfx::Insets().set_left(kGroupingIndicatorSize +
+ cell_element_spacing));
+ }
// Always paint the icon in the first visible column.
if (j == 0 && table_type_ == ICON_AND_TEXT) {
gfx::ImageSkia image =
model_->GetIcon(model_index).Rasterize(GetColorProvider());
if (!image.isNull()) {
- int image_x =
- GetMirroredXWithWidthInView(text_x, ui::TableModel::kIconSize);
+ int image_x = GetMirroredXWithWidthInView(text_bounds.x(),
+ ui::TableModel::kIconSize);
canvas->DrawImageInt(
image, 0, 0, image.width(), image.height(), image_x,
cell_bounds.y() +
(cell_bounds.height() - ui::TableModel::kIconSize) / 2,
ui::TableModel::kIconSize, ui::TableModel::kIconSize, true);
}
- text_x += ui::TableModel::kIconSize + cell_element_spacing;
+ text_bounds.Inset(gfx::Insets().set_left(ui::TableModel::kIconSize +
+ cell_element_spacing));
}
- if (text_x < cell_bounds.right() - cell_margin) {
+
+ // Paint text if there is still room for it after all that insetting.
+ if (!text_bounds.IsEmpty()) {
canvas->DrawStringRectWithFlags(
model_->GetText(model_index, visible_columns_[j].column.id),
font_list_, is_selected ? selected_fg_color : fg_color,
- gfx::Rect(GetMirroredXWithWidthInView(
- text_x, cell_bounds.right() - text_x - cell_margin),
- cell_bounds.y(), cell_bounds.right() - text_x,
- row_height_),
+ GetMirroredRect(text_bounds),
TableColumnAlignmentToCanvasAlignment(
- visible_columns_[j].column.alignment));
+ GetMirroredTableColumnAlignment(
+ visible_columns_[j].column.alignment)));
}
}
}
@@ -1029,15 +1041,15 @@ void TableView::OnPaintImpl(gfx::Canvas* canvas) {
grouping_flags.setAntiAlias(true);
const int group_indicator_x = GetMirroredXInView(
GetCellBounds(0, 0).x() + cell_margin + kGroupingIndicatorSize / 2);
- for (int i = region.min_row; i < region.max_row;) {
- const int model_index = ViewToModel(i);
+ for (size_t i = region.min_row; i < region.max_row;) {
+ const size_t model_index = ViewToModel(i);
GroupRange range;
grouper_->GetGroupRange(model_index, &range);
- DCHECK_GT(range.length, 0);
+ DCHECK_GT(range.length, 0u);
// The order of rows in a group is consistent regardless of sort, so it's ok
// to do this calculation.
- const int start = i - (model_index - range.start);
- const int last = start + range.length - 1;
+ const size_t start = i - (model_index - range.start);
+ const size_t last = start + range.length - 1;
const gfx::RectF start_cell_bounds(GetCellBounds(start, 0));
const gfx::RectF last_cell_bounds(GetCellBounds(last, 0));
canvas->DrawLine(
@@ -1060,7 +1072,7 @@ int TableView::GetCellElementSpacing() const {
void TableView::SortItemsAndUpdateMapping(bool schedule_paint) {
- const int row_count = GetRowCount();
+ const size_t row_count = GetRowCount();
if (!GetIsSorted()) {
view_to_model_.clear();
@@ -1070,7 +1082,7 @@ void TableView::SortItemsAndUpdateMapping(bool schedule_paint) {
model_to_view_.resize(row_count);
// Resets the mapping so it can be sorted again.
- for (int view_index = 0; view_index < row_count; ++view_index)
+ for (size_t view_index = 0; view_index < row_count; ++view_index)
view_to_model_[view_index] = view_index;
if (grouper_) {
@@ -1084,7 +1096,7 @@ void TableView::SortItemsAndUpdateMapping(bool schedule_paint) {
SortHelper(this));
}
- for (int view_index = 0; view_index < row_count; ++view_index)
+ for (size_t view_index = 0; view_index < row_count; ++view_index)
model_to_view_[view_to_model_[view_index]] = view_index;
model_->ClearCollator();
@@ -1096,7 +1108,7 @@ void TableView::SortItemsAndUpdateMapping(bool schedule_paint) {
SchedulePaint();
}
-int TableView::CompareRows(int model_row1, int model_row2) {
+int TableView::CompareRows(size_t model_row1, size_t model_row2) {
const int sort_result = model_->CompareValues(model_row1, model_row2,
sort_descriptors_[0].column_id);
if (sort_result == 0 && sort_descriptors_.size() > 1) {
@@ -1109,25 +1121,28 @@ int TableView::CompareRows(int model_row1, int model_row2) {
return SwapCompareResult(sort_result, sort_descriptors_[0].ascending);
}
-gfx::Rect TableView::GetRowBounds(int row) const {
- return gfx::Rect(0, row * row_height_, width(), row_height_);
+gfx::Rect TableView::GetRowBounds(size_t row) const {
+ return gfx::Rect(0, static_cast<int>(row) * row_height_, width(),
+ row_height_);
}
-gfx::Rect TableView::GetCellBounds(int row, int visible_column_index) const {
+gfx::Rect TableView::GetCellBounds(size_t row,
+ size_t visible_column_index) const {
if (!header_)
return GetRowBounds(row);
const VisibleColumn& vis_col(visible_columns_[visible_column_index]);
- return gfx::Rect(vis_col.x, row * row_height_, vis_col.width, row_height_);
+ return gfx::Rect(vis_col.x, static_cast<int>(row) * row_height_,
+ vis_col.width, row_height_);
}
gfx::Rect TableView::GetActiveCellBounds() const {
- if (selection_model_.active() == ui::ListSelectionModel::kUnselectedIndex)
+ if (!selection_model_.active().has_value())
return gfx::Rect();
- return GetCellBounds(ModelToView(selection_model_.active()),
- active_visible_column_index_);
+ return GetCellBounds(ModelToView(selection_model_.active().value()),
+ active_visible_column_index_.value());
}
-void TableView::AdjustCellBoundsForText(int visible_column_index,
+void TableView::AdjustCellBoundsForText(size_t visible_column_index,
gfx::Rect* bounds) const {
const int cell_margin = GetCellMargin();
const int cell_element_spacing = GetCellElementSpacing();
@@ -1191,8 +1206,10 @@ TableView::PaintRegion TableView::GetPaintRegion(
DCHECK(GetRowCount());
PaintRegion region;
- region.min_row = base::clamp(bounds.y() / row_height_, 0, GetRowCount() - 1);
- region.max_row = bounds.bottom() / row_height_;
+ region.min_row = static_cast<size_t>(
+ base::clamp(bounds.y() / row_height_, 0,
+ base::saturated_cast<int>(GetRowCount() - 1)));
+ region.max_row = static_cast<size_t>(bounds.bottom() / row_height_);
if (bounds.bottom() % row_height_ != 0)
region.max_row++;
region.max_row = std::min(region.max_row, GetRowCount());
@@ -1204,13 +1221,13 @@ TableView::PaintRegion TableView::GetPaintRegion(
const int paint_x = GetMirroredXForRect(bounds);
const int paint_max_x = paint_x + bounds.width();
- region.min_column = -1;
- region.max_column = visible_columns_.size();
+ region.min_column = region.max_column = visible_columns_.size();
for (size_t i = 0; i < visible_columns_.size(); ++i) {
int max_x = visible_columns_[i].x + visible_columns_[i].width;
- if (region.min_column == -1 && max_x >= paint_x)
- region.min_column = static_cast<int>(i);
- if (region.min_column != -1 && visible_columns_[i].x >= paint_max_x) {
+ if (region.min_column == visible_columns_.size() && max_x >= paint_x)
+ region.min_column = i;
+ if (region.min_column != visible_columns_.size() &&
+ visible_columns_[i].x >= paint_max_x) {
region.max_column = i;
break;
}
@@ -1227,12 +1244,14 @@ gfx::Rect TableView::GetPaintBounds(gfx::Canvas* canvas) const {
void TableView::SchedulePaintForSelection() {
if (selection_model_.size() == 1) {
- const int first_model_row = GetFirstSelectedRow();
- SchedulePaintInRect(GetRowBounds(ModelToView(first_model_row)));
+ const absl::optional<size_t> first_model_row = GetFirstSelectedRow();
+ SchedulePaintInRect(GetRowBounds(ModelToView(first_model_row.value())));
- const int active_row = selection_model_.active();
- if (active_row >= 0 && first_model_row != active_row)
- SchedulePaintInRect(GetRowBounds(ModelToView(active_row)));
+ if (selection_model_.active().has_value() &&
+ first_model_row != selection_model_.active().value()) {
+ SchedulePaintInRect(
+ GetRowBounds(ModelToView(selection_model_.active().value())));
+ }
} else if (selection_model_.size() > 1) {
SchedulePaint();
}
@@ -1247,39 +1266,40 @@ ui::TableColumn TableView::FindColumnByID(int id) const {
void TableView::AdvanceActiveVisibleColumn(AdvanceDirection direction) {
if (visible_columns_.empty()) {
- SetActiveVisibleColumnIndex(-1);
+ SetActiveVisibleColumnIndex(absl::nullopt);
return;
}
- if (active_visible_column_index_ == -1) {
- if (selection_model_.active() == -1 && !header_row_is_active_)
- SelectByViewIndex(0);
- SetActiveVisibleColumnIndex(0);
+ if (!active_visible_column_index_.has_value()) {
+ if (!selection_model_.active().has_value() && !header_row_is_active_)
+ SelectByViewIndex(size_t{0});
+ SetActiveVisibleColumnIndex(size_t{0});
return;
}
if (direction == AdvanceDirection::kDecrement) {
- SetActiveVisibleColumnIndex(std::max(0, active_visible_column_index_ - 1));
- } else {
SetActiveVisibleColumnIndex(
- std::min(static_cast<int>(visible_columns_.size()) - 1,
- active_visible_column_index_ + 1));
+ std::max(size_t{1}, active_visible_column_index_.value()) - 1);
+ } else {
+ SetActiveVisibleColumnIndex(std::min(
+ visible_columns_.size() - 1, active_visible_column_index_.value() + 1));
}
}
-int TableView::GetActiveVisibleColumnIndex() const {
+absl::optional<size_t> TableView::GetActiveVisibleColumnIndex() const {
return active_visible_column_index_;
}
-void TableView::SetActiveVisibleColumnIndex(int index) {
+void TableView::SetActiveVisibleColumnIndex(absl::optional<size_t> index) {
if (active_visible_column_index_ == index)
return;
active_visible_column_index_ = index;
- if (selection_model_.active() != ui::ListSelectionModel::kUnselectedIndex &&
- active_visible_column_index_ != -1) {
- ScrollRectToVisible(GetCellBounds(ModelToView(selection_model_.active()),
- active_visible_column_index_));
+ if (selection_model_.active().has_value() &&
+ active_visible_column_index_.has_value()) {
+ ScrollRectToVisible(
+ GetCellBounds(ModelToView(selection_model_.active().value()),
+ active_visible_column_index_.value()));
}
UpdateFocusRings();
@@ -1287,12 +1307,12 @@ void TableView::SetActiveVisibleColumnIndex(int index) {
OnPropertyChanged(&active_visible_column_index_, kPropertyEffectsNone);
}
-void TableView::SelectByViewIndex(int view_index) {
+void TableView::SelectByViewIndex(absl::optional<size_t> view_index) {
ui::ListSelectionModel new_selection;
- if (view_index != -1) {
- SelectRowsInRangeFrom(view_index, true, &new_selection);
- new_selection.set_anchor(ViewToModel(view_index));
- new_selection.set_active(ViewToModel(view_index));
+ if (view_index.has_value()) {
+ SelectRowsInRangeFrom(view_index.value(), true, &new_selection);
+ new_selection.set_anchor(ViewToModel(view_index.value()));
+ new_selection.set_active(ViewToModel(view_index.value()));
}
SetSelectionModel(std::move(new_selection));
@@ -1307,9 +1327,9 @@ void TableView::SetSelectionModel(ui::ListSelectionModel new_selection) {
SchedulePaintForSelection();
// Scroll the group for the active item to visible.
- if (selection_model_.active() != -1) {
+ if (selection_model_.active().has_value()) {
gfx::Rect vis_rect(GetVisibleBounds());
- const GroupRange range(GetGroupRange(selection_model_.active()));
+ const GroupRange range(GetGroupRange(selection_model_.active().value()));
const int start_y = GetRowBounds(ModelToView(range.start)).y();
const int end_y =
GetRowBounds(ModelToView(range.start + range.length - 1)).bottom();
@@ -1317,10 +1337,10 @@ void TableView::SetSelectionModel(ui::ListSelectionModel new_selection) {
vis_rect.set_height(end_y - start_y);
ScrollRectToVisible(vis_rect);
- if (active_visible_column_index_ == -1)
- SetActiveVisibleColumnIndex(0);
+ if (!active_visible_column_index_.has_value())
+ SetActiveVisibleColumnIndex(size_t{0});
} else if (!header_row_is_active_) {
- SetActiveVisibleColumnIndex(-1);
+ SetActiveVisibleColumnIndex(absl::nullopt);
}
UpdateFocusRings();
@@ -1330,34 +1350,39 @@ void TableView::SetSelectionModel(ui::ListSelectionModel new_selection) {
}
void TableView::AdvanceSelection(AdvanceDirection direction) {
- if (selection_model_.active() == -1) {
+ if (!selection_model_.active().has_value()) {
bool make_header_active =
header_ && direction == AdvanceDirection::kDecrement;
header_row_is_active_ = make_header_active;
- SelectByViewIndex(make_header_active ? -1 : 0);
+ SelectByViewIndex(make_header_active ? absl::nullopt
+ : absl::make_optional(size_t{0}));
UpdateFocusRings();
ScheduleUpdateAccessibilityFocusIfNeeded();
return;
}
- int view_index = ModelToView(selection_model_.active());
+ size_t view_index = ModelToView(selection_model_.active().value());
if (direction == AdvanceDirection::kDecrement) {
bool make_header_active = header_ && view_index == 0;
header_row_is_active_ = make_header_active;
- view_index = make_header_active ? -1 : std::max(0, view_index - 1);
+ SelectByViewIndex(
+ make_header_active
+ ? absl::nullopt
+ : absl::make_optional(std::max(size_t{1}, view_index) - 1));
} else {
header_row_is_active_ = false;
- view_index = std::min(GetRowCount() - 1, view_index + 1);
+ SelectByViewIndex(std::min(GetRowCount() - 1, view_index + 1));
}
- SelectByViewIndex(view_index);
}
void TableView::ConfigureSelectionModelForEvent(
const ui::LocatedEvent& event,
ui::ListSelectionModel* model) const {
- const int view_index = event.y() / row_height_;
- DCHECK(view_index >= 0 && view_index < GetRowCount());
+ const int view_index_int = event.y() / row_height_;
+ DCHECK_GE(view_index_int, 0);
+ const size_t view_index = static_cast<size_t>(view_index_int);
+ DCHECK_LT(view_index, GetRowCount());
- if (selection_model_.anchor() == -1 || single_selection_ ||
+ if (!selection_model_.anchor().has_value() || single_selection_ ||
(!IsCmdOrCtrl(event) && !event.IsShiftDown())) {
SelectRowsInRangeFrom(view_index, true, model);
model->set_anchor(ViewToModel(view_index));
@@ -1373,11 +1398,11 @@ void TableView::ConfigureSelectionModelForEvent(
*model = selection_model_;
else
model->set_anchor(selection_model_.anchor());
- for (int i = std::min(view_index, ModelToView(model->anchor())),
- end = std::max(view_index, ModelToView(model->anchor()));
- i <= end; ++i) {
+ DCHECK(model->anchor().has_value());
+ const size_t anchor_index = ModelToView(model->anchor().value());
+ const auto [min, max] = std::minmax(view_index, anchor_index);
+ for (size_t i = min; i <= max; ++i)
SelectRowsInRangeFrom(i, true, model);
- }
model->set_active(ViewToModel(view_index));
} else {
DCHECK(IsCmdOrCtrl(event));
@@ -1391,11 +1416,11 @@ void TableView::ConfigureSelectionModelForEvent(
}
}
-void TableView::SelectRowsInRangeFrom(int view_index,
+void TableView::SelectRowsInRangeFrom(size_t view_index,
bool select,
ui::ListSelectionModel* model) const {
const GroupRange range(GetGroupRange(ViewToModel(view_index)));
- for (int i = 0; i < range.length; ++i) {
+ for (size_t i = 0; i < range.length; ++i) {
if (select)
model->AddIndexToSelection(range.start + i);
else
@@ -1403,7 +1428,7 @@ void TableView::SelectRowsInRangeFrom(int view_index,
}
}
-GroupRange TableView::GetGroupRange(int model_index) const {
+GroupRange TableView::GetGroupRange(size_t model_index) const {
GroupRange range;
if (grouper_) {
grouper_->GetGroupRange(model_index, &range);
@@ -1426,7 +1451,7 @@ void TableView::RebuildVirtualAccessibilityChildren() {
// Create a virtual accessibility view for each row. At this point on, the
// table has no sort behavior, hence the view index is the same as the model
// index, the sorting will happen at the end.
- for (int index = 0; index < GetRowCount(); ++index)
+ for (size_t index = 0; index < GetRowCount(); ++index)
GetViewAccessibility().AddVirtualChildView(
CreateRowAccessibilityView(index));
@@ -1439,7 +1464,7 @@ void TableView::ClearVirtualAccessibilityChildren() {
}
std::unique_ptr<AXVirtualView> TableView::CreateRowAccessibilityView(
- int row_index) {
+ size_t row_index) {
auto ax_row = std::make_unique<AXVirtualView>();
ui::AXNodeData& row_data = ax_row->GetCustomData();
@@ -1475,7 +1500,7 @@ std::unique_ptr<AXVirtualView> TableView::CreateRowAccessibilityView(
}
std::unique_ptr<AXVirtualView> TableView::CreateCellAccessibilityView(
- int row_index,
+ size_t row_index,
size_t column_index) {
const VisibleColumn& visible_column = visible_columns_[column_index];
const ui::TableColumn column = visible_column.column;
@@ -1530,8 +1555,7 @@ void TableView::PopulateAccessibilityRowData(AXVirtualView* ax_row,
DCHECK(ax_index.has_value());
size_t row_index = ax_index.value() - (header_ ? 1 : 0);
- int model_index = ViewToModel(static_cast<int>(row_index));
- DCHECK_GE(model_index, 0);
+ size_t model_index = ViewToModel(row_index);
// When navigating using up / down cursor keys on the Mac, we read the
// contents of the first cell. If the user needs to explore additional cell's,
@@ -1560,8 +1584,7 @@ void TableView::PopulateAccessibilityCellData(AXVirtualView* ax_cell,
auto column_index = ax_row->GetIndexOf(ax_cell);
DCHECK(column_index.has_value());
- int model_index = ViewToModel(static_cast<int>(row_index));
- DCHECK_GE(model_index, 0);
+ size_t model_index = ViewToModel(row_index);
gfx::Rect cell_bounds = GetCellBounds(row_index, column_index.value());
@@ -1569,7 +1592,7 @@ void TableView::PopulateAccessibilityCellData(AXVirtualView* ax_cell,
data->AddState(ax::mojom::State::kInvisible);
if (PlatformStyle::kTableViewSupportsKeyboardNavigationByCell &&
- static_cast<int>(column_index.value()) == GetActiveVisibleColumnIndex()) {
+ column_index.value() == GetActiveVisibleColumnIndex()) {
if (selection_model().IsSelected(model_index))
data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
}
@@ -1695,7 +1718,7 @@ void TableView::UpdateVirtualAccessibilityChildrenBounds() {
}
// Update the bounds for the table's content rows.
- for (int row_index = 0; row_index < GetRowCount(); row_index++) {
+ for (size_t row_index = 0; row_index < GetRowCount(); row_index++) {
const size_t ax_row_index = header_ ? row_index + 1 : row_index;
if (ax_row_index >= virtual_children.size())
break;
@@ -1734,7 +1757,7 @@ gfx::Rect TableView::CalculateHeaderRowAccessibilityBounds() const {
}
gfx::Rect TableView::CalculateHeaderCellAccessibilityBounds(
- const int visible_column_index) const {
+ const size_t visible_column_index) const {
const gfx::Rect& header_bounds = CalculateHeaderRowAccessibilityBounds();
const VisibleColumn& visible_column = visible_columns_[visible_column_index];
gfx::Rect header_cell_bounds(visible_column.x, header_bounds.y(),
@@ -1743,14 +1766,14 @@ gfx::Rect TableView::CalculateHeaderCellAccessibilityBounds(
}
gfx::Rect TableView::CalculateTableRowAccessibilityBounds(
- const int row_index) const {
+ const size_t row_index) const {
gfx::Rect row_bounds = GetRowBounds(row_index);
return row_bounds;
}
gfx::Rect TableView::CalculateTableCellAccessibilityBounds(
- const int row_index,
- const int visible_column_index) const {
+ const size_t row_index,
+ const size_t visible_column_index) const {
gfx::Rect cell_bounds = GetCellBounds(row_index, visible_column_index);
return cell_bounds;
}
@@ -1777,14 +1800,14 @@ void TableView::UpdateAccessibilityFocus(
if (header_ && header_row_is_active_) {
AXVirtualView* ax_header_row = GetVirtualAccessibilityHeaderRow();
if (!PlatformStyle::kTableViewSupportsKeyboardNavigationByCell ||
- active_visible_column_index_ == -1) {
+ !active_visible_column_index_.has_value()) {
if (ax_header_row) {
ax_header_row->NotifyAccessibilityEvent(ax::mojom::Event::kSelection);
GetViewAccessibility().OverrideFocus(ax_header_row);
}
} else {
AXVirtualView* ax_header_cell = GetVirtualAccessibilityCellImpl(
- ax_header_row, active_visible_column_index_);
+ ax_header_row, active_visible_column_index_.value());
if (ax_header_cell) {
ax_header_cell->NotifyAccessibilityEvent(ax::mojom::Event::kSelection);
GetViewAccessibility().OverrideFocus(ax_header_cell);
@@ -1793,13 +1816,13 @@ void TableView::UpdateAccessibilityFocus(
return;
}
- if (selection_model_.active() == ui::ListSelectionModel::kUnselectedIndex ||
- active_visible_column_index_ == -1) {
+ if (!selection_model_.active().has_value() ||
+ !active_visible_column_index_.has_value()) {
GetViewAccessibility().OverrideFocus(nullptr);
return;
}
- int active_row = ModelToView(selection_model_.active());
+ size_t active_row = ModelToView(selection_model_.active().value());
AXVirtualView* ax_row = GetVirtualAccessibilityBodyRow(active_row);
if (!PlatformStyle::kTableViewSupportsKeyboardNavigationByCell) {
if (ax_row) {
@@ -1807,8 +1830,8 @@ void TableView::UpdateAccessibilityFocus(
GetViewAccessibility().OverrideFocus(ax_row);
}
} else {
- AXVirtualView* ax_cell =
- GetVirtualAccessibilityCellImpl(ax_row, active_visible_column_index_);
+ AXVirtualView* ax_cell = GetVirtualAccessibilityCellImpl(
+ ax_row, active_visible_column_index_.value());
if (ax_cell) {
ax_cell->NotifyAccessibilityEvent(ax::mojom::Event::kSelection);
GetViewAccessibility().OverrideFocus(ax_cell);
@@ -1816,13 +1839,11 @@ void TableView::UpdateAccessibilityFocus(
}
}
-AXVirtualView* TableView::GetVirtualAccessibilityBodyRow(int row) {
- DCHECK_GE(row, 0);
+AXVirtualView* TableView::GetVirtualAccessibilityBodyRow(size_t row) {
DCHECK_LT(row, GetRowCount());
if (header_)
++row;
- if (static_cast<size_t>(row) <
- GetViewAccessibility().virtual_children().size()) {
+ if (row < GetViewAccessibility().virtual_children().size()) {
const auto& ax_row = GetViewAccessibility().virtual_children()[row];
DCHECK(ax_row);
DCHECK_EQ(ax_row->GetData().role, ax::mojom::Role::kRow);
@@ -1847,23 +1868,23 @@ AXVirtualView* TableView::GetVirtualAccessibilityHeaderRow() {
}
AXVirtualView* TableView::GetVirtualAccessibilityCell(
- int row,
- int visible_column_index) {
+ size_t row,
+ size_t visible_column_index) {
return GetVirtualAccessibilityCellImpl(GetVirtualAccessibilityBodyRow(row),
visible_column_index);
}
AXVirtualView* TableView::GetVirtualAccessibilityCellImpl(
AXVirtualView* ax_row,
- int visible_column_index) {
+ size_t visible_column_index) {
DCHECK(ax_row) << "|row| not found. Did you forget to call "
"RebuildVirtualAccessibilityChildren()?";
const auto matches_index = [visible_column_index](const auto& ax_cell) {
DCHECK(ax_cell);
DCHECK(ax_cell->GetData().role == ax::mojom::Role::kColumnHeader ||
ax_cell->GetData().role == ax::mojom::Role::kCell);
- return ax_cell->GetData().GetIntAttribute(
- ax::mojom::IntAttribute::kTableCellColumnIndex) ==
+ return base::checked_cast<size_t>(ax_cell->GetData().GetIntAttribute(
+ ax::mojom::IntAttribute::kTableCellColumnIndex)) ==
visible_column_index;
};
const auto i = std::find_if(ax_row->children().cbegin(),
@@ -1875,10 +1896,10 @@ AXVirtualView* TableView::GetVirtualAccessibilityCellImpl(
}
BEGIN_METADATA(TableView, View)
-ADD_READONLY_PROPERTY_METADATA(int, RowCount)
-ADD_READONLY_PROPERTY_METADATA(int, FirstSelectedRow)
+ADD_READONLY_PROPERTY_METADATA(size_t, RowCount)
+ADD_READONLY_PROPERTY_METADATA(absl::optional<size_t>, FirstSelectedRow)
ADD_READONLY_PROPERTY_METADATA(bool, HasFocusIndicator)
-ADD_PROPERTY_METADATA(int, ActiveVisibleColumnIndex)
+ADD_PROPERTY_METADATA(absl::optional<size_t>, ActiveVisibleColumnIndex)
ADD_READONLY_PROPERTY_METADATA(bool, IsSorted)
ADD_PROPERTY_METADATA(TableViewObserver*, Observer)
ADD_READONLY_PROPERTY_METADATA(int, RowHeight)
diff --git a/chromium/ui/views/controls/table/table_view.h b/chromium/ui/views/controls/table/table_view.h
index 726353c5e4b..aafba0fd39e 100644
--- a/chromium/ui/views/controls/table/table_view.h
+++ b/chromium/ui/views/controls/table/table_view.h
@@ -151,16 +151,16 @@ class VIEWS_EXPORT TableView : public views::View,
void SetGrouper(TableGrouper* grouper);
// Returns the number of rows in the TableView.
- int GetRowCount() const;
+ size_t GetRowCount() const;
// Selects the specified item, making sure it's visible.
- void Select(int model_row);
+ void Select(absl::optional<size_t> model_row);
// Selects all items.
void SetSelectionAll(bool select);
// Returns the first selected row in terms of the model.
- int GetFirstSelectedRow() const;
+ absl::optional<size_t> GetFirstSelectedRow() const;
const ui::ListSelectionModel& selection_model() const {
return selection_model_;
@@ -187,18 +187,18 @@ class VIEWS_EXPORT TableView : public views::View,
void SetObserver(TableViewObserver* observer);
TableViewObserver* GetObserver() const;
- int GetActiveVisibleColumnIndex() const;
+ absl::optional<size_t> GetActiveVisibleColumnIndex() const;
- void SetActiveVisibleColumnIndex(int index);
+ void SetActiveVisibleColumnIndex(absl::optional<size_t> index);
const std::vector<VisibleColumn>& visible_columns() const {
return visible_columns_;
}
- const VisibleColumn& GetVisibleColumn(int index);
+ const VisibleColumn& GetVisibleColumn(size_t index);
// Sets the width of the column. |index| is in terms of |visible_columns_|.
- void SetVisibleColumnWidth(int index, int width);
+ void SetVisibleColumnWidth(size_t index, int width);
// Modify the table sort order, depending on a clicked column and the previous
// table sort order. Does nothing if this column is not sortable.
@@ -207,17 +207,17 @@ class VIEWS_EXPORT TableView : public views::View,
// cycle through three states in order: sorted -> reverse-sorted -> unsorted.
// When switching from one sort column to another, the previous sort column
// will be remembered and used as a secondary sort key.
- void ToggleSortOrder(int visible_column_index);
+ void ToggleSortOrder(size_t visible_column_index);
const SortDescriptors& sort_descriptors() const { return sort_descriptors_; }
void SetSortDescriptors(const SortDescriptors& descriptors);
bool GetIsSorted() const { return !sort_descriptors_.empty(); }
// Maps from the index in terms of the model to that of the view.
- int ModelToView(int model_index) const;
+ size_t ModelToView(size_t model_index) const;
// Maps from the index in terms of the view to that of the model.
- int ViewToModel(int view_index) const;
+ size_t ViewToModel(size_t view_index) const;
int GetRowHeight() const { return row_height_; }
@@ -247,7 +247,8 @@ class VIEWS_EXPORT TableView : public views::View,
// Returns the virtual accessibility view corresponding to the specified cell.
// |row| should be a view index, not a model index.
// |visible_column_index| indexes into |visible_columns_|.
- AXVirtualView* GetVirtualAccessibilityCell(int row, int visible_column_index);
+ AXVirtualView* GetVirtualAccessibilityCell(size_t row,
+ size_t visible_column_index);
bool header_row_is_active() const { return header_row_is_active_; }
@@ -265,10 +266,10 @@ class VIEWS_EXPORT TableView : public views::View,
// ui::TableModelObserver overrides:
void OnModelChanged() override;
- void OnItemsChanged(int start, int length) override;
- void OnItemsAdded(int start, int length) override;
- void OnItemsRemoved(int start, int length) override;
- void OnItemsMoved(int old_start, int length, int new_start) override;
+ void OnItemsChanged(size_t start, size_t length) override;
+ void OnItemsAdded(size_t start, size_t length) override;
+ void OnItemsRemoved(size_t start, size_t length) override;
+ void OnItemsMoved(size_t old_start, size_t length, size_t new_start) override;
protected:
// View overrides:
@@ -292,10 +293,10 @@ class VIEWS_EXPORT TableView : public views::View,
PaintRegion();
~PaintRegion();
- int min_row = 0;
- int max_row = 0;
- int min_column = 0;
- int max_column = 0;
+ size_t min_row = 0;
+ size_t max_row = 0;
+ size_t min_column = 0;
+ size_t max_column = 0;
};
void OnPaintImpl(gfx::Canvas* canvas);
@@ -317,14 +318,14 @@ class VIEWS_EXPORT TableView : public views::View,
// Used to sort the two rows. Returns a value < 0, == 0 or > 0 indicating
// whether the row2 comes before row1, row2 is the same as row1 or row1 comes
// after row2. This invokes CompareValues on the model with the sorted column.
- int CompareRows(int model_row1, int model_row2);
+ int CompareRows(size_t model_row1, size_t model_row2);
// Returns the bounds of the specified row.
- gfx::Rect GetRowBounds(int row) const;
+ gfx::Rect GetRowBounds(size_t row) const;
// Returns the bounds of the specified cell. |visible_column_index| indexes
// into |visible_columns_|.
- gfx::Rect GetCellBounds(int row, int visible_column_index) const;
+ gfx::Rect GetCellBounds(size_t row, size_t visible_column_index) const;
// Returns the bounds of the active cell.
gfx::Rect GetActiveCellBounds() const;
@@ -332,7 +333,7 @@ class VIEWS_EXPORT TableView : public views::View,
// Adjusts |bounds| based on where the text should be painted. |bounds| comes
// from GetCellBounds() and |visible_column_index| is the corresponding column
// (in terms of |visible_columns_|).
- void AdjustCellBoundsForText(int visible_column_index,
+ void AdjustCellBoundsForText(size_t visible_column_index,
gfx::Rect* bounds) const;
// Creates |header_| if necessary.
@@ -360,7 +361,7 @@ class VIEWS_EXPORT TableView : public views::View,
void AdvanceActiveVisibleColumn(AdvanceDirection direction);
// Sets the selection to the specified index (in terms of the view).
- void SelectByViewIndex(int view_index);
+ void SelectByViewIndex(absl::optional<size_t> view_index);
// Sets the selection model to |new_selection|.
void SetSelectionModel(ui::ListSelectionModel new_selection);
@@ -375,14 +376,14 @@ class VIEWS_EXPORT TableView : public views::View,
// Set the selection state of row at |view_index| to |select|, additionally
// any other rows in the GroupRange containing |view_index| are updated as
// well. This does not change the anchor or active index of |model|.
- void SelectRowsInRangeFrom(int view_index,
+ void SelectRowsInRangeFrom(size_t view_index,
bool select,
ui::ListSelectionModel* model) const;
// Returns the range of the specified model index. If a TableGrouper has not
// been set this returns a group with a start of |model_index| and length of
// 1.
- GroupRange GetGroupRange(int model_index) const;
+ GroupRange GetGroupRange(size_t model_index) const;
// Updates a set of accessibility views that expose the visible table contents
// to assistive software.
@@ -399,11 +400,11 @@ class VIEWS_EXPORT TableView : public views::View,
// cell's.
gfx::Rect CalculateHeaderRowAccessibilityBounds() const;
gfx::Rect CalculateHeaderCellAccessibilityBounds(
- const int visible_column_index) const;
- gfx::Rect CalculateTableRowAccessibilityBounds(const int row_index) const;
+ const size_t visible_column_index) const;
+ gfx::Rect CalculateTableRowAccessibilityBounds(const size_t row_index) const;
gfx::Rect CalculateTableCellAccessibilityBounds(
- const int row_index,
- const int visible_column_index) const;
+ const size_t row_index,
+ const size_t visible_column_index) const;
// Schedule a future call UpdateAccessibilityFocus if not already pending.
void ScheduleUpdateAccessibilityFocusIfNeeded();
@@ -431,7 +432,7 @@ class VIEWS_EXPORT TableView : public views::View,
// Returns the virtual accessibility view corresponding to the specified row.
// |row| should be a view index into the TableView's body elements, not a
// model index.
- AXVirtualView* GetVirtualAccessibilityBodyRow(int row);
+ AXVirtualView* GetVirtualAccessibilityBodyRow(size_t row);
// Returns the virtual accessibility view corresponding to the header row, if
// it exists.
@@ -442,17 +443,17 @@ class VIEWS_EXPORT TableView : public views::View,
// `ax_row` should be the virtual view of either a header or body row.
// `visible_column_index` indexes into `visible_columns_`.
AXVirtualView* GetVirtualAccessibilityCellImpl(AXVirtualView* ax_row,
- int visible_column_index);
+ size_t visible_column_index);
// Creates a virtual accessibility view that is used to expose information
// about the row at |view_index| to assistive software.
- std::unique_ptr<AXVirtualView> CreateRowAccessibilityView(int view_index);
+ std::unique_ptr<AXVirtualView> CreateRowAccessibilityView(size_t view_index);
// Creates a virtual accessibility view that is used to expose information
// about the cell at the provided coordinates |row_index| and |column_index|
// to assistive software.
std::unique_ptr<AXVirtualView> CreateCellAccessibilityView(
- int row_index,
+ size_t row_index,
size_t column_index);
// Creates a virtual accessibility view that is used to expose information
@@ -487,8 +488,8 @@ class VIEWS_EXPORT TableView : public views::View,
std::vector<VisibleColumn> visible_columns_;
// The active visible column. Used for keyboard access to functionality such
- // as sorting and resizing. -1 if no visible column is active.
- int active_visible_column_index_ = -1;
+ // as sorting and resizing. nullopt if no visible column is active.
+ absl::optional<size_t> active_visible_column_index_ = absl::nullopt;
// The header. This is only created if more than one column is specified or
// the first column has a non-empty title.
@@ -532,8 +533,8 @@ class VIEWS_EXPORT TableView : public views::View,
SortDescriptors sort_descriptors_;
// Mappings used when sorted.
- std::vector<int> view_to_model_;
- std::vector<int> model_to_view_;
+ std::vector<size_t> view_to_model_;
+ std::vector<size_t> model_to_view_;
raw_ptr<TableGrouper> grouper_ = nullptr;
@@ -549,7 +550,7 @@ class VIEWS_EXPORT TableView : public views::View,
};
BEGIN_VIEW_BUILDER(VIEWS_EXPORT, TableView, View)
-VIEW_BUILDER_PROPERTY(int, ActiveVisibleColumnIndex)
+VIEW_BUILDER_PROPERTY(absl::optional<size_t>, ActiveVisibleColumnIndex)
VIEW_BUILDER_PROPERTY(const std::vector<ui::TableColumn>&,
Columns,
std::vector<ui::TableColumn>)
diff --git a/chromium/ui/views/controls/table/table_view_unittest.cc b/chromium/ui/views/controls/table/table_view_unittest.cc
index 348de27e81c..a2ddafb5f08 100644
--- a/chromium/ui/views/controls/table/table_view_unittest.cc
+++ b/chromium/ui/views/controls/table/table_view_unittest.cc
@@ -30,6 +30,7 @@
#include "ui/views/style/platform_style.h"
#include "ui/views/test/focus_manager_test.h"
#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/unique_widget_ptr.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/widget/widget_utils.h"
@@ -55,7 +56,7 @@ class TableViewTestHelper {
size_t visible_col_count() { return table_->visible_columns().size(); }
- int GetActiveVisibleColumnIndex() {
+ absl::optional<size_t> GetActiveVisibleColumnIndex() {
return table_->GetActiveVisibleColumnIndex();
}
@@ -67,7 +68,7 @@ class TableViewTestHelper {
const gfx::FontList& font_list() { return table_->font_list_; }
- AXVirtualView* GetVirtualAccessibilityBodyRow(int row) {
+ AXVirtualView* GetVirtualAccessibilityBodyRow(size_t row) {
return table_->GetVirtualAccessibilityBodyRow(row);
}
@@ -75,17 +76,18 @@ class TableViewTestHelper {
return table_->GetVirtualAccessibilityHeaderRow();
}
- AXVirtualView* GetVirtualAccessibilityHeaderCell(int visible_column_index) {
+ AXVirtualView* GetVirtualAccessibilityHeaderCell(
+ size_t visible_column_index) {
return table_->GetVirtualAccessibilityCellImpl(
GetVirtualAccessibilityHeaderRow(), visible_column_index);
}
- AXVirtualView* GetVirtualAccessibilityCell(int row,
- int visible_column_index) {
+ AXVirtualView* GetVirtualAccessibilityCell(size_t row,
+ size_t visible_column_index) {
return table_->GetVirtualAccessibilityCell(row, visible_column_index);
}
- gfx::Rect GetCellBounds(int row, int visible_column_index) const {
+ gfx::Rect GetCellBounds(size_t row, size_t visible_column_index) const {
return table_->GetCellBounds(row, visible_column_index);
}
@@ -116,7 +118,7 @@ class TableViewTestHelper {
expected_bounds.push_back(header_row);
// Generate the bounds for the table rows and cells.
- for (int row_index = 0; row_index < table_->GetRowCount(); row_index++) {
+ for (size_t row_index = 0; row_index < table_->GetRowCount(); row_index++) {
auto table_row = std::vector<gfx::Rect>();
gfx::Rect table_row_bounds =
table_->CalculateTableRowAccessibilityBounds(row_index);
@@ -137,7 +139,9 @@ class TableViewTestHelper {
}
private:
- raw_ptr<TableView> table_;
+ // TODO(crbug.com/1298696): views_unittests breaks with MTECheckedPtr
+ // enabled. Triage.
+ raw_ptr<TableView, DegradeToNoOpWhenMTE> table_;
};
namespace {
@@ -167,34 +171,34 @@ class TestTableModel2 : public ui::TableModel {
TestTableModel2& operator=(const TestTableModel2&) = delete;
// Adds a new row at index |row| with values |c1_value| and |c2_value|.
- void AddRow(int row, int c1_value, int c2_value);
+ void AddRow(size_t row, int c1_value, int c2_value);
// Adds new rows starting from |row| to |row| + |length| with the value
// of |row| times the |value_multiplier|. The |value_multiplier| can be used
// to distinguish these rows from the rest.
- void AddRows(int row, int length, int value_multiplier);
+ void AddRows(size_t row, size_t length, int value_multiplier);
// Removes the row at index |row|.
- void RemoveRow(int row);
+ void RemoveRow(size_t row);
// Removes all the rows starting from |row| to |row| + |length|.
- void RemoveRows(int row, int length);
+ void RemoveRows(size_t row, size_t length);
// Changes the values of the row at |row|.
- void ChangeRow(int row, int c1_value, int c2_value);
+ void ChangeRow(size_t row, int c1_value, int c2_value);
// Reorders rows in the model.
- void MoveRows(int row_from, int length, int row_to);
+ void MoveRows(size_t row_from, size_t length, size_t row_to);
// Allows overriding the tooltip for testing.
void SetTooltip(const std::u16string& tooltip);
// ui::TableModel:
- int RowCount() override;
- std::u16string GetText(int row, int column_id) override;
- std::u16string GetTooltip(int row) override;
+ size_t RowCount() override;
+ std::u16string GetText(size_t row, int column_id) override;
+ std::u16string GetTooltip(size_t row) override;
void SetObserver(ui::TableModelObserver* observer) override;
- int CompareValues(int row1, int row2, int column_id) override;
+ int CompareValues(size_t row1, size_t row2, int column_id) override;
private:
raw_ptr<ui::TableModelObserver> observer_ = nullptr;
@@ -212,8 +216,8 @@ TestTableModel2::TestTableModel2() {
AddRow(3, 3, 0);
}
-void TestTableModel2::AddRow(int row, int c1_value, int c2_value) {
- DCHECK(row >= 0 && row <= static_cast<int>(rows_.size()));
+void TestTableModel2::AddRow(size_t row, int c1_value, int c2_value) {
+ DCHECK(row <= rows_.size());
std::vector<int> new_row;
new_row.push_back(c1_value);
new_row.push_back(c2_value);
@@ -222,14 +226,13 @@ void TestTableModel2::AddRow(int row, int c1_value, int c2_value) {
observer_->OnItemsAdded(row, 1);
}
-void TestTableModel2::AddRows(int row, int length, int value_multiplier) {
- DCHECK(row >= 0 && length >= 0);
+void TestTableModel2::AddRows(size_t row, size_t length, int value_multiplier) {
// Do not DCHECK here since we are testing the OnItemsAdded callback.
- if (row >= 0 && row <= static_cast<int>(rows_.size())) {
- for (int i = row; i < row + length; i++) {
+ if (row <= rows_.size()) {
+ for (size_t i = row; i < row + length; i++) {
std::vector<int> new_row;
- new_row.push_back(i + value_multiplier);
- new_row.push_back(i + value_multiplier);
+ new_row.push_back(static_cast<int>(i) + value_multiplier);
+ new_row.push_back(static_cast<int>(i) + value_multiplier);
rows_.insert(rows_.begin() + i, new_row);
}
}
@@ -238,39 +241,36 @@ void TestTableModel2::AddRows(int row, int length, int value_multiplier) {
observer_->OnItemsAdded(row, length);
}
-void TestTableModel2::RemoveRow(int row) {
- DCHECK(row >= 0 && row < static_cast<int>(rows_.size()));
+void TestTableModel2::RemoveRow(size_t row) {
+ DCHECK(row < rows_.size());
rows_.erase(rows_.begin() + row);
if (observer_)
observer_->OnItemsRemoved(row, 1);
}
-void TestTableModel2::RemoveRows(int row, int length) {
- DCHECK(row >= 0 && length >= 0);
- if (row >= 0 && row <= static_cast<int>(rows_.size())) {
- rows_.erase(rows_.begin() + row,
- rows_.begin() + base::clamp(row + length, 0,
- static_cast<int>(rows_.size())));
+void TestTableModel2::RemoveRows(size_t row, size_t length) {
+ if (row <= rows_.size()) {
+ rows_.erase(
+ rows_.begin() + row,
+ rows_.begin() + base::clamp(row + length, size_t{0}, rows_.size()));
}
if (observer_ && length > 0)
observer_->OnItemsRemoved(row, length);
}
-void TestTableModel2::ChangeRow(int row, int c1_value, int c2_value) {
- DCHECK(row >= 0 && row < static_cast<int>(rows_.size()));
+void TestTableModel2::ChangeRow(size_t row, int c1_value, int c2_value) {
+ DCHECK(row < rows_.size());
rows_[row][0] = c1_value;
rows_[row][1] = c2_value;
if (observer_)
observer_->OnItemsChanged(row, 1);
}
-void TestTableModel2::MoveRows(int row_from, int length, int row_to) {
- DCHECK_GT(length, 0);
- DCHECK_GE(row_from, 0);
- DCHECK_LE(row_from + length, static_cast<int>(rows_.size()));
- DCHECK_GE(row_to, 0);
- DCHECK_LE(row_to + length, static_cast<int>(rows_.size()));
+void TestTableModel2::MoveRows(size_t row_from, size_t length, size_t row_to) {
+ DCHECK_GT(length, 0u);
+ DCHECK_LE(row_from + length, rows_.size());
+ DCHECK_LE(row_to + length, rows_.size());
auto old_start = rows_.begin() + row_from;
std::vector<std::vector<int>> temp(old_start, old_start + length);
@@ -284,15 +284,15 @@ void TestTableModel2::SetTooltip(const std::u16string& tooltip) {
tooltip_ = tooltip;
}
-int TestTableModel2::RowCount() {
- return static_cast<int>(rows_.size());
+size_t TestTableModel2::RowCount() {
+ return rows_.size();
}
-std::u16string TestTableModel2::GetText(int row, int column_id) {
+std::u16string TestTableModel2::GetText(size_t row, int column_id) {
return base::NumberToString16(rows_[row][column_id]);
}
-std::u16string TestTableModel2::GetTooltip(int row) {
+std::u16string TestTableModel2::GetTooltip(size_t row) {
return tooltip_ ? *tooltip_ : u"Tooltip" + base::NumberToString16(row);
}
@@ -300,14 +300,14 @@ void TestTableModel2::SetObserver(ui::TableModelObserver* observer) {
observer_ = observer;
}
-int TestTableModel2::CompareValues(int row1, int row2, int column_id) {
+int TestTableModel2::CompareValues(size_t row1, size_t row2, int column_id) {
return rows_[row1][column_id] - rows_[row2][column_id];
}
// Returns the view to model mapping as a string.
std::string GetViewToModelAsString(TableView* table) {
std::string result;
- for (int i = 0; i < table->GetRowCount(); ++i) {
+ for (size_t i = 0; i < table->GetRowCount(); ++i) {
if (i != 0)
result += " ";
result += base::NumberToString(table->ViewToModel(i));
@@ -318,7 +318,7 @@ std::string GetViewToModelAsString(TableView* table) {
// Returns the model to view mapping as a string.
std::string GetModelToViewAsString(TableView* table) {
std::string result;
- for (int i = 0; i < table->GetRowCount(); ++i) {
+ for (size_t i = 0; i < table->GetRowCount(); ++i) {
if (i != 0)
result += " ";
result += base::NumberToString(table->ModelToView(i));
@@ -330,7 +330,7 @@ std::string GetModelToViewAsString(TableView* table) {
// scrolled out of view are included; hidden columns are excluded.
std::string GetRowsInViewOrderAsString(TableView* table) {
std::string result;
- for (int i = 0; i < table->GetRowCount(); ++i) {
+ for (size_t i = 0; i < table->GetRowCount(); ++i) {
if (i != 0)
result += ", "; // Comma between each row.
@@ -454,17 +454,16 @@ class TableViewTest : public ViewsTestBase,
table_ = table.get();
auto scroll_view = TableView::CreateScrollViewWithTable(std::move(table));
scroll_view->SetBounds(0, 0, 10000, 10000);
- scroll_view->Layout();
helper_ = std::make_unique<TableViewTestHelper>(table_);
widget_ = std::make_unique<Widget>();
Widget::InitParams params =
CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(0, 0, 650, 650);
params.delegate = GetWidgetDelegate(widget_.get());
widget_->Init(std::move(params));
- widget_->GetRootView()->AddChildView(std::move(scroll_view));
+ RunScheduledLayout(
+ widget_->GetRootView()->AddChildView(std::move(scroll_view)));
widget_->Show();
}
@@ -485,25 +484,8 @@ class TableViewTest : public ViewsTestBase,
generator.GestureTapAt(GetPointForRow(row));
}
- // Returns the state of the selection model as a string. The format is:
- // 'active=X anchor=X selection=X X X...'.
std::string SelectionStateAsString() const {
- const ui::ListSelectionModel& model(table_->selection_model());
- std::string result = "active=" + base::NumberToString(model.active()) +
- " anchor=" + base::NumberToString(model.anchor()) +
- " selection=";
- const ui::ListSelectionModel::SelectedIndices& selection(
- model.selected_indices());
- bool first = true;
- for (int index : selection) {
- if (first) {
- first = false;
- } else {
- result += " ";
- }
- result += base::NumberToString(index);
- }
- return result;
+ return table_->selection_model().ToString();
}
void PressKey(ui::KeyboardCode code) { PressKey(code, ui::EF_NONE); }
@@ -608,11 +590,14 @@ class TableViewTest : public ViewsTestBase,
std::unique_ptr<TestTableModel2> model_;
// Owned by |parent_|.
- raw_ptr<TableView> table_ = nullptr;
+ //
+ // TODO(crbug.com/1298696): views_unittests breaks with MTECheckedPtr
+ // enabled. Triage.
+ raw_ptr<TableView, DegradeToNoOpWhenMTE> table_ = nullptr;
std::unique_ptr<TableViewTestHelper> helper_;
- std::unique_ptr<Widget> widget_;
+ UniqueWidgetPtr widget_;
private:
gfx::Point GetPointForRow(int row) {
@@ -641,7 +626,7 @@ TEST_P(TableViewTest, RebuildVirtualAccessibilityChildren) {
EXPECT_TRUE(data.HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(ax::mojom::Restriction::kReadOnly, data.GetRestriction());
EXPECT_EQ(table_->GetRowCount(),
- static_cast<int>(
+ static_cast<size_t>(
data.GetIntAttribute(ax::mojom::IntAttribute::kTableRowCount)));
EXPECT_EQ(helper_->visible_col_count(),
static_cast<size_t>(data.GetIntAttribute(
@@ -664,15 +649,15 @@ TEST_P(TableViewTest, RebuildVirtualAccessibilityChildren) {
ax::mojom::IntAttribute::kTableCellColumnIndex));
}
- int i = 0;
+ size_t i = 0;
for (auto child_iter = view_accessibility.virtual_children().begin() + 1;
i < table_->GetRowCount(); ++child_iter, ++i) {
const auto& row = *child_iter;
ASSERT_TRUE(row);
const ui::AXNodeData& row_data = row->GetData();
EXPECT_EQ(ax::mojom::Role::kRow, row_data.role);
- EXPECT_EQ(
- i, row_data.GetIntAttribute(ax::mojom::IntAttribute::kTableRowIndex));
+ EXPECT_EQ(i, static_cast<size_t>(row_data.GetIntAttribute(
+ ax::mojom::IntAttribute::kTableRowIndex)));
ASSERT_FALSE(row_data.HasState(ax::mojom::State::kInvisible));
ASSERT_EQ(helper_->visible_col_count(), row->children().size());
@@ -681,8 +666,8 @@ TEST_P(TableViewTest, RebuildVirtualAccessibilityChildren) {
ASSERT_TRUE(cell);
const ui::AXNodeData& cell_data = cell->GetData();
EXPECT_EQ(ax::mojom::Role::kCell, cell_data.role);
- EXPECT_EQ(i, cell_data.GetIntAttribute(
- ax::mojom::IntAttribute::kTableCellRowIndex));
+ EXPECT_EQ(i, static_cast<size_t>(cell_data.GetIntAttribute(
+ ax::mojom::IntAttribute::kTableCellRowIndex)));
EXPECT_EQ(j++, cell_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableCellColumnIndex));
ASSERT_FALSE(cell_data.HasState(ax::mojom::State::kInvisible));
@@ -723,26 +708,26 @@ TEST_P(TableViewTest, UpdateVirtualAccessibilityChildrenBoundsHideColumn) {
}
TEST_P(TableViewTest, GetVirtualAccessibilityBodyRow) {
- for (int i = 0; i < table_->GetRowCount(); ++i) {
+ for (size_t i = 0; i < table_->GetRowCount(); ++i) {
const AXVirtualView* row = helper_->GetVirtualAccessibilityBodyRow(i);
ASSERT_TRUE(row);
const ui::AXNodeData& row_data = row->GetData();
EXPECT_EQ(ax::mojom::Role::kRow, row_data.role);
- EXPECT_EQ(i, static_cast<int>(row_data.GetIntAttribute(
+ EXPECT_EQ(i, static_cast<size_t>(row_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableRowIndex)));
}
}
TEST_P(TableViewTest, GetVirtualAccessibilityCell) {
- for (int i = 0; i < table_->GetRowCount(); ++i) {
- for (int j = 0; j < static_cast<int>(helper_->visible_col_count()); ++j) {
+ for (size_t i = 0; i < table_->GetRowCount(); ++i) {
+ for (size_t j = 0; j < helper_->visible_col_count(); ++j) {
const AXVirtualView* cell = helper_->GetVirtualAccessibilityCell(i, j);
ASSERT_TRUE(cell);
const ui::AXNodeData& cell_data = cell->GetData();
EXPECT_EQ(ax::mojom::Role::kCell, cell_data.role);
- EXPECT_EQ(i, static_cast<int>(cell_data.GetIntAttribute(
+ EXPECT_EQ(i, static_cast<size_t>(cell_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableCellRowIndex)));
- EXPECT_EQ(j, static_cast<int>(cell_data.GetIntAttribute(
+ EXPECT_EQ(j, static_cast<size_t>(cell_data.GetIntAttribute(
ax::mojom::IntAttribute::kTableCellColumnIndex)));
}
}
@@ -860,12 +845,12 @@ TEST_P(TableViewTest, ResizeViaKeyboard) {
EXPECT_NE(0, x);
// Table starts off with no visible column being active.
- ASSERT_EQ(-1, helper_->GetActiveVisibleColumnIndex());
+ ASSERT_FALSE(helper_->GetActiveVisibleColumnIndex().has_value());
ui::ListSelectionModel new_selection;
new_selection.SetSelectedIndex(1);
helper_->SetSelectionModel(new_selection);
- ASSERT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ ASSERT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
PressKey(ui::VKEY_LEFT, ui::EF_CONTROL_DOWN);
// This should shrink the first column and pull the second column in.
@@ -1062,12 +1047,12 @@ TEST_P(TableViewTest, SortOnSpaceBar) {
table_->RequestFocus();
ASSERT_TRUE(table_->sort_descriptors().empty());
// Table starts off with no visible column being active.
- ASSERT_EQ(-1, helper_->GetActiveVisibleColumnIndex());
+ ASSERT_FALSE(helper_->GetActiveVisibleColumnIndex().has_value());
ui::ListSelectionModel new_selection;
new_selection.SetSelectedIndex(1);
helper_->SetSelectionModel(new_selection);
- ASSERT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ ASSERT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
PressKey(ui::VKEY_SPACE);
ASSERT_EQ(1u, table_->sort_descriptors().size());
@@ -1080,7 +1065,7 @@ TEST_P(TableViewTest, SortOnSpaceBar) {
EXPECT_FALSE(table_->sort_descriptors()[0].ascending);
PressKey(ui::VKEY_RIGHT);
- ASSERT_EQ(1, helper_->GetActiveVisibleColumnIndex());
+ ASSERT_EQ(1u, helper_->GetActiveVisibleColumnIndex());
PressKey(ui::VKEY_SPACE);
ASSERT_EQ(2u, table_->sort_descriptors().size());
@@ -1102,15 +1087,15 @@ TEST_P(TableViewTest, ActiveCellBoundsFollowColumnSorting) {
table_->ToggleSortOrder(0);
ClickOnRow(0, 0);
EXPECT_EQ(helper_->GetCellBounds(0, 0), helper_->GetActiveCellBounds());
- EXPECT_EQ(0, table_->ViewToModel(0));
+ EXPECT_EQ(0u, table_->ViewToModel(0));
ClickOnRow(1, 0);
EXPECT_EQ(helper_->GetCellBounds(1, 0), helper_->GetActiveCellBounds());
- EXPECT_EQ(1, table_->ViewToModel(1));
+ EXPECT_EQ(1u, table_->ViewToModel(1));
ClickOnRow(2, 0);
EXPECT_EQ(helper_->GetCellBounds(2, 0), helper_->GetActiveCellBounds());
- EXPECT_EQ(2, table_->ViewToModel(2));
+ EXPECT_EQ(2u, table_->ViewToModel(2));
// Toggle the sort order of the second column. The active row will stay in
// sync with the view index, meanwhile the model's change which shows that
@@ -1118,15 +1103,15 @@ TEST_P(TableViewTest, ActiveCellBoundsFollowColumnSorting) {
table_->ToggleSortOrder(1);
ClickOnRow(0, 0);
EXPECT_EQ(helper_->GetCellBounds(0, 0), helper_->GetActiveCellBounds());
- EXPECT_EQ(3, table_->ViewToModel(0));
+ EXPECT_EQ(3u, table_->ViewToModel(0));
ClickOnRow(1, 0);
EXPECT_EQ(helper_->GetCellBounds(1, 0), helper_->GetActiveCellBounds());
- EXPECT_EQ(0, table_->ViewToModel(1));
+ EXPECT_EQ(0u, table_->ViewToModel(1));
ClickOnRow(2, 0);
EXPECT_EQ(helper_->GetCellBounds(2, 0), helper_->GetActiveCellBounds());
- EXPECT_EQ(1, table_->ViewToModel(2));
+ EXPECT_EQ(1u, table_->ViewToModel(2));
// Verifying invalid active indexes return an empty rect.
new_selection.Clear();
@@ -1161,11 +1146,11 @@ class TableGrouperImpl : public TableGrouper {
TableGrouperImpl(const TableGrouperImpl&) = delete;
TableGrouperImpl& operator=(const TableGrouperImpl&) = delete;
- void SetRanges(const std::vector<int>& ranges) { ranges_ = ranges; }
+ void SetRanges(const std::vector<size_t>& ranges) { ranges_ = ranges; }
// TableGrouper overrides:
- void GetGroupRange(int model_index, GroupRange* range) override {
- int offset = 0;
+ void GetGroupRange(size_t model_index, GroupRange* range) override {
+ size_t offset = 0;
size_t range_index = 0;
for (; range_index < ranges_.size() && offset < model_index; ++range_index)
offset += ranges_[range_index];
@@ -1180,7 +1165,7 @@ class TableGrouperImpl : public TableGrouper {
}
private:
- std::vector<int> ranges_;
+ std::vector<size_t> ranges_;
};
} // namespace
@@ -1193,10 +1178,7 @@ TEST_P(TableViewTest, Grouping) {
// B 2
// 3
TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
+ grouper.SetRanges({2, 2});
table_->SetGrouper(&grouper);
// Toggle the sort order of the first column, shouldn't change anything.
@@ -1289,7 +1271,7 @@ TEST_P(TableViewTest, Selection) {
table_->set_observer(&observer);
// Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
// Select the last row.
table_->Select(3);
@@ -1385,11 +1367,12 @@ TEST_P(TableViewTest, SelectAll) {
table_->set_observer(&observer);
// Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
table_->SetSelectionAll(/*select=*/true);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=-1 anchor=-1 selection=0 1 2 3", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=0 1 2 3",
+ SelectionStateAsString());
table_->Select(2);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
@@ -1452,7 +1435,7 @@ TEST_P(TableViewTest, SelectionNoSelectOnRemove) {
table_->SetSelectOnRemove(false);
// Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
// Select row 3.
table_->Select(3);
@@ -1464,7 +1447,7 @@ TEST_P(TableViewTest, SelectionNoSelectOnRemove) {
// selected item, so no item is selected.
model_->RemoveRow(3);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
// Select row 1.
table_->Select(1);
@@ -1474,7 +1457,7 @@ TEST_P(TableViewTest, SelectionNoSelectOnRemove) {
// Remove the selected row.
model_->RemoveRow(1);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
// Select row 0.
table_->Select(0);
@@ -1484,7 +1467,7 @@ TEST_P(TableViewTest, SelectionNoSelectOnRemove) {
// Remove the selected row.
model_->RemoveRow(0);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
table_->set_observer(nullptr);
}
@@ -1494,7 +1477,7 @@ TEST_P(TableViewTest, SelectionNoSelectOnRemove) {
// Verifies selection works by way of a gesture.
TEST_P(TableViewTest, SelectOnTap) {
// Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
TableViewObserverImpl observer;
table_->set_observer(&observer);
@@ -1520,11 +1503,7 @@ TEST_P(TableViewTest, KeyUpDown) {
// 3
model_->AddRow(2, 5, 0);
TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(1);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
+ grouper.SetRanges({2, 1, 2});
table_->SetGrouper(&grouper);
TableViewObserverImpl observer;
@@ -1532,7 +1511,7 @@ TEST_P(TableViewTest, KeyUpDown) {
table_->RequestFocus();
// Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
PressKey(ui::VKEY_DOWN);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
@@ -1579,7 +1558,7 @@ TEST_P(TableViewTest, KeyUpDown) {
PressKey(ui::VKEY_UP);
EXPECT_TRUE(table_->header_row_is_active());
EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
PressKey(ui::VKEY_DOWN);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
@@ -1596,8 +1575,8 @@ TEST_P(TableViewTest, KeyUpDown) {
EXPECT_EQ("2 3 4 0 1", GetViewToModelAsString(table_));
- table_->Select(-1);
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ table_->Select(absl::nullopt);
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
observer.GetChangedCountAndClear();
@@ -1606,7 +1585,7 @@ TEST_P(TableViewTest, KeyUpDown) {
PressKey(ui::VKEY_UP);
EXPECT_TRUE(table_->header_row_is_active());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
PressKey(ui::VKEY_DOWN);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
@@ -1653,7 +1632,7 @@ TEST_P(TableViewTest, KeyUpDown) {
PressKey(ui::VKEY_UP);
EXPECT_TRUE(table_->header_row_is_active());
EXPECT_EQ(1, observer.GetChangedCountAndClear());
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
table_->set_observer(nullptr);
}
@@ -1668,89 +1647,89 @@ TEST_P(TableViewTest, KeyLeftRight) {
table_->RequestFocus();
// Initially no active visible column.
- EXPECT_EQ(-1, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_FALSE(helper_->GetActiveVisibleColumnIndex().has_value());
PressKey(ui::VKEY_RIGHT);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(1, observer.GetChangedCountAndClear());
EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
helper_->SetSelectionModel(ui::ListSelectionModel());
- EXPECT_EQ(-1, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_FALSE(helper_->GetActiveVisibleColumnIndex().has_value());
EXPECT_EQ(1, observer.GetChangedCountAndClear());
PressKey(ui::VKEY_LEFT);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(1, observer.GetChangedCountAndClear());
EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
PressKey(ui::VKEY_RIGHT);
- EXPECT_EQ(1, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(1u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
PressKey(ui::VKEY_RIGHT);
- EXPECT_EQ(1, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(1u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
ui::ListSelectionModel new_selection;
new_selection.SetSelectedIndex(1);
helper_->SetSelectionModel(new_selection);
- EXPECT_EQ(1, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(1u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(1, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
PressKey(ui::VKEY_LEFT);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
PressKey(ui::VKEY_LEFT);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
table_->SetColumnVisibility(0, false);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
// Since the first column was hidden, the active visible column should not
// advance.
PressKey(ui::VKEY_RIGHT);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
// If visibility to the first column is restored, the active visible column
// should be unchanged because columns are always added to the end.
table_->SetColumnVisibility(0, true);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
PressKey(ui::VKEY_RIGHT);
- EXPECT_EQ(1, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(1u, helper_->GetActiveVisibleColumnIndex());
// If visibility to the first column is removed, the active visible column
// should be decreased by one.
table_->SetColumnVisibility(0, false);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
PressKey(ui::VKEY_LEFT);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
table_->SetColumnVisibility(0, true);
- EXPECT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
PressKey(ui::VKEY_RIGHT);
- EXPECT_EQ(1, helper_->GetActiveVisibleColumnIndex());
+ EXPECT_EQ(1u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(0, observer.GetChangedCountAndClear());
EXPECT_EQ("active=1 anchor=1 selection=1", SelectionStateAsString());
@@ -1767,8 +1746,7 @@ TEST_P(TableViewTest, HomeEnd) {
// 3
model_->AddRow(2, 5, 0);
TableGrouperImpl grouper;
- std::vector<int> ranges{2, 1, 2};
- grouper.SetRanges(ranges);
+ grouper.SetRanges({2, 1, 2});
table_->SetGrouper(&grouper);
TableViewObserverImpl observer;
@@ -1776,7 +1754,7 @@ TEST_P(TableViewTest, HomeEnd) {
table_->RequestFocus();
// Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
PressKey(ui::VKEY_HOME);
EXPECT_EQ(1, observer.GetChangedCountAndClear());
@@ -1803,15 +1781,11 @@ TEST_P(TableViewTest, Multiselection) {
// 3
model_->AddRow(2, 5, 0);
TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(1);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
+ grouper.SetRanges({2, 1, 2});
table_->SetGrouper(&grouper);
// Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
TableViewObserverImpl observer;
table_->set_observer(&observer);
@@ -1859,11 +1833,7 @@ TEST_P(TableViewTest, MultiselectionWithSort) {
// 3
model_->AddRow(2, 5, 0);
TableGrouperImpl grouper;
- std::vector<int> ranges;
- ranges.push_back(2);
- ranges.push_back(1);
- ranges.push_back(2);
- grouper.SetRanges(ranges);
+ grouper.SetRanges({2, 1, 2});
table_->SetGrouper(&grouper);
// Sort the table descending by column 1, view now looks like:
@@ -1876,7 +1846,7 @@ TEST_P(TableViewTest, MultiselectionWithSort) {
table_->ToggleSortOrder(0);
// Initially no selection.
- EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
+ EXPECT_EQ("active=<none> anchor=<none> selection=", SelectionStateAsString());
TableViewObserverImpl observer;
table_->set_observer(&observer);
@@ -2103,19 +2073,19 @@ TEST_P(TableViewTest, TableHeaderColumnAccessibleViewsFocusable) {
// columns.
PressKey(ui::VKEY_RIGHT);
RunPendingMessages();
- ASSERT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ ASSERT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(helper_->GetVirtualAccessibilityHeaderCell(0),
view_accessibility.FocusedVirtualChild());
PressKey(ui::VKEY_RIGHT);
RunPendingMessages();
- ASSERT_EQ(1, helper_->GetActiveVisibleColumnIndex());
+ ASSERT_EQ(1u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(helper_->GetVirtualAccessibilityHeaderCell(1),
view_accessibility.FocusedVirtualChild());
PressKey(ui::VKEY_LEFT);
RunPendingMessages();
- ASSERT_EQ(0, helper_->GetActiveVisibleColumnIndex());
+ ASSERT_EQ(0u, helper_->GetActiveVisibleColumnIndex());
EXPECT_EQ(helper_->GetVirtualAccessibilityHeaderCell(0),
view_accessibility.FocusedVirtualChild());
}
@@ -2213,7 +2183,6 @@ class TableViewDefaultConstructabilityTest : public ViewsTestBase {
ViewsTestBase::SetUp();
widget_ = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = gfx::Rect(0, 0, 650, 650);
widget_->Init(std::move(params));
widget_->Show();
@@ -2227,14 +2196,13 @@ class TableViewDefaultConstructabilityTest : public ViewsTestBase {
Widget* widget() { return widget_.get(); }
private:
- std::unique_ptr<Widget> widget_;
+ UniqueWidgetPtr widget_;
};
TEST_F(TableViewDefaultConstructabilityTest, TestFunctionalWithoutModel) {
auto scroll_view =
TableView::CreateScrollViewWithTable(std::make_unique<TableView>());
scroll_view->SetBounds(0, 0, 10000, 10000);
- scroll_view->Layout();
- widget()->GetContentsView()->AddChildView(std::move(scroll_view));
+ widget()->client_view()->AddChildView(std::move(scroll_view));
}
} // namespace views
diff --git a/chromium/ui/views/controls/table/test_table_model.cc b/chromium/ui/views/controls/table/test_table_model.cc
index 99642e30e0a..d9b9e186c9b 100644
--- a/chromium/ui/views/controls/table/test_table_model.cc
+++ b/chromium/ui/views/controls/table/test_table_model.cc
@@ -11,21 +11,21 @@
#include "ui/base/models/table_model_observer.h"
#include "ui/gfx/image/image_skia.h"
-TestTableModel::TestTableModel(int row_count)
+TestTableModel::TestTableModel(size_t row_count)
: row_count_(row_count), observer_(nullptr) {}
TestTableModel::~TestTableModel() = default;
-int TestTableModel::RowCount() {
+size_t TestTableModel::RowCount() {
return row_count_;
}
-std::u16string TestTableModel::GetText(int row, int column_id) {
+std::u16string TestTableModel::GetText(size_t row, int column_id) {
return base::ASCIIToUTF16(base::NumberToString(row) + "x" +
base::NumberToString(column_id));
}
-ui::ImageModel TestTableModel::GetIcon(int row) {
+ui::ImageModel TestTableModel::GetIcon(size_t row) {
SkBitmap bitmap;
bitmap.setInfo(SkImageInfo::MakeN32Premul(16, 16));
return ui::ImageModel::FromImageSkia(
diff --git a/chromium/ui/views/controls/table/test_table_model.h b/chromium/ui/views/controls/table/test_table_model.h
index 614489801ef..a3883aa3f79 100644
--- a/chromium/ui/views/controls/table/test_table_model.h
+++ b/chromium/ui/views/controls/table/test_table_model.h
@@ -10,7 +10,7 @@
class TestTableModel : public ui::TableModel {
public:
- explicit TestTableModel(int row_count);
+ explicit TestTableModel(size_t row_count);
TestTableModel(const TestTableModel&) = delete;
TestTableModel& operator=(const TestTableModel&) = delete;
@@ -18,13 +18,13 @@ class TestTableModel : public ui::TableModel {
~TestTableModel() override;
// ui::TableModel overrides:
- int RowCount() override;
- std::u16string GetText(int row, int column_id) override;
- ui::ImageModel GetIcon(int row) override;
+ size_t RowCount() override;
+ std::u16string GetText(size_t row, int column_id) override;
+ ui::ImageModel GetIcon(size_t row) override;
void SetObserver(ui::TableModelObserver* observer) override;
private:
- int row_count_;
+ size_t row_count_;
raw_ptr<ui::TableModelObserver> observer_;
};
diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc
index 4699148d8f8..a0a5dd7f469 100644
--- a/chromium/ui/views/controls/textfield/textfield.cc
+++ b/chromium/ui/views/controls/textfield/textfield.cc
@@ -70,11 +70,9 @@
#include "base/win/win_util.h"
#endif
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX)
#include "ui/base/ime/linux/text_edit_command_auralinux.h"
-#include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"
+#include "ui/linux/linux_ui.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -270,15 +268,6 @@ Textfield::~Textfield() {
void Textfield::SetAssociatedLabel(View* labelling_view) {
DCHECK(labelling_view);
GetViewAccessibility().OverrideLabelledBy(labelling_view);
- ui::AXNodeData node_data;
- labelling_view->GetAccessibleNodeData(&node_data);
- // Labelled-by relations are not common practice in native UI, so we also
- // set the accessible name for ATs which don't support that.
- // TODO(aleventhal) automatically handle setting the name from the related
- // label in ViewAccessibility and have it update the name if the text of the
- // associated label changes.
- SetAccessibleName(
- node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
}
void Textfield::SetController(TextfieldController* controller) {
@@ -717,13 +706,11 @@ bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
if (!textfield)
return handled;
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
- ui::TextEditKeyBindingsDelegateAuraLinux* delegate =
- ui::GetTextEditKeyBindingsDelegate();
+#if BUILDFLAG(IS_LINUX)
+ auto* linux_ui = ui::LinuxUi::instance();
std::vector<ui::TextEditCommandAuraLinux> commands;
- if (!handled && delegate && delegate->MatchEvent(event, &commands)) {
+ if (!handled && linux_ui &&
+ linux_ui->GetTextEditCommandsForEvent(event, &commands)) {
for (const auto& command : commands) {
if (IsTextEditCommandEnabled(command.command())) {
ExecuteTextEditCommand(command.command());
@@ -863,14 +850,11 @@ void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
}
bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX)
// Skip any accelerator handling that conflicts with custom keybindings.
- ui::TextEditKeyBindingsDelegateAuraLinux* delegate =
- ui::GetTextEditKeyBindingsDelegate();
+ auto* linux_ui = ui::LinuxUi::instance();
std::vector<ui::TextEditCommandAuraLinux> commands;
- if (delegate && delegate->MatchEvent(event, &commands)) {
+ if (linux_ui && linux_ui->GetTextEditCommandsForEvent(event, &commands)) {
const auto is_enabled = [this](const auto& command) {
return IsTextEditCommandEnabled(command.command());
};
@@ -987,14 +971,16 @@ void Textfield::GetAccessibleNodeData(ui::AXNodeData* node_data) {
const gfx::Range range = GetSelectedRange();
node_data->AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart,
- range.start());
- node_data->AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, range.end());
+ base::checked_cast<int32_t>(range.start()));
+ node_data->AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd,
+ base::checked_cast<int32_t>(range.end()));
}
bool Textfield::HandleAccessibleAction(const ui::AXActionData& action_data) {
if (action_data.action == ax::mojom::Action::kSetSelection) {
DCHECK_EQ(action_data.anchor_node_id, action_data.focus_node_id);
- const gfx::Range range(action_data.anchor_offset, action_data.focus_offset);
+ const gfx::Range range(static_cast<size_t>(action_data.anchor_offset),
+ static_cast<size_t>(action_data.focus_offset));
return SetEditableSelectionRange(range);
}
@@ -1358,7 +1344,7 @@ void Textfield::SetCompositionText(const ui::CompositionText& composition) {
OnAfterUserAction();
}
-uint32_t Textfield::ConfirmCompositionText(bool keep_selection) {
+size_t Textfield::ConfirmCompositionText(bool keep_selection) {
// TODO(b/134473433) Modify this function so that when keep_selection is
// true, the selection is not changed when text committed
if (keep_selection) {
@@ -1368,7 +1354,7 @@ uint32_t Textfield::ConfirmCompositionText(bool keep_selection) {
return 0;
OnBeforeUserAction();
skip_input_method_cancel_composition_ = true;
- const uint32_t confirmed_text_length = model_->ConfirmCompositionText();
+ const size_t confirmed_text_length = model_->ConfirmCompositionText();
skip_input_method_cancel_composition_ = false;
UpdateAfterChange(TextChangeType::kUserTriggered, true);
OnAfterUserAction();
@@ -1426,7 +1412,7 @@ void Textfield::InsertChar(const ui::KeyEvent& event) {
DoInsertChar(ch);
if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) {
- password_char_reveal_index_ = -1;
+ password_char_reveal_index_ = absl::nullopt;
base::TimeDelta duration = GetPasswordRevealDuration(event);
if (!duration.is_zero()) {
const size_t change_offset = model_->GetCursorPosition();
@@ -1473,7 +1459,7 @@ gfx::Rect Textfield::GetSelectionBoundingBox() const {
return gfx::Rect();
}
-bool Textfield::GetCompositionCharacterBounds(uint32_t index,
+bool Textfield::GetCompositionCharacterBounds(size_t index,
gfx::Rect* rect) const {
DCHECK(rect);
if (!HasCompositionText())
@@ -1593,7 +1579,14 @@ bool Textfield::ChangeTextDirectionAndLayoutAlignment(
void Textfield::ExtendSelectionAndDelete(size_t before, size_t after) {
gfx::Range range = GetRenderText()->selection();
- DCHECK_GE(range.start(), before);
+ // Discard out-of-bound operations.
+ // TODO(crbug.com/1344096): this is a temporary fix to prevent the
+ // checked_cast failure in gfx::Range. There does not seem to be any
+ // observable bad behaviors before checked_cast was added. However, range
+ // clipping or dropping should be the last resort because a checkfail
+ // indicates that we run into bad states somewhere earlier on the stack.
+ if (range.start() < before)
+ return;
range.set_start(range.start() - before);
range.set_end(range.end() + after);
@@ -2060,7 +2053,7 @@ Textfield::EditCommandResult Textfield::DoExecuteTextEditCommand(
return {changed, cursor_changed};
}
-void Textfield::OffsetDoubleClickWord(int offset) {
+void Textfield::OffsetDoubleClickWord(size_t offset) {
selection_controller_.OffsetDoubleClickWord(offset);
}
@@ -2562,17 +2555,18 @@ bool Textfield::ImeEditingAllowed() const {
return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD);
}
-void Textfield::RevealPasswordChar(int index, base::TimeDelta duration) {
+void Textfield::RevealPasswordChar(absl::optional<size_t> index,
+ base::TimeDelta duration) {
GetRenderText()->SetObscuredRevealIndex(index);
SchedulePaint();
password_char_reveal_index_ = index;
UpdateCursorViewPosition();
- if (index != -1) {
- password_reveal_timer_.Start(
- FROM_HERE, duration,
- base::BindOnce(&Textfield::RevealPasswordChar,
- weak_ptr_factory_.GetWeakPtr(), -1, duration));
+ if (index.has_value()) {
+ password_reveal_timer_.Start(FROM_HERE, duration,
+ base::BindOnce(&Textfield::RevealPasswordChar,
+ weak_ptr_factory_.GetWeakPtr(),
+ absl::nullopt, duration));
}
}
diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h
index ea29dbdf334..9768a28f0e9 100644
--- a/chromium/ui/views/controls/textfield/textfield.h
+++ b/chromium/ui/views/controls/textfield/textfield.h
@@ -313,7 +313,9 @@ class VIEWS_EXPORT Textfield : public View,
// Set extra spacing placed between glyphs; used for obscured text styling.
void SetObscuredGlyphSpacing(int spacing);
- int GetPasswordCharRevealIndex() const { return password_char_reveal_index_; }
+ absl::optional<size_t> GetPasswordCharRevealIndex() const {
+ return password_char_reveal_index_;
+ }
void SetExtraInsets(const gfx::Insets& insets);
@@ -407,7 +409,7 @@ class VIEWS_EXPORT Textfield : public View,
// ui::TextInputClient overrides:
void SetCompositionText(const ui::CompositionText& composition) override;
- uint32_t ConfirmCompositionText(bool keep_selection) override;
+ size_t ConfirmCompositionText(bool keep_selection) override;
void ClearCompositionText() override;
void InsertText(const std::u16string& text,
InsertTextCursorBehavior cursor_behavior) override;
@@ -419,7 +421,7 @@ class VIEWS_EXPORT Textfield : public View,
bool CanComposeInline() const override;
gfx::Rect GetCaretBounds() const override;
gfx::Rect GetSelectionBoundingBox() const override;
- bool GetCompositionCharacterBounds(uint32_t index,
+ bool GetCompositionCharacterBounds(size_t index,
gfx::Rect* rect) const override;
bool HasCompositionText() const override;
FocusReason GetFocusReason() const override;
@@ -427,7 +429,11 @@ class VIEWS_EXPORT Textfield : public View,
bool GetCompositionTextRange(gfx::Range* range) const override;
bool GetEditableSelectionRange(gfx::Range* range) const override;
bool SetEditableSelectionRange(const gfx::Range& range) override;
+#if BUILDFLAG(IS_MAC)
bool DeleteRange(const gfx::Range& range) override;
+#else
+ bool DeleteRange(const gfx::Range& range);
+#endif
bool GetTextFromRange(const gfx::Range& range,
std::u16string* text) const override;
void OnInputMethodChanged() override;
@@ -492,7 +498,7 @@ class VIEWS_EXPORT Textfield : public View,
// Offsets the double-clicked word's range. This is only used in the unusual
// case where the text changes on the second mousedown of a double-click.
// This is harmless if there is not a currently double-clicked word.
- void OffsetDoubleClickWord(int offset);
+ void OffsetDoubleClickWord(size_t offset);
// Returns true if the drop cursor is for insertion at a target text location,
// the standard behavior/style. Returns false when drop will do something
@@ -615,9 +621,10 @@ class VIEWS_EXPORT Textfield : public View,
bool ImeEditingAllowed() const;
// Reveals the password character at |index| for a set duration.
- // If |index| is -1, the existing revealed character will be reset.
+ // If |index| is nullopt, the existing revealed character will be reset.
// |duration| is the time to remain the password char to be visible.
- void RevealPasswordChar(int index, base::TimeDelta duration);
+ void RevealPasswordChar(absl::optional<size_t> index,
+ base::TimeDelta duration);
void CreateTouchSelectionControllerAndNotifyIt();
@@ -772,7 +779,7 @@ class VIEWS_EXPORT Textfield : public View,
ui::TextInputClient::FOCUS_REASON_NONE;
// The password char reveal index, for testing only.
- int password_char_reveal_index_ = -1;
+ absl::optional<size_t> password_char_reveal_index_;
// Extra insets, useful to make room for a button for example.
gfx::Insets extra_insets_ = gfx::Insets();
diff --git a/chromium/ui/views/controls/textfield/textfield_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc
index b3202c3e087..8c67d53d205 100644
--- a/chromium/ui/views/controls/textfield/textfield_model.cc
+++ b/chromium/ui/views/controls/textfield/textfield_model.cc
@@ -364,8 +364,8 @@ void SelectRangeInCompositionText(gfx::RenderText* render_text,
const gfx::Range& range) {
DCHECK(render_text);
DCHECK(range.IsValid());
- uint32_t start = range.GetMin();
- uint32_t end = range.GetMax();
+ size_t start = range.GetMin();
+ size_t end = range.GetMax();
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Swap |start| and |end| so that GetCaretBounds() can always return the same
// value during conversion.
@@ -783,11 +783,11 @@ void TextfieldModel::SetCompositionFromExistingText(const gfx::Range& range) {
render_text_->SetCompositionRange(range);
}
-uint32_t TextfieldModel::ConfirmCompositionText() {
+size_t TextfieldModel::ConfirmCompositionText() {
DCHECK(HasCompositionText());
std::u16string composition =
text().substr(composition_range_.start(), composition_range_.length());
- uint32_t composition_length = composition_range_.length();
+ size_t composition_length = composition_range_.length();
// TODO(oshima): current behavior on ChromeOS is a bit weird and not
// sure exactly how this should work. Find out and fix if necessary.
AddOrMergeEditHistory(std::make_unique<internal::InsertEdit>(
diff --git a/chromium/ui/views/controls/textfield/textfield_model.h b/chromium/ui/views/controls/textfield/textfield_model.h
index 01452147fbb..614cb0b121c 100644
--- a/chromium/ui/views/controls/textfield/textfield_model.h
+++ b/chromium/ui/views/controls/textfield/textfield_model.h
@@ -257,7 +257,7 @@ class VIEWS_EXPORT TextfieldModel {
// Converts current composition text into final content and returns the
// length of the text committed.
- uint32_t ConfirmCompositionText();
+ size_t ConfirmCompositionText();
// Removes current composition text.
void CancelCompositionText();
diff --git a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
index b87b93f5146..809201cd843 100644
--- a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
+++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc
@@ -1947,8 +1947,8 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) {
EXPECT_EQ(u"ABCDEabc", model.text());
// Confirm the composition.
- uint32_t composition_text_length = model.ConfirmCompositionText();
- EXPECT_EQ(composition_text_length, static_cast<uint32_t>(3));
+ size_t composition_text_length = model.ConfirmCompositionText();
+ EXPECT_EQ(composition_text_length, 3u);
EXPECT_EQ(u"ABCDEabc", model.text());
EXPECT_TRUE(model.Undo());
EXPECT_EQ(u"ABCDE", model.text());
diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc
index 81d16dcdfd8..3f77efc92df 100644
--- a/chromium/ui/views/controls/textfield/textfield_unittest.cc
+++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc
@@ -28,9 +28,9 @@
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/ime/constants.h"
+#include "ui/base/ime/ime_key_event_dispatcher.h"
#include "ui/base/ime/init/input_method_factory.h"
#include "ui/base/ime/input_method_base.h"
-#include "ui/base/ime/input_method_delegate.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/l10n/l10n_util.h"
@@ -62,10 +62,9 @@
#include "base/win/windows_version.h"
#endif
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"
+#if BUILDFLAG(IS_LINUX)
+#include "ui/linux/fake_linux_ui.h"
+#include "ui/linux/linux_ui.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -88,14 +87,10 @@ namespace test {
const char16_t kHebrewLetterSamekh = 0x05E1;
// Convenience to make constructing a GestureEvent simpler.
-class GestureEventForTest : public ui::GestureEvent {
- public:
- GestureEventForTest(int x, int y, ui::GestureEventDetails details)
- : GestureEvent(x, y, ui::EF_NONE, base::TimeTicks(), details) {}
-
- GestureEventForTest(const GestureEventForTest&) = delete;
- GestureEventForTest& operator=(const GestureEventForTest&) = delete;
-};
+ui::GestureEvent
+CreateTestGestureEvent(int x, int y, const ui::GestureEventDetails& details) {
+ return ui::GestureEvent(x, y, ui::EF_NONE, base::TimeTicks(), details);
+}
// This controller will happily destroy the target field passed on
// construction when a key event is triggered.
@@ -167,6 +162,16 @@ class MockInputMethod : public ui::InputMethodBase {
count_show_virtual_keyboard_++;
}
+#if BUILDFLAG(IS_WIN)
+ bool OnUntranslatedIMEMessage(
+ const CHROME_MSG event,
+ InputMethod::NativeEventResult* result) override {
+ return false;
+ }
+ void OnInputLocaleChanged() override {}
+ bool IsInputLocaleCJK() const override { return false; }
+#endif
+
bool untranslated_ime_message_called() const {
return untranslated_ime_message_called_;
}
@@ -473,8 +478,8 @@ void TextfieldTest::PrepareTextfieldsInternal(int count,
Textfield* textfield,
View* container,
gfx::Rect bounds) {
- input_method_->SetDelegate(
- test::WidgetTest::GetInputMethodDelegateForWidget(widget_.get()));
+ input_method_->SetImeKeyEventDispatcher(
+ test::WidgetTest::GetImeKeyEventDispatcherForWidget(widget_.get()));
textfield->set_controller(this);
textfield->SetBoundsRect(bounds);
@@ -806,12 +811,14 @@ void TextfieldTest::MoveMouseTo(const gfx::Point& where) {
void TextfieldTest::TapAtCursor(ui::EventPointerType pointer_type) {
ui::GestureEventDetails tap_down_details(ui::ET_GESTURE_TAP_DOWN);
tap_down_details.set_primary_pointer_type(pointer_type);
- GestureEventForTest tap_down(GetCursorPositionX(0), 0, tap_down_details);
+ ui::GestureEvent tap_down =
+ CreateTestGestureEvent(GetCursorPositionX(0), 0, tap_down_details);
textfield_->OnGestureEvent(&tap_down);
ui::GestureEventDetails tap_up_details(ui::ET_GESTURE_TAP);
tap_up_details.set_primary_pointer_type(pointer_type);
- GestureEventForTest tap_up(GetCursorPositionX(0), 0, tap_up_details);
+ ui::GestureEvent tap_up =
+ CreateTestGestureEvent(GetCursorPositionX(0), 0, tap_up_details);
textfield_->OnGestureEvent(&tap_up);
}
@@ -1473,15 +1480,15 @@ TEST_F(TextfieldTest, TextInputType_InsertionTest) {
EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType());
SendKeyEvent(ui::VKEY_A);
- EXPECT_EQ(-1, textfield_->GetPasswordCharRevealIndex());
+ EXPECT_FALSE(textfield_->GetPasswordCharRevealIndex().has_value());
SendKeyEvent(kHebrewLetterSamekh, ui::EF_NONE, true /* from_vk */);
#if !BUILDFLAG(IS_MAC)
// Don't verifies the password character reveal on MacOS, because on MacOS,
// the text insertion is not done through TextInputClient::InsertChar().
- EXPECT_EQ(1, textfield_->GetPasswordCharRevealIndex());
+ EXPECT_EQ(1u, textfield_->GetPasswordCharRevealIndex());
#endif
SendKeyEvent(ui::VKEY_B);
- EXPECT_EQ(-1, textfield_->GetPasswordCharRevealIndex());
+ EXPECT_FALSE(textfield_->GetPasswordCharRevealIndex().has_value());
EXPECT_EQ(
u"a\x05E1"
@@ -1579,11 +1586,9 @@ TEST_F(TextfieldTest, OnKeyPress) {
TEST_F(TextfieldTest, OnKeyPressBinding) {
InitTextfield();
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX)
// Install a TextEditKeyBindingsDelegateAuraLinux that does nothing.
- class TestDelegate : public ui::TextEditKeyBindingsDelegateAuraLinux {
+ class TestDelegate : public ui::FakeLinuxUi {
public:
TestDelegate() = default;
@@ -1592,15 +1597,14 @@ TEST_F(TextfieldTest, OnKeyPressBinding) {
~TestDelegate() override = default;
- bool MatchEvent(
+ bool GetTextEditCommandsForEvent(
const ui::Event& event,
std::vector<ui::TextEditCommandAuraLinux>* commands) override {
return false;
}
};
- TestDelegate delegate;
- ui::SetTextEditKeyBindingsDelegate(&delegate);
+ ui::LinuxUi::SetInstance(std::make_unique<TestDelegate>());
#endif
SendKeyEvent(ui::VKEY_A, false, false);
@@ -1620,10 +1624,8 @@ TEST_F(TextfieldTest, OnKeyPressBinding) {
EXPECT_EQ(u"a", textfield_->GetText());
textfield_->clear();
-// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
-// of lacros-chrome is complete.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
- ui::SetTextEditKeyBindingsDelegate(nullptr);
+#if BUILDFLAG(IS_LINUX)
+ ui::LinuxUi::SetInstance(nullptr);
#endif
}
@@ -2321,8 +2323,10 @@ TEST_F(TextfieldTest, TextInputClientTest) {
EXPECT_TRUE(client->GetTextFromRange(range, &substring));
EXPECT_EQ(u"123", substring);
+#if BUILDFLAG(IS_MAC)
EXPECT_TRUE(client->DeleteRange(range));
EXPECT_EQ(u"0456789", textfield_->GetText());
+#endif
ui::CompositionText composition;
composition.text = u"321";
@@ -3062,10 +3066,10 @@ TEST_F(TextfieldTest, CommitComposingTextTest) {
ui::CompositionText composition;
composition.text = u"abc123";
textfield_->SetCompositionText(composition);
- uint32_t composed_text_length =
+ size_t composed_text_length =
textfield_->ConfirmCompositionText(/* keep_selection */ false);
- EXPECT_EQ(composed_text_length, static_cast<uint32_t>(6));
+ EXPECT_EQ(composed_text_length, 6u);
}
TEST_F(TextfieldTest, CommitEmptyComposingTextTest) {
@@ -3073,10 +3077,10 @@ TEST_F(TextfieldTest, CommitEmptyComposingTextTest) {
ui::CompositionText composition;
composition.text = u"";
textfield_->SetCompositionText(composition);
- uint32_t composed_text_length =
+ size_t composed_text_length =
textfield_->ConfirmCompositionText(/* keep_selection */ false);
- EXPECT_EQ(composed_text_length, static_cast<uint32_t>(0));
+ EXPECT_EQ(composed_text_length, 0u);
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
@@ -3206,8 +3210,7 @@ TEST_F(TextfieldTest, GetAutocorrectCharacterBoundsTest) {
gfx::Rect rect_for_long_text = textfield_->GetAutocorrectCharacterBounds();
- // Clear the text
- textfield_->DeleteRange(gfx::Range(0, 99));
+ textfield_->clear();
textfield_->InsertText(
u"hello placeholder text",
@@ -3457,7 +3460,7 @@ TEST_F(TextfieldTest, TestLongPressInitiatesDragDrop) {
switches::kEnableTouchDragDrop);
// Create a long press event in the selected region should start a drag.
- GestureEventForTest long_press(
+ ui::GestureEvent long_press = CreateTestGestureEvent(
kStringPoint.x(), kStringPoint.y(),
ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
textfield_->OnGestureEvent(&long_press);
@@ -3541,7 +3544,7 @@ TEST_F(TextfieldTest, VirtualKeyboardFocusEnsureCaretNotInRect) {
EXPECT_EQ(widget_->GetNativeView()->bounds(), orig_widget_bounds);
// Simulate virtual keyboard.
- input_method_->SetOnScreenKeyboardBounds(keyboard_view_bounds);
+ input_method_->SetVirtualKeyboardBounds(keyboard_view_bounds);
// Window should be shifted.
EXPECT_EQ(widget_->GetNativeView()->bounds(), shifted_widget_bounds);
@@ -3560,26 +3563,27 @@ class TextfieldTouchSelectionTest : public TextfieldTest {
protected:
// Simulates a complete tap.
void Tap(const gfx::Point& point) {
- GestureEventForTest begin(point.x(), point.y(),
- ui::GestureEventDetails(ui::ET_GESTURE_BEGIN));
+ ui::GestureEvent begin = CreateTestGestureEvent(
+ point.x(), point.y(), ui::GestureEventDetails(ui::ET_GESTURE_BEGIN));
textfield_->OnGestureEvent(&begin);
- GestureEventForTest tap_down(
+ ui::GestureEvent tap_down = CreateTestGestureEvent(
point.x(), point.y(), ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
textfield_->OnGestureEvent(&tap_down);
- GestureEventForTest show_press(
+ ui::GestureEvent show_press = CreateTestGestureEvent(
point.x(), point.y(),
ui::GestureEventDetails(ui::ET_GESTURE_SHOW_PRESS));
textfield_->OnGestureEvent(&show_press);
ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP);
tap_details.set_tap_count(1);
- GestureEventForTest tap(point.x(), point.y(), tap_details);
+ ui::GestureEvent tap =
+ CreateTestGestureEvent(point.x(), point.y(), tap_details);
textfield_->OnGestureEvent(&tap);
- GestureEventForTest end(point.x(), point.y(),
- ui::GestureEventDetails(ui::ET_GESTURE_END));
+ ui::GestureEvent end = CreateTestGestureEvent(
+ point.x(), point.y(), ui::GestureEventDetails(ui::ET_GESTURE_END));
textfield_->OnGestureEvent(&end);
}
};
@@ -3595,7 +3599,7 @@ TEST_F(TextfieldTouchSelectionTest, TouchSelectionAndDraggingTest) {
// Tapping on the textfield should turn on the TouchSelectionController.
ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP);
tap_details.set_tap_count(1);
- GestureEventForTest tap(x, 0, tap_details);
+ ui::GestureEvent tap = CreateTestGestureEvent(x, 0, tap_details);
textfield_->OnGestureEvent(&tap);
EXPECT_TRUE(test_api_->touch_selection_controller());
@@ -3606,7 +3610,7 @@ TEST_F(TextfieldTouchSelectionTest, TouchSelectionAndDraggingTest) {
// With touch editing enabled, long press should not show context menu.
// Instead, select word and invoke TouchSelectionController.
- GestureEventForTest long_press_1(
+ ui::GestureEvent long_press_1 = CreateTestGestureEvent(
x, 0, ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
textfield_->OnGestureEvent(&long_press_1);
EXPECT_EQ(u"hello", textfield_->GetSelectedText());
@@ -3616,7 +3620,7 @@ TEST_F(TextfieldTouchSelectionTest, TouchSelectionAndDraggingTest) {
// With touch drag drop enabled, long pressing in the selected region should
// start a drag and remove TouchSelectionController.
ASSERT_TRUE(switches::IsTouchDragDropEnabled());
- GestureEventForTest long_press_2(
+ ui::GestureEvent long_press_2 = CreateTestGestureEvent(
x, 0, ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
textfield_->OnGestureEvent(&long_press_2);
EXPECT_EQ(u"hello", textfield_->GetSelectedText());
@@ -3628,7 +3632,7 @@ TEST_F(TextfieldTouchSelectionTest, TouchSelectionAndDraggingTest) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableTouchDragDrop);
ASSERT_FALSE(switches::IsTouchDragDropEnabled());
- GestureEventForTest long_press_3(
+ ui::GestureEvent long_press_3 = CreateTestGestureEvent(
x, 0, ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
textfield_->OnGestureEvent(&long_press_3);
EXPECT_EQ(u"hello", textfield_->GetSelectedText());
@@ -3878,7 +3882,7 @@ TEST_F(TextfieldTest, EmojiItem_EmptyField) {
// A normal empty field may show the Emoji option (if supported).
ui::MenuModel* context_menu = GetContextMenuModel();
EXPECT_TRUE(context_menu);
- EXPECT_GT(context_menu->GetItemCount(), 0);
+ EXPECT_GT(context_menu->GetItemCount(), 0u);
// Not all OS/versions support the emoji menu.
EXPECT_EQ(ui::IsEmojiPanelSupported(),
context_menu->GetLabelAt(0) ==
@@ -3893,7 +3897,7 @@ TEST_F(TextfieldTest, EmojiItem_ReadonlyField) {
// In no case is the emoji option showing on a read-only field.
ui::MenuModel* context_menu = GetContextMenuModel();
EXPECT_TRUE(context_menu);
- EXPECT_GT(context_menu->GetItemCount(), 0);
+ EXPECT_GT(context_menu->GetItemCount(), 0u);
EXPECT_NE(context_menu->GetLabelAt(0),
l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_EMOJI));
}
@@ -3915,7 +3919,7 @@ TEST_F(TextfieldTest, EmojiItem_FieldWithText) {
textfield_->SelectAll(false);
ui::MenuModel* context_menu = GetContextMenuModel();
EXPECT_TRUE(context_menu);
- EXPECT_GT(context_menu->GetItemCount(), 0);
+ EXPECT_GT(context_menu->GetItemCount(), 0u);
// Not all OS/versions support the emoji menu.
EXPECT_EQ(ui::IsEmojiPanelSupported(),
context_menu->GetLabelAt(kExpectedEmojiIndex) ==
@@ -3966,7 +3970,7 @@ TEST_F(TextfieldTest, LookUpPassword) {
ui::MenuModel* context_menu = GetContextMenuModel();
EXPECT_TRUE(context_menu);
- EXPECT_GT(context_menu->GetItemCount(), 0);
+ EXPECT_GT(context_menu->GetItemCount(), 0u);
EXPECT_NE(context_menu->GetCommandIdAt(0), IDS_CONTENT_CONTEXT_LOOK_UP);
EXPECT_NE(context_menu->GetLabelAt(0),
l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, kText));
diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc
index 27011fc5fdd..e9bbeed02f4 100644
--- a/chromium/ui/views/controls/tree/tree_view.cc
+++ b/chromium/ui/views/controls/tree/tree_view.cc
@@ -204,6 +204,10 @@ void TreeView::StartEditing(TreeModelNode* node) {
editor_->set_controller(this);
}
editor_->SetText(selected_node_->model_node()->GetTitle());
+ // TODO(crbug.com/1345828): Investigate whether accessible name should stay in
+ // sync during editing.
+ editor_->SetAccessibleName(
+ selected_node_->model_node()->GetAccessibleTitle());
LayoutEditor();
editor_->SetVisible(true);
SchedulePaintForNode(selected_node_);
@@ -245,6 +249,7 @@ void TreeView::CommitEdit() {
DCHECK(selected_node_);
const bool editor_has_focus = editor_->HasFocus();
model_->SetTitle(GetSelectedNode(), editor_->GetText());
+ editor_->SetAccessibleName(GetSelectedNode()->GetAccessibleTitle());
CancelEdit();
if (editor_has_focus)
RequestFocus();
@@ -646,30 +651,35 @@ void TreeView::OnDidChangeFocus(View* focused_before, View* focused_now) {
CommitEdit();
}
-int TreeView::GetRowCount() {
- int row_count = root_.NumExpandedNodes();
+size_t TreeView::GetRowCount() {
+ size_t row_count = root_.NumExpandedNodes();
if (!root_shown_)
row_count--;
return row_count;
}
-int TreeView::GetSelectedRow() {
+absl::optional<size_t> TreeView::GetSelectedRow() {
// Type-ahead searches should be relative to the active node, so return the
// row of the active node for |PrefixSelector|.
ui::TreeModelNode* model_node = GetActiveNode();
- return model_node ? GetRowForNode(model_node) : -1;
+ if (!model_node)
+ return absl::nullopt;
+ const int row = GetRowForNode(model_node);
+ return (row == -1) ? absl::nullopt
+ : absl::make_optional(static_cast<size_t>(row));
}
-void TreeView::SetSelectedRow(int row) {
+void TreeView::SetSelectedRow(absl::optional<size_t> row) {
// Type-ahead manipulates selection because active node is synced to selected
// node, so call SetSelectedNode() instead of SetActiveNode().
// TODO(crbug.com/1080944): Decouple active node from selected node by adding
// new keyboard affordances.
- SetSelectedNode(GetNodeForRow(row));
+ SetSelectedNode(
+ GetNodeForRow(row.has_value() ? static_cast<int>(row.value()) : -1));
}
-std::u16string TreeView::GetTextForRow(int row) {
- return GetNodeForRow(row)->GetTitle();
+std::u16string TreeView::GetTextForRow(size_t row) {
+ return GetNodeForRow(static_cast<int>(row))->GetTitle();
}
gfx::Point TreeView::GetKeyboardContextMenuLocation() {
@@ -966,12 +976,12 @@ void TreeView::PopulateAccessibilityData(InternalNode* node,
// Per the ARIA Spec, aria-posinset and aria-setsize are 1-based
// not 0-based.
- int pos_in_parent = node->parent()->GetIndexOf(node) + 1;
- int sibling_size = static_cast<int>(node->parent()->children().size());
+ size_t pos_in_parent = node->parent()->GetIndexOf(node).value() + 1;
+ size_t sibling_size = node->parent()->children().size();
data->AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
- int32_t{pos_in_parent});
+ static_cast<int32_t>(pos_in_parent));
data->AddIntAttribute(ax::mojom::IntAttribute::kSetSize,
- int32_t{sibling_size});
+ static_cast<int32_t>(sibling_size));
}
int ignored_depth;
@@ -1003,7 +1013,7 @@ void TreeView::UpdatePreferredSize() {
preferred_size_.SetSize(
root_.GetMaxWidth(this, text_offset_, root_shown_ ? 1 : 0) +
kTextHorizontalPadding * 2,
- row_height_ * GetRowCount());
+ row_height_ * base::checked_cast<int>(GetRowCount()));
// When the editor is visible, more space is needed beyond the regular row,
// such as for drawing the focus ring.
@@ -1173,9 +1183,9 @@ void TreeView::PaintExpandControl(gfx::Canvas* canvas,
void TreeView::PaintNodeIcon(gfx::Canvas* canvas,
InternalNode* node,
const gfx::Rect& bounds) {
- int icon_index = model_->GetIconIndex(node->model_node());
+ absl::optional<size_t> icon_index = model_->GetIconIndex(node->model_node());
int icon_x = kArrowRegionSize + kImagePadding;
- if (icon_index == -1) {
+ if (!icon_index.has_value()) {
// Flip just the |bounds| region of |canvas|.
gfx::ScopedCanvas scoped_canvas(canvas);
canvas->Translate(gfx::Vector2d(bounds.x(), 0));
@@ -1188,7 +1198,7 @@ void TreeView::PaintNodeIcon(gfx::Canvas* canvas,
gfx::Rect(0, bounds.y(), bounds.width(), bounds.height()));
} else {
const gfx::ImageSkia& icon =
- icons_[icon_index].Rasterize(GetColorProvider());
+ icons_[icon_index.value()].Rasterize(GetColorProvider());
icon_x += (open_icon_.Size().width() - icon.width()) / 2;
if (base::i18n::IsRTL())
icon_x = bounds.width() - icon_x - icon.width();
@@ -1211,7 +1221,8 @@ TreeView::InternalNode* TreeView::GetInternalNodeForModelNode(
LoadChildren(parent_internal_node);
}
size_t index =
- model_->GetIndexOf(parent_internal_node->model_node(), model_node);
+ model_->GetIndexOf(parent_internal_node->model_node(), model_node)
+ .value();
return parent_internal_node->children()[index].get();
}
@@ -1297,11 +1308,13 @@ int TreeView::GetRowForInternalNode(InternalNode* node, int* depth) {
int row = -1;
const InternalNode* tmp_node = node;
while (tmp_node->parent()) {
- size_t index_in_parent = tmp_node->parent()->GetIndexOf(tmp_node);
+ size_t index_in_parent = tmp_node->parent()->GetIndexOf(tmp_node).value();
(*depth)++;
row++; // For node.
- for (size_t i = 0; i < index_in_parent; ++i)
- row += tmp_node->parent()->children()[i]->NumExpandedNodes();
+ for (size_t i = 0; i < index_in_parent; ++i) {
+ row += static_cast<int>(
+ tmp_node->parent()->children()[i]->NumExpandedNodes());
+ }
tmp_node = tmp_node->parent();
}
if (root_shown_) {
@@ -1363,10 +1376,11 @@ void TreeView::IncrementSelection(IncrementType type) {
if (root_.children().empty())
return;
if (type == IncrementType::kPrevious) {
- int row_count = GetRowCount();
+ size_t row_count = GetRowCount();
int depth = 0;
DCHECK(row_count);
- InternalNode* node = GetNodeByRow(row_count - 1, &depth);
+ InternalNode* node =
+ GetNodeByRow(static_cast<int>(row_count - 1), &depth);
SetSelectedNode(node->model_node());
} else if (root_shown_) {
SetSelectedNode(root_.model_node());
@@ -1379,7 +1393,8 @@ void TreeView::IncrementSelection(IncrementType type) {
int depth = 0;
int delta = type == IncrementType::kPrevious ? -1 : 1;
int row = GetRowForInternalNode(active_node_, &depth);
- int new_row = base::clamp(row + delta, 0, GetRowCount() - 1);
+ int new_row =
+ base::clamp(row + delta, 0, base::checked_cast<int>(GetRowCount()) - 1);
if (new_row == row)
return; // At the end/beginning.
SetSelectedNode(GetNodeByRow(new_row, &depth)->model_node());
@@ -1472,8 +1487,8 @@ void TreeView::InternalNode::Reset(ui::TreeModelNode* node) {
accessibility_view_ = nullptr;
}
-int TreeView::InternalNode::NumExpandedNodes() const {
- int result = 1; // For this.
+size_t TreeView::InternalNode::NumExpandedNodes() const {
+ size_t result = 1; // For this.
if (!is_expanded_)
return result;
for (const auto& child : children())
diff --git a/chromium/ui/views/controls/tree/tree_view.h b/chromium/ui/views/controls/tree/tree_view.h
index f7ab0945b19..03678965087 100644
--- a/chromium/ui/views/controls/tree/tree_view.h
+++ b/chromium/ui/views/controls/tree/tree_view.h
@@ -202,10 +202,10 @@ class VIEWS_EXPORT TreeView : public View,
void OnDidChangeFocus(View* focused_before, View* focused_now) override;
// PrefixDelegate overrides:
- int GetRowCount() override;
- int GetSelectedRow() override;
- void SetSelectedRow(int row) override;
- std::u16string GetTextForRow(int row) override;
+ size_t GetRowCount() override;
+ absl::optional<size_t> GetSelectedRow() override;
+ void SetSelectedRow(absl::optional<size_t> row) override;
+ std::u16string GetTextForRow(size_t row) override;
protected:
// View overrides:
@@ -276,7 +276,7 @@ class VIEWS_EXPORT TreeView : public View,
int text_width() const { return text_width_; }
// Returns the total number of descendants (including this node).
- int NumExpandedNodes() const;
+ size_t NumExpandedNodes() const;
// Returns the max width of all descendants (including this node). |indent|
// is how many pixels each child is indented and |depth| is the depth of
diff --git a/chromium/ui/views/controls/tree/tree_view_unittest.cc b/chromium/ui/views/controls/tree/tree_view_unittest.cc
index 20ff974e567..2582f594198 100644
--- a/chromium/ui/views/controls/tree/tree_view_unittest.cc
+++ b/chromium/ui/views/controls/tree/tree_view_unittest.cc
@@ -145,7 +145,7 @@ class TreeViewTest : public ViewsTestBase {
void IncrementSelection(bool next);
void CollapseOrSelectParent();
void ExpandOrSelectChild();
- int GetRowCount();
+ size_t GetRowCount();
PrefixSelector* selector() { return tree_->GetPrefixSelector(); }
ui::TreeNodeModel<TestNode> model_;
@@ -343,7 +343,7 @@ void TreeViewTest::ExpandOrSelectChild() {
tree_->ExpandOrSelectChild();
}
-int TreeViewTest::GetRowCount() {
+size_t TreeViewTest::GetRowCount() {
return tree_->GetRowCount();
}
@@ -396,7 +396,7 @@ TEST_F(TreeViewTest, SetModel) {
EXPECT_EQ("root [a b c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
EXPECT_EQ(
(AccessibilityEventsVector{
@@ -468,7 +468,7 @@ TEST_F(TreeViewTest, HideRoot) {
EXPECT_EQ("root [a b c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("a", GetSelectedNodeTitle());
EXPECT_EQ("a", GetSelectedAccessibilityViewName());
- EXPECT_EQ(3, GetRowCount());
+ EXPECT_EQ(3u, GetRowCount());
EXPECT_EQ((AccessibilityEventsVector{
std::make_pair(GetAccessibilityViewByName("a"),
@@ -489,7 +489,7 @@ TEST_F(TreeViewTest, Expand) {
EXPECT_EQ("root [a b [b1] c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(5, GetRowCount());
+ EXPECT_EQ(5u, GetRowCount());
EXPECT_EQ((AccessibilityEventsVector{
std::make_pair(GetTreeAccessibilityView(),
@@ -509,7 +509,7 @@ TEST_F(TreeViewTest, Collapse) {
tree_->Expand(GetNodeByTitle("b1"));
EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
EXPECT_EQ("root [a b [b1] c]", TreeViewAccessibilityContentsAsString());
- EXPECT_EQ(5, GetRowCount());
+ EXPECT_EQ(5u, GetRowCount());
tree_->SetSelectedNode(GetNodeByTitle("b1"));
EXPECT_EQ("b1", GetSelectedNodeTitle());
EXPECT_EQ("b1", GetSelectedAccessibilityViewName());
@@ -521,7 +521,7 @@ TEST_F(TreeViewTest, Collapse) {
// Selected node should have moved to 'b'
EXPECT_EQ("b", GetSelectedNodeTitle());
EXPECT_EQ("b", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
EXPECT_EQ((AccessibilityEventsVector{
std::make_pair(GetAccessibilityViewByName("b"),
@@ -550,7 +550,7 @@ TEST_F(TreeViewTest, TreeNodesAdded) {
EXPECT_EQ("root [a b B c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(5, GetRowCount());
+ EXPECT_EQ(5u, GetRowCount());
EXPECT_EQ((AccessibilityEventsVector{
std::make_pair(GetTreeAccessibilityView(),
ax::mojom::Event::kChildrenChanged),
@@ -565,7 +565,7 @@ TEST_F(TreeViewTest, TreeNodesAdded) {
EXPECT_EQ("root [a b B c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(5, GetRowCount());
+ EXPECT_EQ(5u, GetRowCount());
// Added node is not visible, hence no accessibility event needed.
EXPECT_EQ(AccessibilityEventsVector(), accessibility_events());
@@ -577,7 +577,7 @@ TEST_F(TreeViewTest, TreeNodesAdded) {
EXPECT_EQ("root [a b B c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(5, GetRowCount());
+ EXPECT_EQ(5u, GetRowCount());
// Added node is not visible, hence no accessibility event needed.
EXPECT_EQ(AccessibilityEventsVector(), accessibility_events());
@@ -588,7 +588,7 @@ TEST_F(TreeViewTest, TreeNodesAdded) {
EXPECT_EQ("root [a b [b1 b2] B c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(7, GetRowCount());
+ EXPECT_EQ(7u, GetRowCount());
// Since the added node was not visible when it was added, no extra events
// other than the ones for expanding a node are needed.
EXPECT_EQ((AccessibilityEventsVector{
@@ -616,7 +616,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a b c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
// Expand b1, then collapse it and remove its only child, b1. This shouldn't
// effect the tree.
@@ -628,7 +628,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a b c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
EXPECT_EQ(
(AccessibilityEventsVector{std::make_pair(
GetTreeAccessibilityView(), ax::mojom::Event::kChildrenChanged)}),
@@ -641,7 +641,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(3, GetRowCount());
+ EXPECT_EQ(3u, GetRowCount());
EXPECT_EQ(
(AccessibilityEventsVector{
std::make_pair(GetTreeAccessibilityView(), ax::mojom::Event::kFocus),
@@ -658,7 +658,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(3, GetRowCount());
+ EXPECT_EQ(3u, GetRowCount());
// Node "c11" is not visible, hence no accessibility event needed.
EXPECT_EQ(AccessibilityEventsVector(), accessibility_events());
@@ -672,7 +672,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("a", GetSelectedNodeTitle());
EXPECT_EQ("a", GetSelectedAccessibilityViewName());
- EXPECT_EQ(2, GetRowCount());
+ EXPECT_EQ(2u, GetRowCount());
EXPECT_EQ(
(AccessibilityEventsVector{
std::make_pair(GetTreeAccessibilityView(), ax::mojom::Event::kFocus),
@@ -696,7 +696,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a [c1 c3]]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("c3", GetSelectedNodeTitle());
EXPECT_EQ("c3", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
// Now delete 'c3' and then 'c1' should be selected.
ClearAccessibilityEvents();
@@ -705,7 +705,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a [c1]]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("c1", GetSelectedNodeTitle());
EXPECT_EQ("c1", GetSelectedAccessibilityViewName());
- EXPECT_EQ(3, GetRowCount());
+ EXPECT_EQ(3u, GetRowCount());
EXPECT_EQ(
(AccessibilityEventsVector{
std::make_pair(GetTreeAccessibilityView(), ax::mojom::Event::kFocus),
@@ -726,7 +726,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("a", GetSelectedNodeTitle());
EXPECT_EQ("a", GetSelectedAccessibilityViewName());
- EXPECT_EQ(2, GetRowCount());
+ EXPECT_EQ(2u, GetRowCount());
EXPECT_EQ(
(AccessibilityEventsVector{
std::make_pair(GetTreeAccessibilityView(), ax::mojom::Event::kFocus),
@@ -750,7 +750,7 @@ TEST_F(TreeViewTest, TreeNodesRemoved) {
EXPECT_EQ("root [a c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("c", GetSelectedNodeTitle());
EXPECT_EQ("c", GetSelectedAccessibilityViewName());
- EXPECT_EQ(2, GetRowCount());
+ EXPECT_EQ(2u, GetRowCount());
}
class TestController : public TreeViewController {
@@ -802,7 +802,7 @@ TEST_F(TreeViewTest, TreeNodeChanged) {
EXPECT_EQ("root [a b c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
EXPECT_EQ(AccessibilityEventsVector(), accessibility_events());
// Change 'b1', shouldn't do anything.
@@ -812,7 +812,7 @@ TEST_F(TreeViewTest, TreeNodeChanged) {
EXPECT_EQ("root [a b c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
EXPECT_EQ(AccessibilityEventsVector(), accessibility_events());
// Change 'b'.
@@ -822,7 +822,7 @@ TEST_F(TreeViewTest, TreeNodeChanged) {
EXPECT_EQ("root [a b.new c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
EXPECT_EQ((AccessibilityEventsVector{
std::make_pair(GetAccessibilityViewByName("b.new"),
ax::mojom::Event::kLocationChanged)}),
@@ -997,7 +997,7 @@ TEST_F(TreeViewTest, VirtualAccessibleAction) {
tree_->Expand(GetNodeByTitle("b1"));
EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
EXPECT_EQ("root [a b [b1] c]", TreeViewAccessibilityContentsAsString());
- EXPECT_EQ(5, GetRowCount());
+ EXPECT_EQ(5u, GetRowCount());
// Set to nullptr should clear the selection.
tree_->SetSelectedNode(nullptr);
@@ -1062,7 +1062,7 @@ TEST_F(TreeViewTest, OnFocusAccessibilityEvents) {
EXPECT_EQ("root [a b c]", TreeViewAccessibilityContentsAsString());
EXPECT_EQ("root", GetSelectedNodeTitle());
EXPECT_EQ("root", GetSelectedAccessibilityViewName());
- EXPECT_EQ(4, GetRowCount());
+ EXPECT_EQ(4u, GetRowCount());
EXPECT_EQ((AccessibilityEventsVector{
std::make_pair(GetTreeAccessibilityView(),
ax::mojom::Event::kChildrenChanged),
diff --git a/chromium/ui/views/controls/webview/webview_unittest.cc b/chromium/ui/views/controls/webview/webview_unittest.cc
index edf636df419..ea25270ff23 100644
--- a/chromium/ui/views/controls/webview/webview_unittest.cc
+++ b/chromium/ui/views/controls/webview/webview_unittest.cc
@@ -173,9 +173,9 @@ class WebViewUnitTest : public views::test::WidgetTest {
top_level_widget_->SetBounds(gfx::Rect(0, 10, 100, 100));
View* const contents_view =
top_level_widget_->SetContentsView(std::make_unique<View>());
- web_view_ = new WebView(browser_context_.get());
- web_view_->SetBoundsRect(gfx::Rect(contents_view->size()));
- contents_view->AddChildView(web_view_.get());
+ auto web_view = std::make_unique<WebView>(browser_context_.get());
+ web_view->SetBoundsRect(gfx::Rect(contents_view->size()));
+ web_view_ = contents_view->AddChildView(std::move(web_view));
top_level_widget_->Show();
ASSERT_EQ(gfx::Rect(0, 0, 100, 100), web_view_->bounds());
}
@@ -231,8 +231,8 @@ TEST_F(WebViewUnitTest, TestWebViewAttachDetachWebContents) {
EXPECT_FALSE(observer1.was_shown());
web_view()->SetWebContents(web_contents1.get());
- // Layout() is normally async, call it now to ensure visibility is updated.
- web_view()->Layout();
+ // Layout is normally async, ensure it runs now so visibility is updated.
+ RunScheduledLayout(web_view());
EXPECT_TRUE(observer1.was_shown());
#if defined(USE_AURA)
EXPECT_TRUE(web_contents1->GetNativeView()->IsVisible());
@@ -251,8 +251,8 @@ TEST_F(WebViewUnitTest, TestWebViewAttachDetachWebContents) {
// Setting the new WebContents should hide the existing one.
web_view()->SetWebContents(web_contents2.get());
- // Layout() is normally async, call it now to ensure visibility is updated.
- web_view()->Layout();
+ // Layout is normally async, ensure it runs now so visibility is updated.
+ RunScheduledLayout(web_view());
EXPECT_FALSE(observer1.was_shown());
EXPECT_TRUE(observer2.was_shown());
EXPECT_TRUE(observer2.valid_root_while_shown());
@@ -270,8 +270,8 @@ TEST_F(WebViewUnitTest, TestWebViewAttachDetachWebContents) {
EXPECT_EQ(1, observer1.shown_count());
web_view()->SetWebContents(web_contents1.get());
- // Layout() is normally async, call it now to ensure visibility is updated.
- web_view()->Layout();
+ // Layout is normally async, ensure it runs now so visibility is updated.
+ RunScheduledLayout(web_view());
EXPECT_EQ(1, observer1.shown_count());
// Nothing else should change.