Skip to content

Commit

Permalink
Merge pull request #4671 from guifel/guifel-1/mru-clycling-ctrl-tab
Browse files Browse the repository at this point in the history
Most recently used tab with Ctrl-Tab cycling
  • Loading branch information
simonhong authored Oct 7, 2020
2 parents 63f812a + 9a91afa commit c15b8ce
Show file tree
Hide file tree
Showing 23 changed files with 465 additions and 5 deletions.
3 changes: 3 additions & 0 deletions app/brave_generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ By installing this extension, you are agreeing to the Google Widevine Terms of U
<message name="IDS_SETTINGS_ALWAYS_SHOW_BOOKMARK_BAR_ON_NTP" desc="The label for settings switch controlling the visibility of bookmarks bar on NTP">
Always show bookmarks on new tab page
</message>
<message name="IDS_SETTINGS_BRAVE_MRU_CYCLING_LABEL" desc="The label to activate MRU cycling with Ctrl-tab">
Cycle through the most recently used tabs with Ctrl-Tab
</message>
<message name="IDS_SETTINGS_APPEARANCE_SETTINGS_SHOW_AUTOCOMPLETE_IN_ADDRESS_BAR" desc="The label for settings switch controlling the showing of autocomplete in address bar">
Show autocomplete in address bar
</message>
Expand Down
1 change: 1 addition & 0 deletions browser/brave_profile_prefs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterBooleanPref(
brave_rewards::prefs::kHideButton,
false);
registry->RegisterBooleanPref(kMRUCyclingEnabled, false);

brave_sync::Prefs::RegisterProfilePrefs(registry);

