chromium/ash/system/tray/tray_utils.cc

// Copyright 2012 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/system/tray/tray_utils.h"

#include <string>

#include "ash/bubble/bubble_constants.h"
#include "ash/constants/ash_constants.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/style/typography.h"
#include "ash/system/tray/hover_highlight_view.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/wm/work_area_insets.h"
#include "base/check.h"
#include "base/strings/string_number_conversions.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/display/screen.h"
#include "ui/gfx/font_list.h"
#include "ui/views/controls/label.h"

namespace ash {

void SetupLabelForTray(views::Label* label) {
  // The text is drawn on an transparent bg, so we must disable subpixel
  // rendering.
  label->SetSubpixelRenderingEnabled(false);
  label->SetAutoColorReadabilityEnabled(false);
  label->SetFontList(gfx::FontList().Derive(
      kTrayTextFontSizeIncrease, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
}

void SetupConnectedScrollListItem(HoverHighlightView* view) {
  SetupConnectedScrollListItem(view, std::nullopt /* battery_percentage */);
}

void SetupConnectedScrollListItem(HoverHighlightView* view,
                                  std::optional<uint8_t> battery_percentage) {
  DCHECK(view->is_populated());

  std::u16string status;

  if (battery_percentage) {
    view->SetSubText(l10n_util::GetStringFUTF16(
        IDS_ASH_STATUS_TRAY_BLUETOOTH_DEVICE_CONNECTED_WITH_BATTERY_LABEL,
        base::NumberToString16(battery_percentage.value())));
  } else {
    view->SetSubText(l10n_util::GetStringUTF16(
        IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED));
  }

  view->sub_text_label()->SetAutoColorReadabilityEnabled(false);

  view->sub_text_label()->SetEnabledColorId(cros_tokens::kCrosSysPositive);
  ash::TypographyProvider::Get()->StyleLabel(
      ash::TypographyToken::kCrosAnnotation1, *view->sub_text_label());
}

void SetupConnectingScrollListItem(HoverHighlightView* view) {
  DCHECK(view->is_populated());

  view->SetSubText(
      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING));
}

void SetWarningSubText(HoverHighlightView* view, std::u16string subtext) {
  DCHECK(view->is_populated());

  view->SetSubText(subtext);
  view->sub_text_label()->SetAutoColorReadabilityEnabled(false);
  view->sub_text_label()->SetEnabledColorId(cros_tokens::kCrosSysWarning);
  ash::TypographyProvider::Get()->StyleLabel(
      ash::TypographyToken::kCrosAnnotation1, *view->sub_text_label());
}

gfx::Insets GetTrayBubbleInsets(aura::Window* window) {
  // Decrease bottom and side insets by `kShelfDisplayOffset` to compensate for
  // the adjustment of the respective edges in Shelf::GetSystemTrayAnchorRect().
  gfx::Insets insets = gfx::Insets::TLBR(
      kBubbleMenuPadding, kBubbleMenuPadding,
      kBubbleMenuPadding - kShelfDisplayOffset,
      kBubbleMenuPadding - (base::i18n::IsRTL() ? 0 : kShelfDisplayOffset));

  // The work area in tablet mode always uses the in-app shelf height, which is
  // shorter than the standard shelf height. In this state, we need to add back
  // the difference to compensate (see crbug.com/1033302).
  if (!display::Screen::GetScreen()->InTabletMode()) {
    return insets;
  }

  Shelf* shelf = Shelf::ForWindow(window);
  bool is_bottom_alignment =
      shelf->alignment() == ShelfAlignment::kBottom ||
      shelf->alignment() == ShelfAlignment::kBottomLocked;

  if (!is_bottom_alignment)
    return insets;

  int height_compensation = GetBubbleInsetHotseatCompensation(window);
  insets.set_bottom(insets.bottom() + height_compensation);
  return insets;
}

int GetBubbleInsetHotseatCompensation(aura::Window* window) {
  int height_compensation = kTrayBubbleInsetHotseatCompensation;
  Shelf* shelf = Shelf::ForWindow(window);

  switch (shelf->GetBackgroundType()) {
    case ShelfBackgroundType::kInApp:
    case ShelfBackgroundType::kOverview:
      // Certain modes do not require a height compensation.
      height_compensation = 0;
      break;
    case ShelfBackgroundType::kLogin:
      // The hotseat is not visible on the lock screen, so we need a smaller
      // height compensation.
      height_compensation = kTrayBubbleInsetTabletModeCompensation;
      break;
    default:
      break;
  }
  return height_compensation;
}

gfx::Insets GetInkDropInsets(TrayPopupInkDropStyle ink_drop_style) {
  if (ink_drop_style == TrayPopupInkDropStyle::HOST_CENTERED ||
      ink_drop_style == TrayPopupInkDropStyle::INSET_BOUNDS) {
    return gfx::Insets(kTrayPopupInkDropInset);
  }
  return gfx::Insets();
}

int CalculateMaxTrayBubbleHeight(aura::Window* window) {
  Shelf* shelf = Shelf::ForWindow(window);

  // We calculate the available height from the top of the screen to the top of
  // the bubble's anchor rect. We can not use the bottom of the screen since the
  // anchor's position is not always exactly at the bottom of the screen. If
  // we're in tablet mode then we also need to subtract out any extra padding
  // that may be present due to the hotseat.
  int anchor_rect_top = shelf->GetSystemTrayAnchorRect().y();
  WorkAreaInsets* work_area =
      WorkAreaInsets::ForWindow(shelf->GetWindow()->GetRootWindow());
  int free_space_height_above_anchor =
      anchor_rect_top - work_area->user_work_area_bounds().y();
  if (display::Screen::GetScreen()->InTabletMode()) {
    free_space_height_above_anchor -= GetBubbleInsetHotseatCompensation(window);
  }
  return free_space_height_above_anchor - kBubbleMenuPadding * 2;
}

TrayBubbleView::InitParams CreateInitParamsForTrayBubble(
    TrayBackgroundView* tray,
    bool anchor_to_shelf_corner) {
  TrayBubbleView::InitParams init_params;
  init_params.delegate = tray->GetWeakPtr();
  init_params.parent_window = tray->GetBubbleWindowContainer();
  if (anchor_to_shelf_corner) {
    init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
    init_params.anchor_rect = tray->shelf()->GetSystemTrayAnchorRect();
  } else {
    init_params.anchor_view = tray;
  }
  init_params.insets = GetTrayBubbleInsets(tray->GetBubbleWindowContainer());
  init_params.shelf_alignment = tray->shelf()->alignment();
  init_params.preferred_width = kTrayMenuWidth;
  init_params.close_on_deactivate = true;
  init_params.translucent = true;
  init_params.corner_radius = kTrayItemCornerRadius;
  init_params.reroute_event_handler = true;
  init_params.anchor_to_shelf_corner = anchor_to_shelf_corner;

  return init_params;
}

}  // namespace ash