Commit f7ff81a5 authored by Sammie Quon's avatar Sammie Quon Committed by Commit Bot

ui: Remove ink drop masks from message center.

Use the api which does clipping, which is more performant than using masks.

Test: manual
Bug: 1056490
Change-Id: Id9612882241ec95832c562e60fcf20c82bc88e19
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2099291Reviewed-by: default avatarTim Song <tengs@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Commit-Queue: Sammie Quon <sammiequon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#750820}
parent e8e21ac3
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include "ui/views/border.h" #include "ui/views/border.h"
#include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/radio_button.h" #include "ui/views/controls/button/radio_button.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
#include "ui/views/controls/progress_bar.h" #include "ui/views/controls/progress_bar.h"
...@@ -343,6 +344,8 @@ NotificationButtonMD::NotificationButtonMD( ...@@ -343,6 +344,8 @@ NotificationButtonMD::NotificationButtonMD(
SetBorder(views::CreateEmptyBorder(kActionButtonPadding)); SetBorder(views::CreateEmptyBorder(kActionButtonPadding));
SetMinSize(kActionButtonMinSize); SetMinSize(kActionButtonMinSize);
SetFocusForPlatform(); SetFocusForPlatform();
views::InstallRectHighlightPathGenerator(this);
} }
NotificationButtonMD::~NotificationButtonMD() = default; NotificationButtonMD::~NotificationButtonMD() = default;
...@@ -378,8 +381,6 @@ NotificationInputContainerMD::NotificationInputContainerMD( ...@@ -378,8 +381,6 @@ NotificationInputContainerMD::NotificationInputContainerMD(
SetInkDropMode(InkDropMode::ON); SetInkDropMode(InkDropMode::ON);
set_ink_drop_visible_opacity(1); set_ink_drop_visible_opacity(1);
ink_drop_container_->SetPaintToLayer();
ink_drop_container_->layer()->SetFillsBoundsOpaquely(false);
AddChildView(ink_drop_container_); AddChildView(ink_drop_container_);
textfield_->set_controller(this); textfield_->set_controller(this);
...@@ -395,6 +396,8 @@ NotificationInputContainerMD::NotificationInputContainerMD( ...@@ -395,6 +396,8 @@ NotificationInputContainerMD::NotificationInputContainerMD(
button_->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE); button_->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
OnAfterUserAction(textfield_); OnAfterUserAction(textfield_);
AddChildView(button_); AddChildView(button_);
views::InstallRectHighlightPathGenerator(this);
} }
NotificationInputContainerMD::~NotificationInputContainerMD() = default; NotificationInputContainerMD::~NotificationInputContainerMD() = default;
...@@ -408,21 +411,22 @@ void NotificationInputContainerMD::AnimateBackground(const ui::Event& event) { ...@@ -408,21 +411,22 @@ void NotificationInputContainerMD::AnimateBackground(const ui::Event& event) {
AnimateInkDrop(views::InkDropState::ACTION_PENDING, located_event); AnimateInkDrop(views::InkDropState::ACTION_PENDING, located_event);
} }
void NotificationInputContainerMD::AddInkDropLayer(ui::Layer* ink_drop_layer) { void NotificationInputContainerMD::AddLayerBeneathView(ui::Layer* layer) {
// When a ink drop layer is added it is stacked between the textfield/button
// and the parent (|this|). Since the ink drop is opaque, we have to paint the
// textfield/button on their own layers in otherwise they remain painted on
// |this|'s layer which would be covered by the ink drop.
textfield_->SetPaintToLayer(); textfield_->SetPaintToLayer();
textfield_->layer()->SetFillsBoundsOpaquely(false); textfield_->layer()->SetFillsBoundsOpaquely(false);
button_->SetPaintToLayer(); button_->SetPaintToLayer();
button_->layer()->SetFillsBoundsOpaquely(false); button_->layer()->SetFillsBoundsOpaquely(false);
ink_drop_container_->AddInkDropLayer(ink_drop_layer); ink_drop_container_->AddLayerBeneathView(layer);
InstallInkDropMask(ink_drop_layer);
} }
void NotificationInputContainerMD::RemoveInkDropLayer( void NotificationInputContainerMD::RemoveLayerBeneathView(ui::Layer* layer) {
ui::Layer* ink_drop_layer) { ink_drop_container_->RemoveLayerBeneathView(layer);
textfield_->DestroyLayer(); textfield_->DestroyLayer();
button_->DestroyLayer(); button_->DestroyLayer();
ResetInkDropMask();
ink_drop_container_->RemoveInkDropLayer(ink_drop_layer);
} }
std::unique_ptr<views::InkDropRipple> std::unique_ptr<views::InkDropRipple>
...@@ -506,6 +510,42 @@ class NotificationInkDropImpl : public views::InkDropImpl { ...@@ -506,6 +510,42 @@ class NotificationInkDropImpl : public views::InkDropImpl {
// NotificationViewMD // NotificationViewMD
// //////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////
class NotificationViewMD::NotificationViewMDPathGenerator
: public views::HighlightPathGenerator {
public:
NotificationViewMDPathGenerator() = default;
NotificationViewMDPathGenerator(const NotificationViewMDPathGenerator&) =
delete;
NotificationViewMDPathGenerator& operator=(
const NotificationViewMDPathGenerator&) = delete;
// views::HighlightPathGenerator:
base::Optional<RoundRect> GetRoundRect(const gfx::RectF& rect) override {
RoundRect round_rect;
round_rect.bounds = rect;
if (!preferred_size_.IsEmpty())
round_rect.bounds.set_size(gfx::SizeF(preferred_size_));
round_rect.corner_radius = gfx::RoundedCornersF(
top_radius_, top_radius_, bottom_radius_, bottom_radius_);
return round_rect;
}
void set_top_radius(int val) { top_radius_ = val; }
void set_bottom_radius(int val) { bottom_radius_ = val; }
void set_preferred_size(const gfx::Size& val) { preferred_size_ = val; }
private:
int top_radius_ = 0;
int bottom_radius_ = 0;
// This custom PathGenerator is used for the ink drop clipping bounds. By
// setting |preferred_size_| we set the correct clip bounds in
// GetRoundRect(). This is needed as the correct bounds for the ink drop are
// required before a Layout() on the view is run. See
// http://crbug.com/915222.
gfx::Size preferred_size_;
};
void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) { void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) {
left_content_count_ = 0; left_content_count_ = 0;
...@@ -534,8 +574,6 @@ NotificationViewMD::NotificationViewMD(const Notification& notification) ...@@ -534,8 +574,6 @@ NotificationViewMD::NotificationViewMD(const Notification& notification)
set_ink_drop_visible_opacity(1); set_ink_drop_visible_opacity(1);
ink_drop_container_->SetPaintToLayer();
ink_drop_container_->layer()->SetFillsBoundsOpaquely(false);
AddChildView(ink_drop_container_); AddChildView(ink_drop_container_);
control_buttons_view_ = control_buttons_view_ =
...@@ -600,6 +638,12 @@ NotificationViewMD::NotificationViewMD(const Notification& notification) ...@@ -600,6 +638,12 @@ NotificationViewMD::NotificationViewMD(const Notification& notification)
// - To make it look similar to ArcNotificationContentView::EventForwarder. // - To make it look similar to ArcNotificationContentView::EventForwarder.
AddPreTargetHandler(click_activator_.get()); AddPreTargetHandler(click_activator_.get());
auto highlight_path_generator =
std::make_unique<NotificationViewMDPathGenerator>();
highlight_path_generator_ = highlight_path_generator.get();
views::HighlightPathGenerator::Install(this,
std::move(highlight_path_generator));
UpdateCornerRadius(kNotificationCornerRadius, kNotificationCornerRadius); UpdateCornerRadius(kNotificationCornerRadius, kNotificationCornerRadius);
} }
...@@ -607,6 +651,22 @@ NotificationViewMD::~NotificationViewMD() { ...@@ -607,6 +651,22 @@ NotificationViewMD::~NotificationViewMD() {
RemovePreTargetHandler(click_activator_.get()); RemovePreTargetHandler(click_activator_.get());
} }
void NotificationViewMD::AddLayerBeneathView(ui::Layer* layer) {
GetInkDrop()->AddObserver(this);
for (auto* child : GetChildrenForLayerAdjustment()) {
child->SetPaintToLayer();
child->layer()->SetFillsBoundsOpaquely(false);
}
ink_drop_container_->AddLayerBeneathView(layer);
}
void NotificationViewMD::RemoveLayerBeneathView(ui::Layer* layer) {
ink_drop_container_->RemoveLayerBeneathView(layer);
for (auto* child : GetChildrenForLayerAdjustment())
child->DestroyLayer();
GetInkDrop()->RemoveObserver(this);
}
void NotificationViewMD::Layout() { void NotificationViewMD::Layout() {
MessageView::Layout(); MessageView::Layout();
...@@ -635,10 +695,6 @@ void NotificationViewMD::Layout() { ...@@ -635,10 +695,6 @@ void NotificationViewMD::Layout() {
// The animation is needed to run inside of the border. // The animation is needed to run inside of the border.
ink_drop_container_->SetBoundsRect(GetLocalBounds()); ink_drop_container_->SetBoundsRect(GetLocalBounds());
if (ink_drop_layer_)
ink_drop_layer_->SetBounds(GetContentsBounds());
if (ink_drop_mask_)
ink_drop_mask_->layer()->SetBounds(GetContentsBounds());
} }
void NotificationViewMD::OnFocus() { void NotificationViewMD::OnFocus() {
...@@ -707,6 +763,11 @@ void NotificationViewMD::OnGestureEvent(ui::GestureEvent* event) { ...@@ -707,6 +763,11 @@ void NotificationViewMD::OnGestureEvent(ui::GestureEvent* event) {
MessageView::OnGestureEvent(event); MessageView::OnGestureEvent(event);
} }
void NotificationViewMD::PreferredSizeChanged() {
highlight_path_generator_->set_preferred_size(GetPreferredSize());
MessageView::PreferredSizeChanged();
}
void NotificationViewMD::UpdateWithNotification( void NotificationViewMD::UpdateWithNotification(
const Notification& notification) { const Notification& notification) {
MessageView::UpdateWithNotification(notification); MessageView::UpdateWithNotification(notification);
...@@ -1319,8 +1380,8 @@ void NotificationViewMD::UpdateCornerRadius(int top_radius, int bottom_radius) { ...@@ -1319,8 +1380,8 @@ void NotificationViewMD::UpdateCornerRadius(int top_radius, int bottom_radius) {
action_buttons_row_->SetBackground(views::CreateBackgroundFromPainter( action_buttons_row_->SetBackground(views::CreateBackgroundFromPainter(
std::make_unique<NotificationBackgroundPainter>( std::make_unique<NotificationBackgroundPainter>(
0, bottom_radius, kActionsRowBackgroundColor))); 0, bottom_radius, kActionsRowBackgroundColor)));
top_radius_ = top_radius; highlight_path_generator_->set_top_radius(top_radius);
bottom_radius_ = bottom_radius; highlight_path_generator_->set_bottom_radius(bottom_radius);
} }
NotificationControlButtonsView* NotificationViewMD::GetControlButtonsView() NotificationControlButtonsView* NotificationViewMD::GetControlButtonsView()
...@@ -1397,32 +1458,6 @@ void NotificationViewMD::RemoveBackgroundAnimation() { ...@@ -1397,32 +1458,6 @@ void NotificationViewMD::RemoveBackgroundAnimation() {
AnimateInkDrop(views::InkDropState::HIDDEN, nullptr); AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
} }
void NotificationViewMD::AddInkDropLayer(ui::Layer* ink_drop_layer) {
GetInkDrop()->AddObserver(this);
header_row_->SetPaintToLayer();
header_row_->layer()->SetFillsBoundsOpaquely(false);
block_all_button_->SetPaintToLayer();
block_all_button_->layer()->SetFillsBoundsOpaquely(false);
dont_block_button_->SetPaintToLayer();
dont_block_button_->layer()->SetFillsBoundsOpaquely(false);
settings_done_button_->SetPaintToLayer();
settings_done_button_->layer()->SetFillsBoundsOpaquely(false);
ink_drop_container_->AddInkDropLayer(ink_drop_layer);
ink_drop_layer_ = ink_drop_layer;
InstallNotificationInkDropMask();
}
void NotificationViewMD::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
header_row_->DestroyLayer();
block_all_button_->DestroyLayer();
dont_block_button_->DestroyLayer();
settings_done_button_->DestroyLayer();
ink_drop_mask_.reset();
ink_drop_container_->RemoveInkDropLayer(ink_drop_layer);
GetInkDrop()->RemoveObserver(this);
ink_drop_layer_ = nullptr;
}
std::unique_ptr<views::InkDrop> NotificationViewMD::CreateInkDrop() { std::unique_ptr<views::InkDrop> NotificationViewMD::CreateInkDrop() {
return std::make_unique<NotificationInkDropImpl>(this, size()); return std::make_unique<NotificationInkDropImpl>(this, size());
} }
...@@ -1434,22 +1469,14 @@ std::unique_ptr<views::InkDropRipple> NotificationViewMD::CreateInkDropRipple() ...@@ -1434,22 +1469,14 @@ std::unique_ptr<views::InkDropRipple> NotificationViewMD::CreateInkDropRipple()
GetInkDropBaseColor(), ink_drop_visible_opacity()); GetInkDropBaseColor(), ink_drop_visible_opacity());
} }
void NotificationViewMD::InstallNotificationInkDropMask() { std::vector<views::View*> NotificationViewMD::GetChildrenForLayerAdjustment()
SkPath path; const {
SkScalar radii[8] = {top_radius_, top_radius_, top_radius_, return {header_row_, block_all_button_, dont_block_button_,
top_radius_, bottom_radius_, bottom_radius_, settings_done_button_};
bottom_radius_, bottom_radius_};
gfx::Rect rect(GetPreferredSize());
path.addRoundRect(gfx::RectToSkRect(rect), radii);
ink_drop_mask_ = std::make_unique<views::PathInkDropMask>(size(), path);
ink_drop_layer_->SetMaskLayer(ink_drop_mask_->layer());
} }
std::unique_ptr<views::InkDropMask> NotificationViewMD::CreateInkDropMask() std::unique_ptr<views::InkDropMask> NotificationViewMD::CreateInkDropMask()
const { const {
// We don't use this as we need access to the |ink_drop_mask_|.
// See crbug.com/915222.
NOTREACHED();
return nullptr; return nullptr;
} }
......
...@@ -124,9 +124,9 @@ class NotificationInputContainerMD : public views::InkDropHostView, ...@@ -124,9 +124,9 @@ class NotificationInputContainerMD : public views::InkDropHostView,
void AnimateBackground(const ui::Event& event); void AnimateBackground(const ui::Event& event);
// Overridden from views::InkDropHostView: // views::InkDropHostView:
void AddInkDropLayer(ui::Layer* ink_drop_layer) override; void AddLayerBeneathView(ui::Layer* layer) override;
void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override; void RemoveLayerBeneathView(ui::Layer* layer) override;
std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override; std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
SkColor GetInkDropBaseColor() const override; SkColor GetInkDropBaseColor() const override;
...@@ -170,7 +170,9 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD ...@@ -170,7 +170,9 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
void AddBackgroundAnimation(const ui::Event& event); void AddBackgroundAnimation(const ui::Event& event);
void RemoveBackgroundAnimation(); void RemoveBackgroundAnimation();
// Overridden from views::View: // MessageView:
void AddLayerBeneathView(ui::Layer* layer) override;
void RemoveLayerBeneathView(ui::Layer* layer) override;
void Layout() override; void Layout() override;
void OnFocus() override; void OnFocus() override;
bool OnMousePressed(const ui::MouseEvent& event) override; bool OnMousePressed(const ui::MouseEvent& event) override;
...@@ -178,16 +180,11 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD ...@@ -178,16 +180,11 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
void OnMouseReleased(const ui::MouseEvent& event) override; void OnMouseReleased(const ui::MouseEvent& event) override;
void OnMouseEvent(ui::MouseEvent* event) override; void OnMouseEvent(ui::MouseEvent* event) override;
void OnGestureEvent(ui::GestureEvent* event) override; void OnGestureEvent(ui::GestureEvent* event) override;
void PreferredSizeChanged() override;
// Overridden from views::InkDropHostView:
void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
std::unique_ptr<views::InkDrop> CreateInkDrop() override; std::unique_ptr<views::InkDrop> CreateInkDrop() override;
std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override; std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override; std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
SkColor GetInkDropBaseColor() const override; SkColor GetInkDropBaseColor() const override;
// Overridden from MessageView:
void UpdateWithNotification(const Notification& notification) override; void UpdateWithNotification(const Notification& notification) override;
void ButtonPressed(views::Button* sender, const ui::Event& event) override; void ButtonPressed(views::Button* sender, const ui::Event& event) override;
void UpdateCornerRadius(int top_radius, int bottom_radius) override; void UpdateCornerRadius(int top_radius, int bottom_radius) override;
...@@ -242,6 +239,8 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD ...@@ -242,6 +239,8 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
friend class NotificationViewMDTest; friend class NotificationViewMDTest;
class NotificationViewMDPathGenerator;
void UpdateControlButtonsVisibilityWithNotification( void UpdateControlButtonsVisibilityWithNotification(
const Notification& notification); const Notification& notification);
...@@ -265,8 +264,9 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD ...@@ -265,8 +264,9 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
void UpdateViewForExpandedState(bool expanded); void UpdateViewForExpandedState(bool expanded);
void ToggleInlineSettings(const ui::Event& event); void ToggleInlineSettings(const ui::Event& event);
// Initializes |ink_drop_mask_| and sets the mask on |ink_drop_layer_|. // Returns the list of children which need to have their layers created or
void InstallNotificationInkDropMask(); // destroyed when the ink drop is visible.
std::vector<views::View*> GetChildrenForLayerAdjustment() const;
views::InkDropContainerView* const ink_drop_container_; views::InkDropContainerView* const ink_drop_container_;
...@@ -289,15 +289,6 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD ...@@ -289,15 +289,6 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
// Describes whether the view should display a hand pointer or not. // Describes whether the view should display a hand pointer or not.
bool clickable_; bool clickable_;
// Corner radii for the InkDropMask.
int top_radius_ = 0;
int bottom_radius_ = 0;
// The InkDrop layer and InkDropMask used to update their bounds on
// OnBoundsChanged(). See crbug.com/915222.
ui::Layer* ink_drop_layer_ = nullptr;
std::unique_ptr<views::InkDropMask> ink_drop_mask_;
// Container views directly attached to this view. // Container views directly attached to this view.
NotificationHeaderView* header_row_ = nullptr; NotificationHeaderView* header_row_ = nullptr;
views::View* content_row_ = nullptr; views::View* content_row_ = nullptr;
...@@ -330,6 +321,10 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD ...@@ -330,6 +321,10 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD
views::RadioButton* dont_block_button_ = nullptr; views::RadioButton* dont_block_button_ = nullptr;
views::LabelButton* settings_done_button_ = nullptr; views::LabelButton* settings_done_button_ = nullptr;
// Owned by views properties. Guaranteed to be not null for the lifetime of
// |this| because views properties are the last thing cleaned up.
NotificationViewMDPathGenerator* highlight_path_generator_ = nullptr;
std::unique_ptr<ui::EventHandler> click_activator_; std::unique_ptr<ui::EventHandler> click_activator_;
base::TimeTicks last_mouse_pressed_timestamp_; base::TimeTicks last_mouse_pressed_timestamp_;
......
...@@ -11,6 +11,12 @@ ...@@ -11,6 +11,12 @@
namespace views { namespace views {
HighlightPathGenerator::RoundRect::RoundRect() = default;
HighlightPathGenerator::RoundRect::RoundRect(const gfx::RectF& bounds,
float corner_radius)
: bounds(bounds), corner_radius(corner_radius) {}
HighlightPathGenerator::HighlightPathGenerator() HighlightPathGenerator::HighlightPathGenerator()
: HighlightPathGenerator(gfx::Insets()) {} : HighlightPathGenerator(gfx::Insets()) {}
...@@ -39,11 +45,8 @@ SkPath HighlightPathGenerator::GetHighlightPath(const View* view) { ...@@ -39,11 +45,8 @@ SkPath HighlightPathGenerator::GetHighlightPath(const View* view) {
base::Optional<HighlightPathGenerator::RoundRect> round_rect = base::Optional<HighlightPathGenerator::RoundRect> round_rect =
GetRoundRect(view); GetRoundRect(view);
DCHECK(round_rect); DCHECK(round_rect);
return SkPath().addRRect(
const float corner_radius = round_rect->corner_radius; SkRRect{gfx::RRectF(round_rect->bounds, round_rect->corner_radius)});
return SkPath().addRoundRect(
gfx::RectToSkRect(gfx::ToEnclosingRect(round_rect->bounds)),
corner_radius, corner_radius);
} }
base::Optional<HighlightPathGenerator::RoundRect> base::Optional<HighlightPathGenerator::RoundRect>
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPath.h"
#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/views_export.h" #include "ui/views/views_export.h"
namespace views { namespace views {
...@@ -22,9 +23,15 @@ class View; ...@@ -22,9 +23,15 @@ class View;
// effects. // effects.
class VIEWS_EXPORT HighlightPathGenerator { class VIEWS_EXPORT HighlightPathGenerator {
public: public:
struct RoundRect { struct VIEWS_EXPORT RoundRect {
// TODO(http://crbug.com/1056490): Remove these constructors and have
// callsites create a gfx::RoundedCornersF explicitly, or replace this
// struct with a gfx::RRectF.
RoundRect();
RoundRect(const gfx::RectF& bounds, float corner_radius);
gfx::RectF bounds; gfx::RectF bounds;
float corner_radius = 0.f; gfx::RoundedCornersF corner_radius;
}; };
// TODO(http://crbug.com/1056490): Remove this constructor in favor of the one // TODO(http://crbug.com/1056490): Remove this constructor in favor of the one
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment