blob: d981cd53815a4820609a7fd72541fa3690dc66d1 [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 CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_ENTRY_H_
#define CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_ENTRY_H_
#include <map>
#include <memory>
#include <optional>
#include <string>
#include "base/containers/enum_set.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "chrome/browser/ui/views/side_panel/side_panel_entry_id.h"
#include "chrome/browser/ui/views/side_panel/side_panel_entry_key.h"
#include "chrome/browser/ui/views/side_panel/side_panel_enums.h"
#include "extensions/common/extension_id.h"
#include "ui/base/class_property.h"
#include "ui/base/models/image_model.h"
#include "ui/base/models/menu_model.h"
#include "ui/views/view.h"
class SidePanelEntryScope;
class SidePanelEntryObserver;
enum class SidePanelEntryHideReason;
// This class represents an entry inside the side panel. These are owned by
// a SidePanelRegistry (either a per-tab or a per-window registry).
class SidePanelEntry final : public ui::PropertyHandler {
public:
enum class PanelType {
kMinValue,
// Panel aligned with the web contents.
kContent = kMinValue,
// Panel aligned with the toolbar.
kToolbar,
kMaxValue = kToolbar,
};
using PanelTypes =
base::EnumSet<PanelType, PanelType::kMinValue, PanelType::kMaxValue>;
// The default and minimum acceptable side panel content width.
static constexpr int kSidePanelDefaultContentWidth = 360;
using CreateContentCallback =
base::RepeatingCallback<std::unique_ptr<views::View>(
SidePanelEntryScope&)>;
using Id = SidePanelEntryId;
using Key = SidePanelEntryKey;
// If adding a callback to provide a URL to the 'Open in New Tab' button, you
// must also add a relevant entry in actions.xml because a user action is
// logged on button click.
SidePanelEntry(Key key,
CreateContentCallback create_content_callback,
base::RepeatingCallback<GURL()> open_in_new_tab_url_callback,
base::RepeatingCallback<std::unique_ptr<ui::MenuModel>()>
more_info_callback,
base::RepeatingCallback<int()> default_content_width_callback);
// This constructor should be primarily used for features that want a
// non-kContent PanelType.
SidePanelEntry(PanelType type,
Key key,
CreateContentCallback create_content_callback,
base::RepeatingCallback<int()> default_content_width_callback);
// This constructor is primarily used for extensions. Extensions don't have
// `Open in New Tab` functionality. Other side panels can use this if nothing
// custom is needed (we call the other constructor passing
// base::NullCallback()).
SidePanelEntry(Key key,
CreateContentCallback create_content_callback,
base::RepeatingCallback<int()> default_content_width_callback);
SidePanelEntry(const SidePanelEntry&) = delete;
SidePanelEntry& operator=(const SidePanelEntry&) = delete;
~SidePanelEntry() override;
// Creates the content to be shown inside the side panel when this entry is
// shown.
std::unique_ptr<views::View> GetContent();
void CacheView(std::unique_ptr<views::View> view);
void ClearCachedView();
views::View* CachedView() {
return content_view_ ? content_view_.get() : nullptr;
}
// Called when the entry has been shown/hidden in the side panel.
void OnEntryShown();
void OnEntryWillHide(SidePanelEntryHideReason reason);
void OnEntryHideCancelled();
void OnEntryHidden();
PanelType type() const { return type_; }
const Key& key() const { return key_; }
void set_last_open_trigger(std::optional<SidePanelOpenTrigger> trigger) {
last_open_trigger_ = trigger;
}
std::optional<SidePanelOpenTrigger> last_open_trigger() const {
return last_open_trigger_;
}
// Sets whether a button will be shown ephemerally in the toolbar when the
// entry is showing in the side panel. Note, even if this is false the button
// would still be seen if pinned.
void set_should_show_ephemerally_in_toolbar(
bool should_show_ephemerally_in_toolbar) {
should_show_ephemerally_in_toolbar_ = should_show_ephemerally_in_toolbar;
}
bool should_show_ephemerally_in_toolbar() const {
return should_show_ephemerally_in_toolbar_;
}
// Whether the header should be visible when the entry is shown.
void set_should_show_header(bool should_show_header) {
should_show_header_ = should_show_header;
}
bool should_show_header() const { return should_show_header_; }
// Whether the outline should be visible when the entry is shown.
void set_should_show_outline(bool should_show_outline) {
should_show_outline_ = should_show_outline;
}
bool should_show_outline() const { return should_show_outline_; }
void AddObserver(SidePanelEntryObserver* observer);
void RemoveObserver(SidePanelEntryObserver* observer);
// Gets the 'Open in New Tab' URL. Returns an empty GURL if this function is
// unavailable for the current side panel entry.
GURL GetOpenInNewTabURL() const;
// Gets the menu model for the more info menu if the current side panel entry
// has one, otherwise null.
std::unique_ptr<ui::MenuModel> GetMoreInfoMenuModel() const;
// Returns whether the side panel entry has a defined callback for getting the
// open new tab button URL.
bool SupportsNewTabButton();
// Returns whether the side panel entry has a defined callback for the more
// info button.
bool SupportsMoreInfoButton();
// Resets the `entry_show_triggered_timestamp_` so we don't track metrics
// incorrectly.
void ResetLoadTimestamp();
void set_scope(SidePanelEntryScope* scope) { scope_ = scope; }
base::WeakPtr<SidePanelEntry> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
// Gets the default content width for this entry, if one is specified.
int GetDefaultContentWidth() const;
// Allows tests to override the default width for an existing entry.
void SetDefaultContentWidthForTesting(int width) {
default_content_width_ = width;
}
private:
const PanelType type_;
const Key key_;
std::unique_ptr<views::View> content_view_;
// Whether a button will be shown ephemerally in the toolbar when the entry is
// showing in the side panel. Note, even if this is false the button would
// still be seen if pinned.
bool should_show_ephemerally_in_toolbar_ = true;
// Whether the side panel header will be visible when this entry is showing.
bool should_show_header_ = true;
// Whether the side panel outline will be visible when this entry is showing.
bool should_show_outline_ = true;
// Scope of this entry, will outlive the entry and its content.
raw_ptr<SidePanelEntryScope> scope_ = nullptr;
CreateContentCallback create_content_callback_;
// If this returns an empty GURL, the 'Open in New Tab' button is hidden.
base::RepeatingCallback<GURL()> open_in_new_tab_url_callback_;
// If this returns null, the more info button is hidden.
base::RepeatingCallback<std::unique_ptr<ui::MenuModel>()> more_info_callback_;
// When specified sets the default starting width for this entry. However, if
// the user manually changes the size of the side panel that preference is
// used instead (prefs::kSidePanelIdToWidth). If nothing is specified, then
// the default minimum content width of the side panel is used.
base::RepeatingCallback<int()> default_content_width_callback_;
// Timestamp of when the side panel was triggered to be shown.
base::TimeTicks entry_show_triggered_timestamp_;
base::TimeTicks entry_shown_timestamp_;
base::ObserverList<SidePanelEntryObserver> observers_;
// The last trigger that caused this side panel entry to be shown. This is
// used for metrics.
std::optional<SidePanelOpenTrigger> last_open_trigger_;
// The default minimum content width for the side panel that can be overridden
// for testing. This is used if the default_content_width_callback_ is not
// set. However, if the user manually changes the size of the side panel that
// preference is used instead (prefs::kSidePanelIdToWidth).
int default_content_width_ = kSidePanelDefaultContentWidth;
base::WeakPtrFactory<SidePanelEntry> weak_factory_{this};
};
extern const ui::ClassProperty<bool>* const
kShouldShowTitleInSidePanelHeaderKey;
#endif // CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_ENTRY_H_