chromium/ash/game_dashboard/game_dashboard_utils.cc

// Copyright 2023 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/game_dashboard/game_dashboard_utils.h"

#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/app_types_util.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/icon_button.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/wm/overview/overview_controller.h"
#include "chromeos/ui/frame/frame_header.h"
#include "components/prefs/pref_service.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/display/screen.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/view_utils.h"

namespace ash::game_dashboard_utils {

bool IsFlagSet(ArcGameControlsFlag flags, ArcGameControlsFlag flag) {
  return (flags & flag) == flag;
}

bool IsFlagChanged(ArcGameControlsFlag new_flags,
                   ArcGameControlsFlag old_flags,
                   ArcGameControlsFlag flag) {
  return ((new_flags ^ old_flags) & flag) == flag;
}

ArcGameControlsFlag UpdateFlag(ArcGameControlsFlag flags,
                               ArcGameControlsFlag flag,
                               bool enable_flag) {
  return static_cast<ArcGameControlsFlag>(enable_flag ? flags | flag
                                                      : flags & ~flag);
}

bool ShouldEnableFeatures() {
  return !OverviewController::Get()->InOverviewSession() &&
         !display::Screen::GetScreen()->InTabletMode();
}

views::Widget* GetNextWidgetToFocus(
    const std::vector<views::Widget*> widget_list,
    const views::Widget* focused_widget,
    bool reverse) {
  if (auto it =
          std::find(widget_list.begin(), widget_list.end(), focused_widget);
      it != widget_list.end()) {
    const int focused_widget_index =
        std::distance(widget_list.begin(),
                      it);  // it - widget_list_.begin();
    const size_t widget_list_size = widget_list.size();
    const size_t next_focus_widget_index =
        (reverse ? (focused_widget_index + widget_list_size - 1u)
                 : (focused_widget_index + 1u)) %
        widget_list_size;
    return widget_list[next_focus_widget_index];
  }
  return nullptr;
}

std::optional<ArcGameControlsFlag> GetGameControlsFlag(
    const aura::Window* window) {
  if (!IsArcWindow(window)) {
    return std::nullopt;
  }

  ArcGameControlsFlag flags = window->GetProperty(kArcGameControlsFlagsKey);
  CHECK(IsFlagSet(flags, ArcGameControlsFlag::kKnown));
  return std::make_optional<ArcGameControlsFlag>(flags);
}

void UpdateGameControlsHintButton(views::Button* button,
                                  ArcGameControlsFlag flags) {
  CHECK(button);

  // Update button enabled state, toggle state, and sub-label if it is needed.
  DCHECK(IsFlagSet(flags, ArcGameControlsFlag::kKnown));
  const bool is_available = IsFlagSet(flags, ArcGameControlsFlag::kAvailable);
  const bool is_enabled = IsFlagSet(flags, ArcGameControlsFlag::kEnabled);
  const bool is_empty = IsFlagSet(flags, ArcGameControlsFlag::kEmpty);
  const bool is_hint_on = IsFlagSet(flags, ArcGameControlsFlag::kHint);

  const bool should_enable = is_available && is_enabled && !is_empty;
  const bool should_toggle = should_enable && is_hint_on;
  if (auto* feature_tile = views::AsViewClass<FeatureTile>(button)) {
    feature_tile->SetEnabled(should_enable);
    feature_tile->SetToggled(should_toggle);
    feature_tile->SetSubLabel(l10n_util::GetStringUTF16(
        should_enable ? (should_toggle ? IDS_ASH_GAME_DASHBOARD_GC_TILE_VISIBLE
                                       : IDS_ASH_GAME_DASHBOARD_GC_TILE_HIDDEN)
                      : IDS_ASH_GAME_DASHBOARD_GC_TILE_OFF));
  } else {
    auto* icon_button = views::AsViewClass<IconButton>(button);
    CHECK(icon_button);
    icon_button->SetEnabled(should_enable);
    icon_button->SetToggled(should_toggle);
  }

  // Update tooltip text.
  int tooltip_text_id;
  if (!is_available) {
    // TODO(b/274690042): Replace it with a different tooltip text once it's
    // ready.
    tooltip_text_id = IDS_ASH_GAME_DASHBOARD_GC_TILE_TOOLTIPS_NOT_AVAILABLE;
  } else if (is_enabled) {
    if (is_empty) {
      tooltip_text_id = IDS_ASH_GAME_DASHBOARD_GC_TILE_TOOLTIPS_NOT_SETUP;
    } else if (is_hint_on) {
      tooltip_text_id = IDS_ASH_GAME_DASHBOARD_GC_TILE_TOOLTIPS_HIDE_CONTROLS;
    } else {
      tooltip_text_id = IDS_ASH_GAME_DASHBOARD_GC_TILE_TOOLTIPS_SHOW_CONTROLS;
    }
  } else {
    tooltip_text_id = IDS_ASH_GAME_DASHBOARD_GC_TILE_TOOLTIPS_NOT_AVAILABLE;
  }
  button->SetTooltipText(l10n_util::GetStringUTF16(tooltip_text_id));
}

bool ShouldEnableGameDashboardButton(aura::Window* window) {
  if (!IsArcWindow(window)) {
    return true;
  }

  const auto flags = window->GetProperty(kArcGameControlsFlagsKey);
  return IsFlagSet(flags, ArcGameControlsFlag::kKnown) &&
         !IsFlagSet(flags, ArcGameControlsFlag::kEdit);
}

bool ShouldShowWelcomeDialog() {
  PrefService* prefs =
      Shell::Get()->session_controller()->GetActivePrefService();
  DCHECK(prefs) << "A valid PrefService is needed to determine whether to show "
                   "the welcome dialog.";
  return prefs->GetBoolean(prefs::kGameDashboardShowWelcomeDialog);
}

void SetShowWelcomeDialog(bool show_dialog) {
  PrefService* prefs =
      Shell::Get()->session_controller()->GetActivePrefService();
  DCHECK(prefs) << "A valid PrefService is needed to update the show welcome "
                   "dialog param.";
  prefs->SetBoolean(prefs::kGameDashboardShowWelcomeDialog, show_dialog);
}

bool ShouldShowToolbar() {
  PrefService* prefs =
      Shell::Get()->session_controller()->GetActivePrefService();
  DCHECK(prefs) << "A valid PrefService is needed to determine whether to show "
                   "the toolbar.";
  return prefs->GetBoolean(prefs::kGameDashboardShowToolbar);
}

void SetShowToolbar(bool show_toolbar) {
  PrefService* prefs =
      Shell::Get()->session_controller()->GetActivePrefService();
  DCHECK(prefs) << "A valid PrefService is needed to update the show toolbar "
                   "param.";
  prefs->SetBoolean(prefs::kGameDashboardShowToolbar, show_toolbar);
}

int GetFrameHeaderHeight(aura::Window* window) {
  CHECK(window);
  auto* frame_header = chromeos::FrameHeader::Get(
      views::Widget::GetWidgetForNativeWindow(window));
  return (frame_header && frame_header->view()->GetVisible())
             ? frame_header->GetHeaderHeight()
             : 0;
}

void UpdateAccessibilityTree(const std::vector<views::Widget*>& widget_list) {
  const size_t widget_list_size = widget_list.size();
  if (widget_list_size <= 1u) {
    return;
  }

  for (size_t i = 0; i < widget_list_size; i++) {
    auto* contents_view = widget_list[i]->GetContentsView();
    auto& view_a11y = contents_view->GetViewAccessibility();
    const size_t prev_index = (i + widget_list_size - 1u) % widget_list_size;
    const size_t next_index = (i + 1u) % widget_list_size;

    view_a11y.SetPreviousFocus(widget_list[prev_index]);
    view_a11y.SetNextFocus(widget_list[next_index]);
    contents_view->NotifyAccessibilityEvent(ax::mojom::Event::kTreeChanged,
                                            /*send_native_event=*/true);
  }
}

}  // namespace ash::game_dashboard_utils