blob: 43534354d8505158a55713755758fcc1d624a8de [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 COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SPECIFICATION_H_
#define COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SPECIFICATION_H_
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "components/user_education/common/help_bubble_params.h"
#include "components/user_education/common/tutorial_identifier.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_tracker.h"
namespace base {
struct Feature;
}
namespace gfx {
struct VectorIcon;
}
namespace user_education {
// Specifies the parameters for a feature promo and its associated bubble.
class FeaturePromoSpecification {
public:
// The body text (specified by |bubble_body_string_id|) can have parameters
// that can be specified situationally. When specifying these parameters,
// use a |StringReplacements| object.
using StringReplacements = std::vector<std::u16string>;
// Optional method that filters a set of potential `elements` to choose and
// return the anchor element, or null if none of the inputs is appropriate.
// This method can return an element different from the input list, or null
// if no valid element is found (this will cause the IPH not to run).
using AnchorElementFilter = base::RepeatingCallback<ui::TrackedElement*(
const ui::ElementTracker::ElementList& elements)>;
// Describes the type of promo. Used to configure defaults for the promo's
// bubble.
enum class PromoType {
// Uninitialized/invalid specification.
kUnspecifiied,
// A toast-style promo.
kToast,
// A snooze-style promo.
kSnooze,
// A tutorial promo.
kTutorial,
// A simple promo that acts like a toast but without the required
// accessibility data.
kLegacy,
};
// Represents a command or command accelerator. Can be valueless (falsy) if
// neither a command ID nor an explicit accelerator is specified.
class AcceleratorInfo {
public:
// You can assign either an int (command ID) or a ui::Accelerator to an
// AcceleratorInfo object.
using ValueType = absl::variant<int, ui::Accelerator>;
AcceleratorInfo();
AcceleratorInfo(const AcceleratorInfo& other);
~AcceleratorInfo();
explicit AcceleratorInfo(ValueType value);
AcceleratorInfo& operator=(ValueType value);
AcceleratorInfo& operator=(const AcceleratorInfo& other);
explicit operator bool() const;
bool operator!() const { return !static_cast<bool>(*this); }
ui::Accelerator GetAccelerator(
const ui::AcceleratorProvider* provider) const;
private:
ValueType value_;
};
struct DemoPageInfo {
std::string display_title;
std::string display_description;
base::RepeatingClosure setup_for_feature_promo_callback;
explicit DemoPageInfo(
std::string display_title_ = std::string(),
std::string display_description_ = std::string(),
base::RepeatingClosure setup_for_feature_promo_callback_ =
base::DoNothing());
~DemoPageInfo();
DemoPageInfo(const DemoPageInfo& other);
DemoPageInfo& operator=(const DemoPageInfo& other);
};
FeaturePromoSpecification();
FeaturePromoSpecification(FeaturePromoSpecification&& other);
~FeaturePromoSpecification();
FeaturePromoSpecification& operator=(FeaturePromoSpecification&& other);
// Specifies a standard toast promo.
// Because toasts are transient, they expect a separate screen reader prompt.
// It is recommended that the prompt include an
static FeaturePromoSpecification CreateForToastPromo(
const base::Feature& feature,
ui::ElementIdentifier anchor_element_id,
int body_text_string_id,
int accessible_text_string_id,
AcceleratorInfo accessible_accelerator);
// Specifies a promo with snooze buttons.
static FeaturePromoSpecification CreateForSnoozePromo(
const base::Feature& feature,
ui::ElementIdentifier anchor_element_id,
int body_text_string_id);
// Specifies a promo that launches a tutorial.
static FeaturePromoSpecification CreateForTutorialPromo(
const base::Feature& feature,
ui::ElementIdentifier anchor_element_id,
int body_text_string_id,
TutorialIdentifier tutorial_id);
// Specifies a text-only promo without additional accessibility information.
// Deprecated. Only included for backwards compatibility with existing
// promos. This is the only case in which |feature| can be null, and if it is
// the result can only be used for a critical promo.
static FeaturePromoSpecification CreateForLegacyPromo(
const base::Feature* feature,
ui::ElementIdentifier anchor_element_id,
int body_text_string_id);
// Set the optional bubble title. This text appears above the body text in a
// slightly larger font.
FeaturePromoSpecification& SetBubbleTitleText(int title_text_string_id);
// Set the optional bubble icon. This is displayed next to the title or body
// text.
FeaturePromoSpecification& SetBubbleIcon(const gfx::VectorIcon* bubble_icon);
// Set the bubble arrow. Default is top-left.
FeaturePromoSpecification& SetBubbleArrow(HelpBubbleArrow bubble_arrow);
// Set the anchor element filter.
FeaturePromoSpecification& SetAnchorElementFilter(
AnchorElementFilter anchor_element_filter);
// Set whether we should look for the anchor element in any context.
// Default is false. Since usually we only want to create the bubble in the
// currently active window, this is only really useful for cases where there
// is a floating window, WebContents, or tab-modal dialog that can become
// detached from the current active window and therefore requires its own
// unique context.
FeaturePromoSpecification& SetInAnyContext(bool in_any_context);
// Get the anchor element based on `anchor_element_id`,
// `anchor_element_filter`, and `context`.
ui::TrackedElement* GetAnchorElement(ui::ElementContext context) const;
const base::Feature* feature() const { return feature_; }
PromoType promo_type() const { return promo_type_; }
ui::ElementIdentifier anchor_element_id() const { return anchor_element_id_; }
const AnchorElementFilter& anchor_element_filter() const {
return anchor_element_filter_;
}
bool in_any_context() const { return in_any_context_; }
int bubble_body_string_id() const { return bubble_body_string_id_; }
const std::u16string& bubble_title_text() const { return bubble_title_text_; }
const gfx::VectorIcon* bubble_icon() const { return bubble_icon_; }
HelpBubbleArrow bubble_arrow() const { return bubble_arrow_; }
int screen_reader_string_id() const { return screen_reader_string_id_; }
const AcceleratorInfo& screen_reader_accelerator() const {
return screen_reader_accelerator_;
}
const DemoPageInfo& demo_page_info() const { return demo_page_info_; }
FeaturePromoSpecification& SetDemoPageInfo(DemoPageInfo demo_page_info);
const TutorialIdentifier& tutorial_id() const { return tutorial_id_; }
private:
static constexpr HelpBubbleArrow kDefaultBubbleArrow =
HelpBubbleArrow::kTopRight;
FeaturePromoSpecification(const base::Feature* feature,
PromoType promo_type,
ui::ElementIdentifier anchor_element_id,
int bubble_body_string_id);
raw_ptr<const base::Feature> feature_ = nullptr;
// The type of promo. A promo with type kUnspecified is not valid.
PromoType promo_type_ = PromoType::kUnspecifiied;
// The element identifier of the element to attach the promo to.
ui::ElementIdentifier anchor_element_id_;
// Whether we are allowed to search for the anchor element in any context.
bool in_any_context_ = false;
// The filter to use if there is more than one matching element, or
// additional processing is needed (default is to always use the first
// matching element).
AnchorElementFilter anchor_element_filter_;
// Text to be displayed in the promo bubble body. Should not be zero for
// valid bubbles. We keep the string ID around because we can specify format
// parameters when we actually create the bubble.
int bubble_body_string_id_ = 0;
// Optional text that is displayed at the top of the bubble, in a slightly
// more prominent font.
std::u16string bubble_title_text_;
// Optional icon that is displayed next to bubble text.
raw_ptr<const gfx::VectorIcon> bubble_icon_ = nullptr;
// Optional arrow pointing to the promo'd element. Defaults to top left.
HelpBubbleArrow bubble_arrow_ = kDefaultBubbleArrow;
// Optional screen reader announcement that replaces bubble text when the
// bubble is first announced.
int screen_reader_string_id_ = 0;
// Accelerator that is used to fill in a parametric field in
// screen_reader_string_id_.
AcceleratorInfo screen_reader_accelerator_;
// Information to be displayed on the demo page
DemoPageInfo demo_page_info_;
// Tutorial identifier if the user decides to view a tutorial.
TutorialIdentifier tutorial_id_;
};
} // namespace user_education
#endif // COMPONENTS_USER_EDUCATION_COMMON_FEATURE_PROMO_SPECIFICATION_H_