// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/shelf/shelf_context_menu_model.h"
#include <memory>
#include <string>
#include <utility>
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/app_list_metrics.h"
#include "ash/app_list/app_list_model_provider.h"
#include "ash/app_list/model/app_list_model.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/personalization_entry_point.h"
#include "ash/public/cpp/app_menu_constants.h"
#include "ash/public/cpp/new_window_delegate.h"
#include "ash/public/cpp/shelf_item_delegate.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/shelf_prefs.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf_metrics.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/numerics/safe_conversions.h"
#include "components/prefs/pref_service.h"
#include "ui/base/models/image_model.h"
namespace ash {
namespace {
// Returns true if the user can modify the shelf's auto-hide behavior pref.
bool CanUserModifyShelfAutoHide(PrefService* prefs) {
return prefs && prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)
->IsUserModifiable();
}
// Returns true if the display is showing a fullscreen window.
// NOTE: This duplicates the functionality of Chrome's IsFullScreenMode.
bool IsFullScreenMode(int64_t display_id) {
auto* controller = Shell::GetRootWindowControllerWithDisplayId(display_id);
return controller && controller->GetWindowForFullscreenMode();
}
} // namespace
ShelfContextMenuModel::ShelfContextMenuModel(ShelfItemDelegate* delegate,
int64_t display_id,
bool menu_in_shelf)
: ui::SimpleMenuModel(this),
delegate_(delegate),
display_id_(display_id),
menu_in_shelf_(menu_in_shelf) {
// Add shelf and wallpaper items if ShelfView or HomeButton are selected.
if (!delegate)
AddShelfAndWallpaperItems();
}
ShelfContextMenuModel::~ShelfContextMenuModel() = default;
bool ShelfContextMenuModel::IsCommandIdChecked(int command_id) const {
if (command_id == MENU_ALIGNMENT_LEFT ||
command_id == MENU_ALIGNMENT_BOTTOM ||
command_id == MENU_ALIGNMENT_RIGHT) {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
const ShelfAlignment alignment = GetShelfAlignmentPref(prefs, display_id_);
if (command_id == MENU_ALIGNMENT_LEFT)
return alignment == ShelfAlignment::kLeft;
if (command_id == MENU_ALIGNMENT_BOTTOM) {
return alignment == ShelfAlignment::kBottom ||
alignment == ShelfAlignment::kBottomLocked;
}
if (command_id == MENU_ALIGNMENT_RIGHT)
return alignment == ShelfAlignment::kRight;
}
return SimpleMenuModel::Delegate::IsCommandIdChecked(command_id);
}
void ShelfContextMenuModel::ExecuteCommand(int command_id, int event_flags) {
DCHECK(IsCommandIdEnabled(command_id));
Shell* shell = Shell::Get();
PrefService* prefs =
shell->session_controller()->GetLastActiveUserPrefService();
if (!prefs) // Null during startup.
return;
// Clamshell mode only options should not activate in tablet mode.
const bool is_tablet_mode = display::Screen::GetScreen()->InTabletMode();
switch (command_id) {
case MENU_AUTO_HIDE:
SetShelfAutoHideBehaviorPref(
prefs, display_id_,
GetShelfAutoHideBehaviorPref(prefs, display_id_) ==
ShelfAutoHideBehavior::kAlways
? ShelfAutoHideBehavior::kNever
: ShelfAutoHideBehavior::kAlways);
break;
case MENU_ALIGNMENT_LEFT:
DCHECK(!is_tablet_mode);
base::RecordAction(base::UserMetricsAction("Shelf_AlignmentSetLeft"));
SetShelfAlignmentPref(prefs, display_id_, ShelfAlignment::kLeft);
break;
case MENU_ALIGNMENT_RIGHT:
DCHECK(!is_tablet_mode);
base::RecordAction(base::UserMetricsAction("Shelf_AlignmentSetRight"));
SetShelfAlignmentPref(prefs, display_id_, ShelfAlignment::kRight);
break;
case MENU_ALIGNMENT_BOTTOM:
DCHECK(!is_tablet_mode);
base::RecordAction(base::UserMetricsAction("Shelf_AlignmentSetBottom"));
SetShelfAlignmentPref(prefs, display_id_, ShelfAlignment::kBottom);
break;
case MENU_PERSONALIZATION_HUB:
// Record entry point metric to Personalization Hub through Home Screen.
base::UmaHistogramEnumeration(kPersonalizationEntryPointHistogramName,
PersonalizationEntryPoint::kHomeScreen);
NewWindowDelegate::GetPrimary()->OpenPersonalizationHub();
break;
case MENU_HIDE_CONTINUE_SECTION:
DCHECK(is_tablet_mode);
shell->app_list_controller()->SetHideContinueSection(true);
break;
case MENU_SHOW_CONTINUE_SECTION:
DCHECK(is_tablet_mode);
shell->app_list_controller()->SetHideContinueSection(false);
break;
case MENU_HIDE_DESK_NAME:
base::UmaHistogramBoolean(kDeskButtonHiddenHistogramName, true);
SetShowDeskButtonInShelfPref(prefs, false);
break;
case MENU_SHOW_DESK_NAME:
SetShowDeskButtonInShelfPref(prefs, true);
break;
// Using reorder CommandId in ash/public/cpp/app_menu_constants.h
case REORDER_BY_NAME_ALPHABETICAL:
AppListModelProvider::Get()->model()->delegate()->RequestAppListSort(
AppListSortOrder::kNameAlphabetical);
break;
case REORDER_BY_COLOR:
AppListModelProvider::Get()->model()->delegate()->RequestAppListSort(
AppListSortOrder::kColor);
break;
default:
if (delegate_) {
if (IsCommandIdAnAppLaunch(command_id)) {
shell->app_list_controller()->RecordShelfAppLaunched();
}
delegate_->ExecuteCommand(true, command_id, event_flags, display_id_);
}
break;
}
}
void ShelfContextMenuModel::AddShelfAndWallpaperItems() {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
if (!prefs) // Null during startup.
return;
// In fullscreen, the shelf is either hidden or auto-hidden, depending on the
// type of fullscreen. Do not show the auto-hide menu item while in fullscreen
// because it is confusing when the preference appears not to apply.
const bool is_fullscreen = IsFullScreenMode(display_id_);
if (CanUserModifyShelfAutoHide(prefs) && !is_fullscreen) {
const bool is_autohide_set =
GetShelfAutoHideBehaviorPref(prefs, display_id_) ==
ShelfAutoHideBehavior::kAlways;
auto string_id = is_autohide_set
? IDS_ASH_SHELF_CONTEXT_MENU_ALWAYS_SHOW_SHELF
: IDS_ASH_SHELF_CONTEXT_MENU_AUTO_HIDE;
AddItemWithStringIdAndIcon(
MENU_AUTO_HIDE, string_id,
ui::ImageModel::FromVectorIcon(
is_autohide_set ? kAlwaysShowShelfIcon : kAutoHideIcon,
ui::kColorAshSystemUIMenuIcon));
}
// Only allow shelf alignment modifications by the logged in Gaia users
// (regular or Family Link user). In tablet mode, the shelf alignment option
// is not shown.
LoginStatus status = Shell::Get()->session_controller()->login_status();
const bool in_tablet_mode = display::Screen::GetScreen()->InTabletMode();
if ((status == LoginStatus::USER || status == LoginStatus::CHILD) &&
!in_tablet_mode &&
prefs->FindPreference(prefs::kShelfAlignmentLocal)->IsUserModifiable()) {
alignment_submenu_ = std::make_unique<ui::SimpleMenuModel>(this);
constexpr int group = 0;
alignment_submenu_->AddRadioItemWithStringId(
MENU_ALIGNMENT_LEFT, IDS_ASH_SHELF_CONTEXT_MENU_ALIGN_LEFT, group);
alignment_submenu_->AddRadioItemWithStringId(
MENU_ALIGNMENT_BOTTOM, IDS_ASH_SHELF_CONTEXT_MENU_ALIGN_BOTTOM, group);
alignment_submenu_->AddRadioItemWithStringId(
MENU_ALIGNMENT_RIGHT, IDS_ASH_SHELF_CONTEXT_MENU_ALIGN_RIGHT, group);
AddSubMenuWithStringIdAndIcon(
MENU_ALIGNMENT_MENU, IDS_ASH_SHELF_CONTEXT_MENU_POSITION,
alignment_submenu_.get(),
ui::ImageModel::FromVectorIcon(kShelfPositionIcon,
ui::kColorAshSystemUIMenuIcon));
}
AddItemWithStringIdAndIcon(
MENU_PERSONALIZATION_HUB, IDS_AURA_OPEN_PERSONALIZATION_HUB,
ui::ImageModel::FromVectorIcon(kPaintBrushIcon,
ui::kColorAshSystemUIMenuIcon));
// Only add the desk button items if the context menu was spawned on the
// shelf, tablet mode is not enabled, and full screen is not enabled.
if (features::IsDeskButtonEnabled() && !in_tablet_mode && menu_in_shelf_ &&
!is_fullscreen) {
// If the button is visible for any reason, show the option to hide it
// manually. If it isn't visible show the option to show it.
if (GetDeskButtonVisibility(prefs)) {
AddItemWithStringIdAndIcon(
MENU_HIDE_DESK_NAME, IDS_ASH_SHELF_CONTEXT_MENU_HIDE_DESK_NAME,
ui::ImageModel::FromVectorIcon(kDeskButtonVisibilityOffIcon,
ui::kColorAshSystemUIMenuIcon));
} else {
AddItemWithStringIdAndIcon(
MENU_SHOW_DESK_NAME, IDS_ASH_SHELF_CONTEXT_MENU_SHOW_DESK_NAME,
ui::ImageModel::FromVectorIcon(kDeskButtonVisibilityOnIcon,
ui::kColorAshSystemUIMenuIcon));
}
}
}
} // namespace ash