Expand Down
2 changes: 2 additions & 0 deletions browser/brave_profile_prefs_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ IN_PROC_BROWSER_TEST_F(BraveProfilePrefsBrowserTest, MiscBravePrefs) {
EXPECT_FALSE(
browser()->profile()->GetPrefs()->GetBoolean(kOptedIntoCryptoWallets));
#endif
EXPECT_FALSE(
browser()->profile()->GetPrefs()->GetBoolean(kMRUCyclingEnabled));
#if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
EXPECT_FALSE(
browser()->profile()->GetPrefs()->GetBoolean(kBraveGCMChannelStatus));
Expand Down
2 changes: 2 additions & 0 deletions browser/extensions/api/settings_private/brave_prefs_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ const PrefsUtil::TypedPrefMap& BravePrefsUtil::GetWhitelistedKeys() {
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_brave_whitelist)[kAlwaysShowBookmarkBarOnNTP] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
(*s_brave_whitelist)[kMRUCyclingEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
// WebTorrent pref
(*s_brave_whitelist)[kWebTorrentEnabled] =
settings_api::PrefType::PREF_TYPE_BOOLEAN;
Expand Down
12 changes: 12 additions & 0 deletions browser/resources/settings/brave_overrides/appearance_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ RegisterPolymerTemplateModifications({
<settings-brave-appearance-toolbar prefs="{{prefs}}"></settings-brave-appearance-toolbar>
`)
}
const zoomLevel = templateContent.getElementById('zoomLevel')
if (!zoomLevel || !zoomLevel.parentNode) {
console.error(`[Brave Settings Overrides] Couldn't find zoomLevel`)
} else {
zoomLevel.parentNode.insertAdjacentHTML('afterend', `
<settings-toggle-button
class="hr"
pref="{{prefs.brave.mru_cycling_enabled}}"
label="${I18nBehavior.i18n('mruCyclingSettingLabel')}">
</settings-toggle-button>
`)
}
// Super referral themes prefs
const pages = templateContent.getElementById('pages')
if (!pages) {
Expand Down
9 changes: 6 additions & 3 deletions browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ source_set("ui") {
"brave_browser_command_controller.h",
"brave_browser_content_setting_bubble_model_delegate.cc",
"brave_browser_content_setting_bubble_model_delegate.h",
"brave_browser_window.h",
"brave_layout_constants.cc",
"brave_layout_constants.h",
"brave_pages.cc",
Expand All @@ -73,8 +74,6 @@ source_set("ui") {
"content_settings/brave_content_setting_image_models.h",
"omnibox/brave_omnibox_client_impl.cc",
"omnibox/brave_omnibox_client_impl.h",
"tabs/brave_tab_menu_model.cc",
"tabs/brave_tab_menu_model.h",
"toolbar/brave_app_menu_model.cc",
"toolbar/brave_app_menu_model.h",
"webui/brave_new_tab_message_handler.cc",
Expand Down Expand Up @@ -181,6 +180,7 @@ source_set("ui") {
}

deps = [
"tabs",
"//base",
"//brave/app:command_ids",
"//brave/app/theme:brave_theme_resources",
Expand Down Expand Up @@ -368,7 +368,10 @@ source_set("ui") {
}

if (toolkit_views) {
deps += [ "//ui/views" ]
deps += [
"//ui/events",
"//ui/views",
]

if (enable_brave_translate_extension) {
deps += [ "//components/translate/core/browser" ]
Expand Down
18 changes: 18 additions & 0 deletions browser/ui/brave_browser_window.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_UI_BRAVE_BROWSER_WINDOW_H_
#define BRAVE_BROWSER_UI_BRAVE_BROWSER_WINDOW_H_

#include "chrome/browser/ui/browser_window.h"

class BraveBrowserWindow : public BrowserWindow {
public:
~BraveBrowserWindow() override {}

virtual void StartTabCycling() = 0;
};

#endif // BRAVE_BROWSER_UI_BRAVE_BROWSER_WINDOW_H_
21 changes: 21 additions & 0 deletions browser/ui/tabs/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
source_set("tabs") {
# Set due to //chrome/browser and //chrome/browser/ui circular dependencies.
check_includes = false

if (!is_android) {
sources = [
"brave_tab_menu_model.cc",
"brave_tab_menu_model.h",
"brave_tab_strip_model.cc",
"brave_tab_strip_model.h",
]

deps = [
"//brave/common/",
"//chrome/app:generated_resources",
"//components/prefs",
"//components/sessions",
"//content/public/browser",
]
}
}
74 changes: 74 additions & 0 deletions browser/ui/tabs/brave_tab_strip_model.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "brave/browser/ui/tabs/brave_tab_strip_model.h"

#include <algorithm>

#include "brave/common/pref_names.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_contents.h"

BraveTabStripModel::BraveTabStripModel(TabStripModelDelegate* delegate,
Profile* profile)
: TabStripModel(delegate, profile) {}
BraveTabStripModel::~BraveTabStripModel() {}

void BraveTabStripModel::SelectRelativeTab(bool forward,
UserGestureDetails detail) {
if (contents_data_.empty())
return;

bool is_mru_enabled = profile()->GetPrefs()->GetBoolean(kMRUCyclingEnabled);

if (is_mru_enabled) {
SelectMRUTab(forward, detail);
} else {
TabStripModel::SelectRelativeTab(forward, detail);
}
}

void BraveTabStripModel::SelectMRUTab(bool forward, UserGestureDetails detail) {
if (mru_cycle_list_.empty()) {
// Start cycling

Browser* browser = chrome::FindBrowserWithWebContents(GetWebContentsAt(0));
if (!browser)
return;

// Create a list of tab indexes sorted by time of last activation
for (int i = 0; i < count(); ++i) {
mru_cycle_list_.push_back(i);
}

std::sort(mru_cycle_list_.begin(), mru_cycle_list_.end(),
[this](int a, int b) {
return GetWebContentsAt(a)->GetLastActiveTime() >
GetWebContentsAt(b)->GetLastActiveTime();
});

// Tell the cycling controller that we start cycling to handle tabs keys
static_cast<BraveBrowserWindow*>(browser->window())->StartTabCycling();
}

if (forward) {
std::rotate(mru_cycle_list_.begin(),
mru_cycle_list_.begin() + 1,
mru_cycle_list_.end());
} else {
std::rotate(mru_cycle_list_.rbegin(),
mru_cycle_list_.rbegin() + 1,
mru_cycle_list_.rend());
}

ActivateTabAt(mru_cycle_list_[0], detail);
}

void BraveTabStripModel::StopMRUCycling() {
mru_cycle_list_.clear();
}
38 changes: 38 additions & 0 deletions browser/ui/tabs/brave_tab_strip_model.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_UI_TABS_BRAVE_TAB_STRIP_MODEL_H_
#define BRAVE_BROWSER_UI_TABS_BRAVE_TAB_STRIP_MODEL_H_

#include <vector>

#include "chrome/browser/ui/tabs/tab_strip_model.h"

class BraveTabStripModel : public TabStripModel {
public:
explicit BraveTabStripModel(TabStripModelDelegate* delegate,
Profile* profile);

~BraveTabStripModel() override;

BraveTabStripModel(const BraveTabStripModel&) = delete;
BraveTabStripModel operator=(const BraveTabStripModel&) = delete;

void SelectRelativeTab(bool forward, UserGestureDetails detail) override;

// Set the next tab when doing a MRU cycling with Ctrl-tab
void SelectMRUTab(
bool forward,
UserGestureDetails detail = UserGestureDetails(GestureType::kOther));

// Stop MRU cycling, called when releasing the Ctrl key
void StopMRUCycling();

private:
// List of tab indexes sorted by most recently used
std::vector<int> mru_cycle_list_;
};

#endif // BRAVE_BROWSER_UI_TABS_BRAVE_TAB_STRIP_MODEL_H_
23 changes: 23 additions & 0 deletions browser/ui/tabs/test/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright (c) 2020 The Brave Authors. All rights reserved.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/. */

source_set("browser_tests") {
testonly = true
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]

sources = [
"brave_tab_strip_model_browsertest.cc",
]

deps = [
"//brave/common:pref_names",
"//chrome/app:command_ids",
"//chrome/browser",
"//chrome/browser/ui",
"//chrome/test:test_support_ui",
"//components/prefs",
"//content/test:test_support",
]
}
76 changes: 76 additions & 0 deletions browser/ui/tabs/test/brave_tab_strip_model_browsertest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "brave/common/pref_names.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/browser_test.h"

using BraveTabStripModelTest = InProcessBrowserTest;

IN_PROC_BROWSER_TEST_F(BraveTabStripModelTest, MRUCyclingBasic) {
TabStripModel* tab_strip_model = browser()->tab_strip_model();

// Open 3 tabs
chrome::NewTab(browser());
chrome::NewTab(browser());
EXPECT_EQ(browser()->tab_strip_model()->count(), 3);
EXPECT_EQ(browser()->tab_strip_model()->active_index(), 2);

// Normal next tab expected by default, 2 -> 0
chrome::ExecuteCommand(browser(), IDC_SELECT_NEXT_TAB);
EXPECT_EQ(tab_strip_model->active_index(), 0);

// Activate MRU cycling
browser()->profile()->GetPrefs()->SetBoolean(kMRUCyclingEnabled, true);

// MRU cycling, 0 -> 2
chrome::ExecuteCommand(browser(), IDC_SELECT_NEXT_TAB);
EXPECT_EQ(tab_strip_model->active_index(), 2);
// Ctrl is not being released 2 -> 1
chrome::ExecuteCommand(browser(), IDC_SELECT_NEXT_TAB);
EXPECT_EQ(tab_strip_model->active_index(), 1);
// 1 -> 2
chrome::ExecuteCommand(browser(), IDC_SELECT_PREVIOUS_TAB);
EXPECT_EQ(tab_strip_model->active_index(), 2);
}

// Check MRU Cycling is restarted when tab is closed during the mru cycling.
// User can close current tab while cycling like this.
// For example on linux, when user does "Ctrl + tab -> Ctrl + F4 -> Ctrl + tab",
// second Ctrl + tab should restart mru cycling.
IN_PROC_BROWSER_TEST_F(BraveTabStripModelTest, TabClosingWhileMRUCycling) {
// Activate MRU cycling
browser()->profile()->GetPrefs()->SetBoolean(kMRUCyclingEnabled, true);

TabStripModel* tab_strip_model = browser()->tab_strip_model();

// Open 3 tabs
chrome::NewTab(browser());
chrome::NewTab(browser());
chrome::NewTab(browser());
EXPECT_EQ(browser()->tab_strip_model()->count(), 4);
EXPECT_EQ(browser()->tab_strip_model()->active_index(), 3);

// MRU cycling, 3 -> 2
chrome::ExecuteCommand(browser(), IDC_SELECT_NEXT_TAB);
EXPECT_EQ(tab_strip_model->active_index(), 2);

// MRU cycling, 2 -> 1
chrome::ExecuteCommand(browser(), IDC_SELECT_NEXT_TAB);
EXPECT_EQ(tab_strip_model->active_index(), 1);

// Close current tab (index 1).
chrome::ExecuteCommand(browser(), IDC_CLOSE_TAB);
EXPECT_EQ(tab_strip_model->active_index(), 1);

// New MRU cycling is started
chrome::ExecuteCommand(browser(), IDC_SELECT_NEXT_TAB);
EXPECT_EQ(tab_strip_model->active_index(), 2);
}
Loading

0 comments on commit c15b8ce

Please sign in to comment.