blob: 7ca65a540fef8be61549aea9852f5d7c5723ae99 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/tabs/split_view_iph_controller.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/user_education/browser_user_education_interface.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "components/feature_engagement/public/event_constants.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "ui/views/interaction/element_tracker_views.h"
DEFINE_USER_DATA(SplitViewIphController);
SplitViewIphController::SplitViewIphController(
BrowserWindowInterface* interface)
: browser_window_interface_(interface),
recent_tabs_(kNumTabsTracked),
scoped_data_(interface->GetUnownedUserDataHost(), *this) {
browser_window_interface_->GetTabStripModel()->AddObserver(this);
}
SplitViewIphController::~SplitViewIphController() = default;
SplitViewIphController* SplitViewIphController::From(
BrowserWindowInterface* interface) {
return ui::ScopedUnownedUserData<SplitViewIphController>::Get(
interface->GetUnownedUserDataHost());
}
void SplitViewIphController::OnSplitTabChanged(const SplitTabChange& change) {
if (change.type == SplitTabChange::Type::kAdded &&
change.GetAddedChange()->reason() !=
SplitTabChange::SplitTabAddReason::kSplitTabUpdated) {
BrowserUserEducationInterface::From(browser_window_interface_)
->NotifyAdditionalConditionEvent(
feature_engagement::events::kSplitViewCreated);
MaybeShowPromo(feature_engagement::kIPHSideBySidePinnableFeature);
}
}
void SplitViewIphController::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
if (change.type() == TabStripModelChange::kRemoved) {
RemoveTabFromTracker(change.GetRemove());
}
if (selection.active_tab_changed()) {
tabs::TabInterface* const active_tab = selection.new_tab;
if (recent_tabs_.size() < kNumTabsTracked ||
(recent_tabs_[kMostRecentTabTrackerIndex] != active_tab &&
recent_tabs_[kLeastRecentTabTrackerIndex] != active_tab)) {
AddNewTabToTracker(active_tab);
} else if (++tab_switch_count_ >=
features::kSideBySideIphTabSwitchCount.Get()) {
MaybeShowPromo(feature_engagement::kIPHSideBySideTabSwitchFeature);
}
}
}
void SplitViewIphController::AddNewTabToTracker(tabs::TabInterface* new_tab) {
// Replace the oldest recent tab with the new tab.
if (recent_tabs_.size() >= kNumTabsTracked) {
recent_tabs_.pop_back();
}
recent_tabs_.push_front(new_tab);
// Reset tab_switch_count since we're entering a new tab
tab_switch_count_ = 0;
}
void SplitViewIphController::RemoveTabFromTracker(
const TabStripModelChange::Remove* remove_contents) {
for (const auto& contents : remove_contents->contents) {
if (recent_tabs_.empty()) {
return;
}
if (recent_tabs_[kMostRecentTabTrackerIndex] == contents.tab) {
recent_tabs_.pop_front();
} else if (recent_tabs_.size() == kNumTabsTracked &&
recent_tabs_[kLeastRecentTabTrackerIndex] == contents.tab) {
recent_tabs_.pop_back();
}
}
}
ui::TrackedElement* SplitViewIphController::GetTabSwitchIPHAnchor(
BrowserView* browser_view) {
TabStripModel* tab_strip_model =
browser_window_interface_->GetTabStripModel();
// Default to no tab if tabs have not been switched yet.
int tab_strip_tab_index = TabStripModel::kNoTab;
if (recent_tabs_.size() >= kNumTabsTracked) {
const int inactive_tab_index =
browser_window_interface_->GetActiveTabInterface() ==
recent_tabs_[kMostRecentTabTrackerIndex]
? kLeastRecentTabTrackerIndex
: kMostRecentTabTrackerIndex;
tab_strip_tab_index =
tab_strip_model->GetIndexOfTab(recent_tabs_[inactive_tab_index]);
}
if (tab_strip_tab_index == TabStripModel::kNoTab) {
return nullptr;
}
views::View* tab_view =
browser_view->tab_strip_view()->GetTabAnchorViewAt(tab_strip_tab_index);
return tab_view
? views::ElementTrackerViews::GetInstance()->GetElementForView(
tab_view)
: nullptr;
}
void SplitViewIphController::MaybeShowPromo(const base::Feature& feature) {
BrowserUserEducationInterface::From(browser_window_interface_)
->MaybeShowFeaturePromo(feature);
}