blob: b9f8b55ccf290b1d4d0de4798b17f178fe2759bc [file] [log] [blame]
// Copyright 2021 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_BASE_INTERACTION_ELEMENT_IDENTIFIER_H_
#define UI_BASE_INTERACTION_ELEMENT_IDENTIFIER_H_
#include <stdint.h>
#include <ostream>
#include "base/component_export.h"
#include "base/containers/contains.h"
#include "ui/base/class_property.h"
// Overview:
// ElementIdentifier provides a named opaque value that can be used to identify
// individual (or potentially groups of) elements in the UI.
//
// Unique identifier constants must be both declared and defined. To create a
// publicly-visible identifier, declare a new unique value in your .h file,
// with:
//
// DECLARE_ELEMENT_IDENTIFIER_VALUE(kMyIdentifierName);
//
// In the corresponding .cc file, make sure it is defined:
//
// DEFINE_ELEMENT_IDENTIFIER_VALUE(kMyIdentifierName);
//
// If you want to add an identifier as a class member, use the following:
//
// class MyClass {
// DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(MyClass, kMyIdentifierName);
// };
//
// Then in the corresponding .cc file, add the following:
//
// DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(MyClass, kMyIdentifierValue);
//
// If you want to create an identifier local to a .cc file or to a method, you
// can instead use the following all-in-one declaration:
//
// DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kMyIdentifierName);
//
// That's it! You can now initialize an ElementIdentifier using this value, or
// pass it directly to a method:
//
// ElementIdentifier my_id = kMyIdentifierName;
// ElementIdentifier my_id2 = my_id;
// MyFuncThatTakesAnIdentifier(kMyIdentifierName);
// MyFuncThatTakesAnIdentifier(my_id2);
//
// ElementIdentifier behaves more or less like other mutable primitive types; it
// is default-constructable (producing a null value) and supports the ==, !=, <,
// !, and (bool) operators as well as assignment and copy [1]. This means you
// can use ElementIdentifier as a key in std::set and std::map, and it is safe
// to use in both DCHECK and test assertions:
//
// ElementIdentifier id1;
// ElementIdentifier id2 = kMyIdentifierName;
// std::map<ElementIdentifier, int> map;
// map.emplace(id2, 4);
// DCHECK(!id1);
// EXPECT_TRUE(static_cast<bool>(id2));
// DCHECK_NE(id1, id2);
// EXPECT_FALSE(base::Contains(map, id1));
// ASSERT_EQ(4, map[id2]);
//
// -----
//
// [1] Please note that while operator < will provide a strict ordering, the
// specific order of two ElementIdentifier constants may vary by build and
// should not be relied upon; operator < is only provided for compatibility
// with sorted STL containers.
namespace ui {
namespace internal {
// Defines the underlying value that an ElementIdentifier holds (namely, the
// address of an instance of this class). Because these objects are only
// declared statically, the value of an ElementIdentifier is always valid and
// two ElementIdentifiers are equal if and only if they hold the address of the
// same instance of this class.
//
// Instances of this object are named for debugging/logging purposes only, the
// value of name() should never be used for any other purpose.
struct ElementIdentifierImpl {
// The name of the identifier; only used in testing via PrintTo().
const char* const name;
};
} // namespace internal
// Holds a globally-unqiue, value-typed identifier from a set of identifiers
// which can be declared in any static scope using
// DECLARE_UNIQUE_ELEMENT_VALUE().
//
// This type is comparable and supports operator bool and negation, where
// default-constructed instances have false value and all other values evaluate
// as true. It can also be used as the key in std::set, std::map, and similar
// collections.
class COMPONENT_EXPORT(UI_BASE) ElementIdentifier final {
public:
// Creates a null identifier.
constexpr ElementIdentifier() = default;
// Avoid this constructor - it is used internally by the
// DECLARE_ELEMENT_IDENTIFIER_VALUE() macro.
explicit constexpr ElementIdentifier(
const internal::ElementIdentifierImpl* provider)
: handle_(provider) {}
constexpr explicit operator bool() const { return handle_ != nullptr; }
constexpr bool operator!() const { return !handle_; }
constexpr bool operator==(const ElementIdentifier& other) const {
return handle_ == other.handle_;
}
constexpr bool operator!=(const ElementIdentifier& other) const {
return handle_ != other.handle_;
}
constexpr bool operator<(const ElementIdentifier& other) const {
return handle_ < other.handle_;
}
// Included for interoperability with PropertyHandler. Only works because this
// class contains a single pointer, has a trivial destructor, and has no
// virtual methods.
intptr_t raw_value() const { return reinterpret_cast<intptr_t>(handle_); }
// Included for interoperability with PropertyHandler. Can be used when
// overriding PropertyHandler::AfterPropertyChange() to recover the old
// identifier value.
static ElementIdentifier FromRawValue(intptr_t value) {
return ElementIdentifier(
reinterpret_cast<const internal::ElementIdentifierImpl*>(value));
}
private:
// The value of the identifier. Because all non-null values point to static
// ElementIdentifierImpl objects this can be treated as a value from a set of
// unique, opaque handles.
const void* handle_ = nullptr;
};
// The context of an element is unique to the top-level, primary window that the
// element is associated with. Elements in secondary UI (bubbles, menus,
// drop-downs, etc.) will be associated with the same context as elements in the
// primary window itself.
//
// The value used should be consistent across a toolkit and unique between
// primary windows; a memory address or handle of the window object can
// typically be used (e.g. in Views, we use the address of the primary window's
// Widget). A zero or null value should always correspond to "no context".
//
// Please note that you must consistently use the same pointer or handle type
// across your framework when creating contexts; because of the vagaries of C++
// up- and down-casting (especially with multiple inheritance) constructing an
// ElementContext from different pointer types can produce different results,
// even for the same underlying object.
//
// ElementContext objects are assignable, have boolean value based on whether
// the underlying value is null, and support operator < for use in maps and
// sets.
class COMPONENT_EXPORT(UI_BASE) ElementContext {
public:
ElementContext() = default;
template <class T>
explicit ElementContext(T* value)
: value_(reinterpret_cast<uintptr_t>(value)) {}
template <class T>
explicit ElementContext(T value) : value_(static_cast<uintptr_t>(value)) {}
explicit operator const void*() const {
return reinterpret_cast<const void*>(value_);
}
explicit operator uintptr_t() const { return value_; }
explicit operator intptr_t() const { return static_cast<intptr_t>(value_); }
explicit operator bool() const { return value_ != 0; }
bool operator!() const { return !value_; }
bool operator==(const ElementContext& other) const {
return value_ == other.value_;
}
bool operator!=(const ElementContext& other) const {
return value_ != other.value_;
}
bool operator<(const ElementContext& other) const {
return value_ < other.value_;
}
private:
uintptr_t value_ = 0;
};
COMPONENT_EXPORT(UI_BASE)
extern void PrintTo(ElementIdentifier element_identifier, std::ostream* os);
COMPONENT_EXPORT(UI_BASE)
extern void PrintTo(ElementContext element_context, std::ostream* os);
// Required for interoperability with PropertyHandler.
template <>
class ClassPropertyCaster<ui::ElementIdentifier> {
public:
static int64_t ToInt64(ui::ElementIdentifier x) { return x.raw_value(); }
static ui::ElementIdentifier FromInt64(int64_t x) {
return ui::ElementIdentifier::FromRawValue(x);
}
};
} // namespace ui
// Declaring identifiers outside a scope:
// Use this code in the .h file to declare a new identifier.
#define DECLARE_ELEMENT_IDENTIFIER_VALUE(IdentifierName) \
extern const ui::internal::ElementIdentifierImpl IdentifierName##Provider; \
constexpr ui::ElementIdentifier IdentifierName(&IdentifierName##Provider)
// Use this code in the .cc file to define a new identifier.
#define DEFINE_ELEMENT_IDENTIFIER_VALUE(IdentifierName) \
constexpr ui::internal::ElementIdentifierImpl IdentifierName##Provider { \
#IdentifierName \
}
// Declaring identifiers in a class:
// Use this code in your class declaration in its .h file to declare an
// identifier that is scoped to your class.
#define DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(ClassName, IdentifierName) \
static constexpr ui::internal::ElementIdentifierImpl \
IdentifierName##Provider{#ClassName "::" #IdentifierName}; \
static constexpr ui::ElementIdentifier IdentifierName { \
&IdentifierName##Provider \
}
// Use this code in your class definition .cc file to define the member
// variables
#define DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ClassName, IdentifierName) \
constexpr ui::internal::ElementIdentifierImpl \
ClassName::IdentifierName##Provider; \
constexpr ui::ElementIdentifier ClassName::IdentifierName
// Declaring local identifiers in functions, class methods, or local to a .cc
// file:
// Use this code to declare a local identifier in a function body.
#define DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(IdentifierName) \
static constexpr ui::internal::ElementIdentifierImpl \
IdentifierName##Provider{#IdentifierName}; \
constexpr ui::ElementIdentifier IdentifierName(&IdentifierName##Provider)
#endif // UI_BASE_INTERACTION_ELEMENT_IDENTIFIER_H_