11320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Copyright 2014 The Chromium Authors. All rights reserved. 21320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Use of this source code is governed by a BSD-style license that can be 31320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// found in the LICENSE file. 41320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h" 61320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ash/display/display_controller.h" 81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ash/shell.h" 91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/logging.h" 101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/browser/chromeos/ui/focus_ring_layer.h" 111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ui/gfx/screen.h" 121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace chromeos { 141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace { 161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// The number of pixels the focus ring is outset from the object it outlines, 181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// which also determines the border radius of the rounded corners. 191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// TODO(dmazzoni): take display resolution into account. 201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst int kAccessibilityFocusRingMargin = 7; 211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Time to transition between one location and the next. 231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst int kTransitionTimeMilliseconds = 300; 241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// A Region is an unordered collection of Rects that maintains its 261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// bounding box. Used in the middle of an algorithm that groups 271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// adjacent and overlapping rects. 281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistruct Region { 291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci explicit Region(gfx::Rect initial_rect) { 301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bounds = initial_rect; 311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rects.push_back(initial_rect); 321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect bounds; 341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::vector<gfx::Rect> rects; 351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} // namespace 381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// static 401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAccessibilityFocusRingController* 411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci AccessibilityFocusRingController::GetInstance() { 421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return Singleton<AccessibilityFocusRingController>::get(); 431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAccessibilityFocusRingController::AccessibilityFocusRingController() 461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci : compositor_(NULL) { 471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAccessibilityFocusRingController::~AccessibilityFocusRingController() { 501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid AccessibilityFocusRingController::SetFocusRing( 531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const std::vector<gfx::Rect>& rects) { 541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rects_ = rects; 551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Update(); 561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid AccessibilityFocusRingController::Update() { 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci previous_rings_.swap(rings_); 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rings_.clear(); 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci RectsToRings(rects_, &rings_); 621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci layers_.resize(rings_.size()); 631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (size_t i = 0; i < rings_.size(); ++i) { 641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!layers_[i]) 651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci layers_[i].reset(new AccessibilityFocusRingLayer(this)); 661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (i > 0) { 681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Focus rings other than the first one don't animate. 691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci layers_[i]->Set(rings_[i]); 701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci continue; 711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect bounds = rings_[0].GetBounds(); 741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Display display = 751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Screen::GetNativeScreen()->GetDisplayMatching(bounds); 761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci aura::Window* root_window = ash::Shell::GetInstance()->display_controller() 771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ->GetRootWindowForDisplayId(display.id()); 781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ui::Compositor* compositor = root_window->layer()->GetCompositor(); 791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!compositor || root_window != layers_[0]->root_window()) { 801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci layers_[0]->Set(rings_[0]); 811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (compositor_ && compositor_->HasAnimationObserver(this)) { 821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compositor_->RemoveAnimationObserver(this); 831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compositor_ = NULL; 841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci continue; 861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci focus_change_time_ = base::TimeTicks::Now(); 891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!compositor->HasAnimationObserver(this)) { 901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compositor_ = compositor; 911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compositor_->AddAnimationObserver(this); 921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid AccessibilityFocusRingController::RectsToRings( 971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const std::vector<gfx::Rect>& src_rects, 981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::vector<AccessibilityFocusRing>* rings) const { 991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (src_rects.empty()) 1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return; 1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Give all of the rects a margin. 1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::vector<gfx::Rect> rects; 1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rects.resize(src_rects.size()); 1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (size_t i = 0; i < src_rects.size(); ++i) { 1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rects[i] = src_rects[i]; 1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rects[i].Inset(-GetMargin(), -GetMargin()); 1081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Split the rects into contiguous regions. 1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::vector<Region> regions; 1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions.push_back(Region(rects[0])); 1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (size_t i = 1; i < rects.size(); ++i) { 1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bool found = false; 1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (size_t j = 0; j < regions.size(); ++j) { 1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (Intersects(rects[i], regions[j].bounds)) { 1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions[j].rects.push_back(rects[i]); 1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions[j].bounds.Union(rects[i]); 1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci found = true; 1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!found) { 1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions.push_back(Region(rects[i])); 1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Keep merging regions that intersect. 1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // TODO(dmazzoni): reduce the worst-case complexity! This appears like 1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // it could be O(n^3), make sure it's not in practice. 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bool merged; 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci do { 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci merged = false; 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (size_t i = 0; i < regions.size() - 1 && !merged; ++i) { 1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (size_t j = i + 1; j < regions.size() && !merged; ++j) { 1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (Intersects(regions[i].bounds, regions[j].bounds)) { 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions[i].rects.insert(regions[i].rects.end(), 1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions[j].rects.begin(), 1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions[j].rects.end()); 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions[i].bounds.Union(regions[j].bounds); 1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci regions.erase(regions.begin() + j); 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci merged = true; 1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } while (merged); 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (size_t i = 0; i < regions.size(); ++i) { 1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci std::sort(regions[i].rects.begin(), regions[i].rects.end()); 1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rings->push_back(RingFromSortedRects(regions[i].rects)); 1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint AccessibilityFocusRingController::GetMargin() const { 1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return kAccessibilityFocusRingMargin; 1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Given a vector of rects that all overlap, already sorted from top to bottom 1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// and left to right, split them into three shapes covering the top, middle, 1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// and bottom of a "paragraph shape". 1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// 1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Input: 1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// 1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +---+---+ 1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | 1 | 2 | 1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +---------------------+---+---+ 1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | 3 | 1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +--------+---------------+----+ 1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | 4 | 5 | 1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +--------+---------------+--+ 1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | 6 | 1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +---------+-----------------+ 1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | 7 | 1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +---------+ 1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// 1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Output: 1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// 1771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +-------+ 1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | Top | 1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +---------------------+-------+ 1801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | | 1811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | | 1821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | Middle | 1831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | | 1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | | 1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +---------+-------------------+ 1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// | Bottom | 1871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// +---------+ 1881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// 1891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// When there's no clear "top" or "bottom" segment, split the overall rect 1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// evenly so that some of the area still fits into the "top" and "bottom" 1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// segments. 1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid AccessibilityFocusRingController::SplitIntoParagraphShape( 1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const std::vector<gfx::Rect>& rects, 1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect* top, 1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect* middle, 1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect* bottom) const { 1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci size_t n = rects.size(); 1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Figure out how many rects belong in the top portion. 2001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect top_rect = rects[0]; 2011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int top_middle = (top_rect.y() + top_rect.bottom()) / 2; 2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci size_t top_count = 1; 2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci while (top_count < n && rects[top_count].y() < top_middle) { 2041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top_rect.Union(rects[top_count]); 2051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top_middle = (top_rect.y() + top_rect.bottom()) / 2; 2061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top_count++; 2071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Figure out how many rects belong in the bottom portion. 2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect bottom_rect = rects[n - 1]; 2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int bottom_middle = (bottom_rect.y() + bottom_rect.bottom()) / 2; 2121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci size_t bottom_count = std::min(static_cast<size_t>(1), n - top_count); 2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci while (bottom_count + top_count < n && 2141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rects[n - bottom_count - 1].bottom() > bottom_middle) { 2151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bottom_rect.Union(rects[n - bottom_count - 1]); 2161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bottom_middle = (bottom_rect.y() + bottom_rect.bottom()) / 2; 2171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bottom_count++; 2181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Whatever's left goes to the middle rect, but if there's no middle or 2211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // bottom rect, split the existing rects evenly to make one. 2221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect middle_rect; 2231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (top_count + bottom_count < n) { 2241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci middle_rect = rects[top_count]; 2251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (size_t i = top_count + 1; i < n - bottom_count; i++) 2261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci middle_rect.Union(rects[i]); 2271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else if (bottom_count > 0) { 2281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect enclosing_rect = top_rect; 2291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci enclosing_rect.Union(bottom_rect); 2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int middle_top = (top_rect.y() + top_rect.bottom() * 2) / 3; 2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int middle_bottom = (bottom_rect.y() * 2 + bottom_rect.bottom()) / 3; 2321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top_rect.set_height(middle_top - top_rect.y()); 2331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bottom_rect.set_height(bottom_rect.bottom() - middle_bottom); 2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bottom_rect.set_y(middle_bottom); 2351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci middle_rect = gfx::Rect(enclosing_rect.x(), middle_top, 2361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci enclosing_rect.width(), middle_bottom - middle_top); 2371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } else { 2381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int middle_top = (top_rect.y() * 2 + top_rect.bottom()) / 3; 2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int middle_bottom = (top_rect.y() + top_rect.bottom() * 2) / 3; 2401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci middle_rect = gfx::Rect(top_rect.x(), middle_top, 2411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top_rect.width(), middle_bottom - middle_top); 2421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bottom_rect = gfx::Rect( 2431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top_rect.x(), middle_bottom, 2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top_rect.width(), top_rect.bottom() - middle_bottom); 2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top_rect.set_height(middle_top - top_rect.y()); 2461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (middle_rect.y() > top_rect.bottom()) { 2491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci middle_rect.set_height( 2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci middle_rect.height() + middle_rect.y() - top_rect.bottom()); 2511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci middle_rect.set_y(top_rect.bottom()); 2521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (middle_rect.bottom() < bottom_rect.y()) { 2551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci middle_rect.set_height(bottom_rect.y() - middle_rect.y()); 2561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 2571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *top = top_rect; 2591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *middle = middle_rect; 2601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci *bottom = bottom_rect; 2611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAccessibilityFocusRing AccessibilityFocusRingController::RingFromSortedRects( 2641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const std::vector<gfx::Rect>& rects) const { 2651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (rects.size() == 1) 2661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return AccessibilityFocusRing::CreateWithRect(rects[0], GetMargin()); 2671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect top; 2691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect middle; 2701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci gfx::Rect bottom; 2711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci SplitIntoParagraphShape(rects, &top, &middle, &bottom); 2721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return AccessibilityFocusRing::CreateWithParagraphShape( 2741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci top, middle, bottom, GetMargin()); 2751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool AccessibilityFocusRingController::Intersects( 2781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const gfx::Rect& r1, const gfx::Rect& r2) const { 2791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int slop = GetMargin(); 2801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return (r2.x() <= r1.right() + slop && 2811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci r2.right() >= r1.x() - slop && 2821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci r2.y() <= r1.bottom() + slop && 2831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci r2.bottom() >= r1.y() - slop); 2841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid AccessibilityFocusRingController::OnDeviceScaleFactorChanged() { 2871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci Update(); 2881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 2891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid AccessibilityFocusRingController::OnAnimationStep( 2911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base::TimeTicks timestamp) { 2921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (rings_.empty()) 2931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return; 2941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci CHECK(compositor_); 2961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci CHECK(!rings_.empty()); 2971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci CHECK(!layers_.empty()); 2981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci CHECK(layers_[0]); 2991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3005b892326406927b709cdaf6c384d4ababf456332Ben Murdoch // It's quite possible for the first 1 or 2 animation frames to be 3015b892326406927b709cdaf6c384d4ababf456332Ben Murdoch // for a timestamp that's earlier than the time we received the 3025b892326406927b709cdaf6c384d4ababf456332Ben Murdoch // focus change, so we just treat those as a delta of zero. 3035b892326406927b709cdaf6c384d4ababf456332Ben Murdoch if (timestamp < focus_change_time_) 3045b892326406927b709cdaf6c384d4ababf456332Ben Murdoch timestamp = focus_change_time_; 3055b892326406927b709cdaf6c384d4ababf456332Ben Murdoch 3061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base::TimeDelta delta = timestamp - focus_change_time_; 3071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base::TimeDelta transition_time = 3081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci base::TimeDelta::FromMilliseconds(kTransitionTimeMilliseconds); 3091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (delta >= transition_time) { 3101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci layers_[0]->Set(rings_[0]); 3111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compositor_->RemoveAnimationObserver(this); 3121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci compositor_ = NULL; 3131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return; 3141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 3151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci double fraction = delta.InSecondsF() / transition_time.InSecondsF(); 3171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Ease-in effect. 3191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci fraction = pow(fraction, 0.3); 3201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci layers_[0]->Set(AccessibilityFocusRing::Interpolate( 3221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci previous_rings_[0], rings_[0], fraction)); 3231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 3241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} // namespace chromeos 326