chromium/ash/app_list/app_list_util.cc

// 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/app_list/app_list_util.h"

#include "ash/app_list/model/app_list_folder_item.h"
#include "ash/app_list/model/app_list_item.h"
#include "ash/constants/ash_constants.h"
#include "ash/constants/ash_features.h"
#include "ash/style/ash_color_id.h"
#include "ash/style/ash_color_provider.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/view.h"

namespace ash {

namespace {

// The cardified apps grid and app icons should scale down by this factor.
constexpr float kAppsGridCardifiedScale = 0.9f;

}  // namespace

bool IsUnhandledUnmodifiedEvent(const ui::KeyEvent& event) {
  if (event.handled() || event.type() != ui::EventType::kKeyPressed) {
    return false;
  }

  if (event.IsShiftDown() || event.IsControlDown() || event.IsAltDown())
    return false;

  return true;
}

bool IsUnhandledLeftRightKeyEvent(const ui::KeyEvent& event) {
  if (!IsUnhandledUnmodifiedEvent(event))
    return false;

  return event.key_code() == ui::VKEY_LEFT ||
         event.key_code() == ui::VKEY_RIGHT;
}

bool IsUnhandledUpDownKeyEvent(const ui::KeyEvent& event) {
  if (!IsUnhandledUnmodifiedEvent(event))
    return false;

  return event.key_code() == ui::VKEY_UP || event.key_code() == ui::VKEY_DOWN;
}

bool IsUnhandledArrowKeyEvent(const ui::KeyEvent& event) {
  if (!IsUnhandledUnmodifiedEvent(event))
    return false;

  return IsArrowKeyEvent(event);
}

bool IsArrowKeyEvent(const ui::KeyEvent& event) {
  return IsArrowKey(event.key_code());
}

bool IsArrowKey(const ui::KeyboardCode& key_code) {
  return key_code == ui::VKEY_DOWN || key_code == ui::VKEY_RIGHT ||
         key_code == ui::VKEY_LEFT || key_code == ui::VKEY_UP;
}

bool IsFolderItem(AppListItem* item) {
  return item && item->GetItemType() == AppListFolderItem::kItemType;
}

bool LeftRightKeyEventShouldExitText(views::Textfield* textfield,
                                     const ui::KeyEvent& key_event) {
  DCHECK(IsUnhandledLeftRightKeyEvent(key_event));

  if (textfield->GetText().empty())
    return true;

  if (textfield->HasSelection())
    return false;

  if (textfield->GetCursorPosition() != 0 &&
      textfield->GetCursorPosition() != textfield->GetText().length()) {
    return false;
  }

  // For RTL language, the beginning position of the cursor will be at the right
  // side and it grows towards left as we are typing.
  const bool text_rtl =
      textfield->GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
  const bool cursor_at_beginning = textfield->GetCursorPosition() == 0;
  const bool move_cursor_reverse =
      (text_rtl && key_event.key_code() == ui::VKEY_RIGHT) ||
      (!text_rtl && key_event.key_code() == ui::VKEY_LEFT);

  if ((cursor_at_beginning && !move_cursor_reverse) ||
      (!cursor_at_beginning && move_cursor_reverse)) {
    // Cursor is at either the beginning or the end of the textfield, and it
    // will move inward.
    return false;
  }

  return true;
}

bool ProcessLeftRightKeyTraversalForTextfield(views::Textfield* textfield,
                                              const ui::KeyEvent& key_event) {
  DCHECK(IsUnhandledLeftRightKeyEvent(key_event));

  if (!LeftRightKeyEventShouldExitText(textfield, key_event))
    return false;

  const bool move_focus_reverse = base::i18n::IsRTL()
                                      ? key_event.key_code() == ui::VKEY_RIGHT
                                      : key_event.key_code() == ui::VKEY_LEFT;

  // Move focus outside the textfield.
  textfield->GetFocusManager()->AdvanceFocus(move_focus_reverse);
  return true;
}

gfx::ImageSkia CreateIconWithCircleBackground(
    const gfx::ImageSkia& icon,
    const ui::ColorProvider* color_provider) {
  DCHECK_EQ(icon.width(), icon.height());
  return gfx::ImageSkiaOperations::CreateImageWithCircleBackground(
      icon.width() / 2, color_provider->GetColor(kColorAshShieldAndBaseOpaque),
      icon);
}

void PaintFocusBar(gfx::Canvas* canvas,
                   const gfx::Point& content_origin,
                   int height,
                   SkColor color) {
  SkPath path;
  gfx::Rect focus_bar_bounds(content_origin.x() - kFocusBarThickness,
                             content_origin.y(), kFocusBarThickness * 2,
                             height);
  path.addRRect(SkRRect::MakeRectXY(RectToSkRect(focus_bar_bounds),
                                    kFocusBarThickness, kFocusBarThickness));
  canvas->ClipPath(path, true);

  cc::PaintFlags flags;
  flags.setAntiAlias(true);
  flags.setColor(color);
  flags.setStyle(cc::PaintFlags::kStroke_Style);
  flags.setStrokeWidth(kFocusBarThickness);
  gfx::Point top_point = content_origin + gfx::Vector2d(kFocusBarThickness, 0);
  gfx::Point bottom_point =
      content_origin + gfx::Vector2d(kFocusBarThickness, height);
  canvas->DrawLine(top_point, bottom_point, flags);
}

void SetViewIgnoredForAccessibility(views::View* view, bool ignored) {
  auto& view_accessibility = view->GetViewAccessibility();
  view_accessibility.SetIsLeaf(ignored);
  view_accessibility.SetIsIgnored(ignored);
}

float GetAppsGridCardifiedScale() {
  return kAppsGridCardifiedScale;
}

}  // namespace ash