Skip to content

Commit

Permalink
Add ClassProperty to metadata
Browse files Browse the repository at this point in the history
Add ADD_CLASS_PROPERTY_METADATA to declare an ui::ClassProperty on a
View class. Declared class property can be inspect in UI Devtools.

Bug: 1173664
Change-Id: I2d506ded441cbe3ad76e3f9e3a810460f039c8f4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2703113
Commit-Queue: Keren Zhu <kerenzhu@chromium.org>
Reviewed-by: Allen Bauer <kylixrd@chromium.org>
Cr-Commit-Position: refs/heads/master@{#855405}
  • Loading branch information
naeioi authored and Chromium LUCI CQ committed Feb 18, 2021
1 parent b3e592e commit 81a7e9d
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 17 deletions.
12 changes: 12 additions & 0 deletions ui/views/metadata/metadata_impl_macros.h
Expand Up @@ -66,4 +66,16 @@
property_type, property_name, ##__VA_ARGS__)>(#property_name, \
#property_type); \
AddMemberData(std::move(property_name##_prop));

// Adds a ui::ClassProperty of key |property_key| to metadata.
// If the property value is an pointer of type |TValue*|, specify
// |property_type| as |TValue| to allow inspecting the actually value.
// Otherwise the metadata will treat the pointer as it is.
#define ADD_CLASS_PROPERTY_METADATA(property_type, property_key, ...) \
auto property_key##_prop = \
std::make_unique<METADATA_CLASS_PROPERTY_TYPE_INTERNAL( \
property_type, property_key, ##__VA_ARGS__)>(property_key, \
#property_type); \
AddMemberData(std::move(property_key##_prop));

#endif // UI_VIEWS_METADATA_METADATA_IMPL_MACROS_H_
9 changes: 7 additions & 2 deletions ui/views/metadata/metadata_macros_internal.h
Expand Up @@ -52,19 +52,24 @@
}

#define METADATA_PROPERTY_TYPE_INTERNAL(property_type, property_name, ...) \
views::metadata::ClassPropertyMetaData< \
views::metadata::ObjectPropertyMetaData< \
ViewClass, property_type, decltype(&ViewClass::Set##property_name), \
&ViewClass::Set##property_name, \
decltype(std::declval<ViewClass>().Get##property_name()), \
&ViewClass::Get##property_name, ##__VA_ARGS__>

#define METADATA_READONLY_PROPERTY_TYPE_INTERNAL(property_type, property_name, \
...) \
views::metadata::ClassPropertyReadOnlyMetaData< \
views::metadata::ObjectPropertyReadOnlyMetaData< \
ViewClass, property_type, \
decltype(std::declval<ViewClass>().Get##property_name()), \
&ViewClass::Get##property_name, ##__VA_ARGS__>

#define METADATA_CLASS_PROPERTY_TYPE_INTERNAL(property_type, property_key, \
...) \
views::metadata::ClassPropertyMetaData<decltype(property_key), \
property_type, ##__VA_ARGS__>

#define BEGIN_METADATA_INTERNAL(qualified_class_name, metadata_class_name, \
parent_class_name) \
views::metadata::ClassMetaData* \
Expand Down
65 changes: 65 additions & 0 deletions ui/views/metadata/metadata_unittest.cc
Expand Up @@ -7,6 +7,8 @@
#include "base/strings/string_number_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "ui/base/class_property.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/metadata/metadata_header_macros.h"
#include "ui/views/metadata/metadata_impl_macros.h"
#include "ui/views/metadata/metadata_types.h"
Expand Down Expand Up @@ -97,6 +99,29 @@ BEGIN_METADATA(MetadataTestView, MetadataTestBaseView)
ADD_PROPERTY_METADATA(float, FloatProperty)
END_METADATA

// Test view to which class properties are attached.
class ClassPropertyMetaDataTestView : public views::View {
public:
ClassPropertyMetaDataTestView() = default;
~ClassPropertyMetaDataTestView() override = default;

METADATA_HEADER(ClassPropertyMetaDataTestView);
};

DEFINE_UI_CLASS_PROPERTY_KEY(int, kIntKey, -1)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Insets, kOwnedInsetsKey1, nullptr)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Insets, kOwnedInsetsKey2, nullptr)
DEFINE_UI_CLASS_PROPERTY_KEY(gfx::Insets*, kInsetsKey1, nullptr)
DEFINE_UI_CLASS_PROPERTY_KEY(gfx::Insets*, kInsetsKey2, nullptr)

BEGIN_METADATA(ClassPropertyMetaDataTestView, views::View)
ADD_CLASS_PROPERTY_METADATA(int, kIntKey)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kOwnedInsetsKey1)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets*, kOwnedInsetsKey2)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kInsetsKey1)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets*, kInsetsKey2)
END_METADATA

