blob: 7d0f86c5dd150c719afdbb420d9da07f38451d11 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_INTERACTION_FRAMEWORK_SPECIFIC_IMPLEMENTATION_H_
#define UI_BASE_INTERACTION_FRAMEWORK_SPECIFIC_IMPLEMENTATION_H_
#include <ostream>
#include <string>
#include "base/component_export.h"
#include "ui/base/interaction/element_identifier.h"
namespace ui {
// Represents a type that has different implementations in different frameworks.
// This provides a very simple RTTI implementation so that we can retrieve
// platform-specific implementations from platform-agnostic systems (such as
// ElementTracker).
//
// To use this class, implement a base class for your implementations:
//
// class ThingBase : public FrameworkSpecificImplementation {
// ~ThingBase() override; // optional, include if class has local data
// // <-- interface methods and common implementation here
// };
//
// Then, in your framework-specific .h file:
//
// class ThingInMyFramework : public ThingBase {
// public:
// ~ThingInMyFramework() override;
// DECLARE_FRAMEWORK_SPECIFIC_METADATA()
// };
//
// In the corresponding .cc file:
//
// DEFINE_FRAMEWORK_SPECIFIC_METADATA(ThingInMyFramework)
//
// If you want to have a derived class that also reports as one of its ancestor
// classes, instead use this:
//
// DEFINE_FRAMEWORK_SPECIFIC_METADATA_SUBCLASS(
// SubclassInMyFramework, SuperclassInMyFramework)
//
// In this case, SubclassInMyFramework::IsA<SuperclassInMyFramework>() will
// return true. The superclass must also DECLARE_FRAMEWORK_SPECIFIC_METADATA().
//
// This is transitive, so if C is declared as a framework specific subclass of
// B, and B of A, then C::IsA<A>() will return true.
//
// While the subclass must be a descendant of the superclass by inheritance,
// not every intermediate class need be registered. Furthermore, inheritance is
// not automatic; DEFINE_FRAMEWORK_SPECIFIC_METADATA_SUBCLASS() is required to
// establish the association.
class COMPONENT_EXPORT(UI_BASE_INTERACTION) FrameworkSpecificImplementation {
public:
// Used by IsA() and AsA() methods to do runtime type-checking.
using FrameworkIdentifier = ElementIdentifier;
FrameworkSpecificImplementation() = default;
FrameworkSpecificImplementation(const FrameworkSpecificImplementation&) =
delete;
virtual ~FrameworkSpecificImplementation() = default;
void operator=(const FrameworkSpecificImplementation&) = delete;
// Returns whether this object is a specific subtype - for example, whether a
// `TrackedElement` is a `views::TrackedElementViews`.
template <typename T>
bool IsA() const {
return AsA<T>();
}
// Dynamically casts this object to a specific subtype, returning null if the
// element is the wrong type. This version converts non-const objects.
template <typename T>
T* AsA() {
return CheckInstanceFrameworkHierarchy(T::GetFrameworkIdentifier())
? static_cast<T*>(this)
: nullptr;
}
// Dynamically casts this object to a specific subtype, returning null if the
// object is the wrong type. This version converts const objects.
template <typename T>
const T* AsA() const {
return CheckInstanceFrameworkHierarchy(T::GetFrameworkIdentifier())
? static_cast<const T*>(this)
: nullptr;
}
// Gets the class name of the implementation.
virtual const char* GetImplementationName() const = 0;
// Gets a string representation of this element.
virtual std::string ToString() const;
protected:
// Checks that `id` corresponds to something in this class' hierarchy.
// Use DECLARE/DEFINE_FRAMEWORK_SPECIFIC_METADATA() - see below - to
// implement this method in your framework-specific derived classes.
virtual bool CheckInstanceFrameworkHierarchy(
FrameworkIdentifier id) const = 0;
};
// These macros can be used to help define platform-specific subclasses of
// base classes derived from FrameworkSpecificImplementation.
// Put this at the top of the class declaration, in the public section.
#define DECLARE_FRAMEWORK_SPECIFIC_METADATA() \
const char* GetImplementationName() const override; \
static FrameworkIdentifier GetFrameworkIdentifier(); \
bool CheckInstanceFrameworkHierarchy(FrameworkIdentifier) const override;
// This is used internally; don't use it directly.
#define DEFINE_FRAMEWORK_SPECIFIC_METADATA_COMMON(ClassName) \
const char* ClassName::GetImplementationName() const { \
return #ClassName; \
} \
ui::FrameworkSpecificImplementation::FrameworkIdentifier \
ClassName::GetFrameworkIdentifier() { \
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(k##ClassName##Identifier); \
return k##ClassName##Identifier; \
}
// Use to define an implementation that will only report as itself. Put this in
// `ClassName`'s .cc file.
#define DEFINE_FRAMEWORK_SPECIFIC_METADATA(ClassName) \
DEFINE_FRAMEWORK_SPECIFIC_METADATA_COMMON(ClassName) \
bool ClassName::CheckInstanceFrameworkHierarchy(FrameworkIdentifier id) \
const { \
return id == GetFrameworkIdentifier(); \
}
// Use to define an implementation that will report as both itself and as
// `BaseClassName`. Put this in `ClassName`'s .cc file.
#define DEFINE_FRAMEWORK_SPECIFIC_METADATA_SUBCLASS(ClassName, BaseClassName) \
static_assert(std::derived_from<ClassName, BaseClassName>); \
DEFINE_FRAMEWORK_SPECIFIC_METADATA_COMMON(ClassName) \
bool ClassName::CheckInstanceFrameworkHierarchy(FrameworkIdentifier id) \
const { \
return id == GetFrameworkIdentifier() || \
BaseClassName::CheckInstanceFrameworkHierarchy(id); \
}
COMPONENT_EXPORT(UI_BASE_INTERACTION)
extern void PrintTo(const FrameworkSpecificImplementation& impl,
std::ostream* os);
COMPONENT_EXPORT(UI_BASE_INTERACTION)
extern std::ostream& operator<<(std::ostream& os,
const FrameworkSpecificImplementation& impl);
} // namespace ui
#endif // UI_BASE_INTERACTION_FRAMEWORK_SPECIFIC_IMPLEMENTATION_H_