TEST_F(MetadataTest, TestFloatMetadataPropertyAccess) {
const float start_value = 12.34f;

Expand Down Expand Up @@ -186,3 +211,43 @@ TEST_F(MetadataTest, TestMetaDataFile) {

CHECK_EQ(metadata->file(), "ui/views/metadata/metadata_unittest.cc");
}

TEST_F(MetadataTest, TestClassPropertyMetaData) {
ClassPropertyMetaDataTestView view;
gfx::Insets insets1(8, 8, 8, 8), insets2 = insets1;

std::map<std::string, base::string16> expected_kv = {
{"kIntKey", base::ASCIIToUTF16("-1")},
{"kOwnedInsetsKey1", base::ASCIIToUTF16("(not assigned)")},
{"kOwnedInsetsKey2", base::ASCIIToUTF16("(not assigned)")},
{"kInsetsKey1", base::ASCIIToUTF16("(not assigned)")},
{"kInsetsKey2", base::ASCIIToUTF16("(not assigned)")}};

auto verify = [&]() {
views::metadata::ClassMetaData* metadata = view.GetClassMetaData();
for (auto member = metadata->begin(); member != metadata->end(); member++) {
std::string key = (*member)->member_name();
if (expected_kv.count(key)) {
EXPECT_EQ((*member)->GetValueAsString(&view), expected_kv[key]);
expected_kv.erase(key);
}
}
EXPECT_EQ(expected_kv.empty(), true);
};

verify();

view.SetProperty(kIntKey, 1);
view.SetProperty(kOwnedInsetsKey1, insets1);
view.SetProperty(kOwnedInsetsKey2, insets1);
view.SetProperty(kInsetsKey1, &insets1);
view.SetProperty(kInsetsKey2, &insets2);

expected_kv = {{"kIntKey", base::ASCIIToUTF16("1")},
{"kOwnedInsetsKey1", base::ASCIIToUTF16("8,8,8,8")},
{"kOwnedInsetsKey2", base::ASCIIToUTF16("(assigned)")},
{"kInsetsKey1", base::ASCIIToUTF16("8,8,8,8")},
{"kInsetsKey2", base::ASCIIToUTF16("(assigned)")}};

verify();
}
119 changes: 104 additions & 15 deletions ui/views/metadata/property_metadata.h
Expand Up @@ -11,6 +11,7 @@

#include "base/macros.h"
#include "base/strings/string16.h"
#include "ui/base/class_property.h"
#include "ui/views/metadata/metadata_cache.h"
#include "ui/views/metadata/metadata_types.h"
#include "ui/views/metadata/type_conversion.h"
Expand All @@ -19,6 +20,40 @@

namespace views {
namespace metadata {
namespace internal {

template <typename TSource, typename TTarget, typename = void>
struct DeRefHelper {
static TTarget Get(TSource value) { return value; }
};

template <typename TSource, typename TTarget>
struct DeRefHelper<
TSource,
TTarget,
typename std::enable_if<!std::is_same<TSource, TTarget>::value>::type> {
static TTarget Get(TSource value) { return *value; }
};

template <typename TKey, typename TValue>
struct ClassPropertyMetaDataTypeHelper;

template <typename TKValue_, typename TValue_>
struct ClassPropertyMetaDataTypeHelper<const ui::ClassProperty<TKValue_>* const,
TValue_> {
using TKValue = TKValue_;
using TValue = TValue_;

// Returns |value| when |TKValue| == |TValue|. Otherwise, TKValue must be the
// pointer type to TValue, returns |*value| instead.
// This is useful for owned propertyies like ui::ClassProperty<gfx::Insets*>
// where we want to inspect the actual value, rather than the pointer.
static TValue DeRef(TKValue value) {
return DeRefHelper<TKValue, TValue>::Get(value);
}
};

} // namespace internal

// Represents meta data for a specific read-only property member of class
// |TClass|, with underlying type |TValue|, as the type of the actual member.
Expand All @@ -29,13 +64,14 @@ template <typename TClass,
typename TRet,
TRet (TClass::*Get)() const,
typename TConverter = TypeConverter<TValue>>
class ClassPropertyReadOnlyMetaData : public MemberMetaDataBase {
class ObjectPropertyReadOnlyMetaData : public MemberMetaDataBase {
public:
using MemberMetaDataBase::MemberMetaDataBase;
ClassPropertyReadOnlyMetaData(const ClassPropertyReadOnlyMetaData&) = delete;
ClassPropertyReadOnlyMetaData& operator=(
const ClassPropertyReadOnlyMetaData&) = delete;
~ClassPropertyReadOnlyMetaData() override = default;
ObjectPropertyReadOnlyMetaData(const ObjectPropertyReadOnlyMetaData&) =
delete;
ObjectPropertyReadOnlyMetaData& operator=(
const ObjectPropertyReadOnlyMetaData&) = delete;
~ObjectPropertyReadOnlyMetaData() override = default;

base::string16 GetValueAsString(View* obj) const override {
if (!kTypeIsSerializable && !kTypeIsReadOnly)
Expand Down Expand Up @@ -70,17 +106,18 @@ template <typename TClass,
typename TRet,
TRet (TClass::*Get)() const,
typename TConverter = TypeConverter<TValue>>
class ClassPropertyMetaData : public ClassPropertyReadOnlyMetaData<TClass,
TValue,
TRet,
Get,
TConverter> {
class ObjectPropertyMetaData
: public ObjectPropertyReadOnlyMetaData<TClass,
TValue,
TRet,
Get,
TConverter> {
public:
using ClassPropertyReadOnlyMetaData<TClass, TValue, TRet, Get, TConverter>::
ClassPropertyReadOnlyMetaData;
ClassPropertyMetaData(const ClassPropertyMetaData&) = delete;
ClassPropertyMetaData& operator=(const ClassPropertyMetaData&) = delete;
~ClassPropertyMetaData() override = default;
using ObjectPropertyReadOnlyMetaData<TClass, TValue, TRet, Get, TConverter>::
ObjectPropertyReadOnlyMetaData;
ObjectPropertyMetaData(const ObjectPropertyMetaData&) = delete;
ObjectPropertyMetaData& operator=(const ObjectPropertyMetaData&) = delete;
~ObjectPropertyMetaData() override = default;

void SetValueAsString(View* obj, const base::string16& new_value) override {
if (!kTypeIsSerializable || kTypeIsReadOnly)
Expand Down Expand Up @@ -110,6 +147,58 @@ class ClassPropertyMetaData : public ClassPropertyReadOnlyMetaData<TClass,
static constexpr bool kTypeIsReadOnly = TConverter::is_read_only;
};

// Represents metadata for a ui::ClassProperty attached on a class instance.
// Converts property value to |TValue| when possible. This allows inspecting
// the actual value when the property is a pointer of type |TValue*|.
template <typename TKey,
typename TValue,
typename TConverter = TypeConverter<TValue>>
class ClassPropertyMetaData : public MemberMetaDataBase {
public:
using TypeHelper = internal::ClassPropertyMetaDataTypeHelper<TKey, TValue>;
ClassPropertyMetaData(TKey key, const std::string& property_type)
: MemberMetaDataBase(key->name, property_type), key_(key) {}
ClassPropertyMetaData(const ClassPropertyMetaData&) = delete;
ClassPropertyMetaData& operator=(const ClassPropertyMetaData&) = delete;
~ClassPropertyMetaData() override = default;

// Returns the property value as a string.
// If the property value is an pointer of type |TKValue*| and
// |TKValue| == |TValue|, dereferences the pointer.
base::string16 GetValueAsString(View* obj) const override {
typename TypeHelper::TKValue value = obj->GetProperty(key_);
if (std::is_pointer<typename TypeHelper::TKValue>::value && !value) {
return base::ASCIIToUTF16("(not assigned)");
} else {
// GetProperty() returns a pointer when this is an owned property.
// If |TValue| is not pointer, DeRef() returns |*value|, otherwise
// it returns |value| as it is.
return TConverter::ToString(TypeHelper::DeRef(value));
}
}

void SetValueAsString(View* obj, const base::string16& new_value) override {
base::Optional<TValue> value = TConverter::FromString(new_value);
if (value)
obj->SetProperty(key_, *value);
}

PropertyFlags GetPropertyFlags() const override {
PropertyFlags flags = PropertyFlags::kEmpty;
if (kTypeIsSerializable)
flags = flags | PropertyFlags::kSerializable;
if (kTypeIsReadOnly)
flags = flags | PropertyFlags::kReadOnly;
return flags;
}

private:
TKey key_;

static constexpr bool kTypeIsSerializable = TConverter::is_serializable;
static constexpr bool kTypeIsReadOnly = TConverter::is_read_only;
};

} // namespace metadata
} // namespace views

Expand Down
4 changes: 4 additions & 0 deletions ui/views/view.cc
Expand Up @@ -3251,6 +3251,10 @@ ADD_PROPERTY_METADATA(bool, UseDefaultFillLayout)
ADD_PROPERTY_METADATA(int, Width)
ADD_PROPERTY_METADATA(int, X)
ADD_PROPERTY_METADATA(int, Y)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kMarginsKey)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kInternalPaddingKey)
ADD_CLASS_PROPERTY_METADATA(LayoutAlignment, kCrossAxisAlignmentKey)
ADD_CLASS_PROPERTY_METADATA(bool, kViewIgnoredByLayoutKey)
END_METADATA

} // namespace views
12 changes: 12 additions & 0 deletions ui/views/view_unittest.cc
Expand Up @@ -5387,6 +5387,18 @@ TEST_F(ViewTest, TestEnabledPropertyMetadata) {
EXPECT_EQ(enabled_property->GetValueAsString(test_view.get()), false_value);
}

TEST_F(ViewTest, TestMarginsPropertyMetadata) {
auto test_view = std::make_unique<View>();
views::metadata::ClassMetaData* view_metadata = View::MetaData();
ASSERT_TRUE(view_metadata);
views::metadata::MemberMetaDataBase* insets_property =
view_metadata->FindMemberData("kMarginsKey");
ASSERT_TRUE(insets_property);
base::string16 insets_value = base::ASCIIToUTF16("8,8,8,8");
insets_property->SetValueAsString(test_view.get(), insets_value);
EXPECT_EQ(insets_property->GetValueAsString(test_view.get()), insets_value);
}

TEST_F(ViewTest, TestEnabledChangedCallback) {
auto test_view = std::make_unique<View>();
bool enabled_changed = false;
Expand Down

0 comments on commit 81a7e9d

Please sign in to comment.