Chromium Code Reviews| Index: chrome/browser/chromeos/ui/accessibility_focus_ring_controller.cc |
| diff --git a/chrome/browser/chromeos/ui/accessibility_focus_ring_controller.cc b/chrome/browser/chromeos/ui/accessibility_focus_ring_controller.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f5814c0c06230051fbc33d1c9dee8feddb907dbc |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/ui/accessibility_focus_ring_controller.cc |
| @@ -0,0 +1,249 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h" |
| + |
| +#include "ash/system/tray/actionable_view.h" |
| +#include "ash/system/tray/tray_background_view.h" |
| +#include "ash/system/tray/tray_popup_header_button.h" |
| +#include "ash/wm/window_util.h" |
|
xiyuan
2014/09/11 03:33:10
nit: line 7-10 seems not used.
dmazzoni
2014/09/11 21:44:38
Done.
|
| +#include "chrome/browser/chromeos/ui/focus_ring_layer.h" |
| +#include "ui/aura/window.h" |
| +#include "ui/views/controls/button/label_button.h" |
| +#include "ui/views/view.h" |
| +#include "ui/views/widget/widget.h" |
|
xiyuan
2014/09/11 03:33:11
nit: line 12-15 seems not used
dmazzoni
2014/09/11 21:44:38
Done.
|
| + |
| +namespace chromeos { |
| + |
| +namespace { |
|
xiyuan
2014/09/11 03:33:10
nit: insert an empty line after
dmazzoni
2014/09/11 21:44:38
Done.
|
| +// The number of pixels the focus ring is outset from the object it outlines, |
| +// which also determines the border radius of the rounded corners. |
| +// TODO(dmazzoni): take display resolution into account. |
| +const int kAccessibilityFocusRingMargin = 16; |
| +} |
|
xiyuan
2014/09/11 03:33:10
nit: insert an emtpy line before and add "// name
dmazzoni
2014/09/11 21:44:38
Done.
|
| + |
| +// static |
| +AccessibilityFocusRingController* |
| + AccessibilityFocusRingController::GetInstance() { |
| + return Singleton<AccessibilityFocusRingController>::get(); |
| +} |
| + |
| +AccessibilityFocusRingController::AccessibilityFocusRingController() { |
| +} |
| + |
| +AccessibilityFocusRingController::~AccessibilityFocusRingController() { |
| +} |
| + |
| +void AccessibilityFocusRingController::SetFocusRing( |
| + const std::vector<gfx::Rect>& rects) { |
| + rects_ = rects; |
| + Update(); |
| +} |
| + |
| +void AccessibilityFocusRingController::Update() { |
| + rings_.clear(); |
| + RectsToRings(rects_, &rings_); |
| + |
| + if (!main_focus_ring_layer_) |
| + main_focus_ring_layer_.reset(new AccessibilityFocusRingLayer(this)); |
| + |
| + if (rings_.size()) |
|
xiyuan
2014/09/11 03:33:10
nit: rings_.size() -> !rings_.empty()
dmazzoni
2014/09/11 21:44:38
Done.
|
| + main_focus_ring_layer_->Set(rings_[0]); |
| +} |
| + |
| +struct Region { |
|
xiyuan
2014/09/11 03:33:10
nit: Move into anonymous namespace?
dmazzoni
2014/09/11 21:44:38
Done, and added a comment.
|
| + explicit Region(gfx::Rect initial_rect) { |
| + bounds = initial_rect; |
| + rects.push_back(initial_rect); |
| + } |
| + gfx::Rect bounds; |
| + std::vector<gfx::Rect> rects; |
| +}; |
| + |
| +void AccessibilityFocusRingController::RectsToRings( |
| + const std::vector<gfx::Rect>& rects, |
| + std::vector<AccessibilityFocusRing>* rings) const { |
| + if (rects.size() == 0) |
|
xiyuan
2014/09/11 03:33:10
nit: rects.size() == 0 -> rects.empty()
dmazzoni
2014/09/11 21:44:38
Done.
|
| + return; |
| + |
| + // Split the rects into contiguous regions. |
| + std::vector<Region> regions; |
| + regions.push_back(Region(rects[0])); |
| + for (size_t i = 1; i < rects.size(); ++i) { |
| + bool found = false; |
| + for (size_t j = 0; j < regions.size(); ++j) { |
| + if (Intersects(rects[i], regions[j].bounds)) { |
|
xiyuan
2014/09/11 03:33:10
Should we set |found| to true here?
dmazzoni
2014/09/11 21:44:39
Yes, thanks
|
| + regions[j].rects.push_back(rects[i]); |
| + regions[j].bounds.Union(rects[i]); |
| + } |
| + } |
| + if (!found) { |
| + regions.push_back(Region(rects[i])); |
| + } |
| + } |
| + |
| + // Keep merging regions that intersect. |
| + // TODO(dmazzoni): reduce the worst-case complexity! This appears like |
| + // it could be O(n^3), make sure it's not in practice. |
| + bool merged; |
| + do { |
| + merged = false; |
| + for (size_t i = 0; i < regions.size() - 1 && !merged; ++i) { |
| + for (size_t j = i + 1; j < regions.size() && !merged; ++j) { |
| + if (Intersects(regions[i].bounds, regions[j].bounds)) { |
| + regions[i].rects.insert(regions[i].rects.end(), |
| + regions[j].rects.begin(), |
| + regions[j].rects.end()); |
| + regions[i].bounds.Union(regions[j].bounds); |
| + regions.erase(regions.begin() + j); |
| + merged = true; |
| + } |
| + } |
| + } |
| + } while (merged); |
| + |
| + for (size_t i = 0; i < regions.size(); ++i) { |
| + std::sort(regions[i].rects.begin(), regions[i].rects.end()); |
| + rings->push_back(RingFromSortedRects(regions[i].rects)); |
| + } |
| +} |
| + |
| +int AccessibilityFocusRingController::GetMargin() const { |
| + return kAccessibilityFocusRingMargin; |
| +} |
| + |
| +// Given a vector of rects that all overlap, already sorted from top to bottom |
| +// and left to right, split them into three shapes covering the top, middle, |
| +// and bottom of a "paragraph shape". |
| +// |
| +// Input: |
| +// |
| +// +---+---+ |
| +// | 1 | 2 | |
| +// +---------------------+---+---+ |
| +// | 3 | |
| +// +--------+---------------+----+ |
| +// | 4 | 5 | |
| +// +--------+---------------+--+ |
| +// | 6 | |
| +// +---------+-----------------+ |
| +// | 7 | |
| +// +---------+ |
| +// |
| +// Output: |
| +// |
| +// +-------+ |
| +// | Top | |
| +// +---------------------+-------+ |
| +// | | |
| +// | | |
| +// | Middle | |
| +// | | |
| +// | | |
| +// +---------+-------------------+ |
| +// | Bottom | |
| +// +---------+ |
| +// |
| +// When there's no clear "top" or "bottom" segment, split the overall rect |
| +// evenly so that some of the area still fits into the "top" and "bottom" |
| +// segments. |
| +void AccessibilityFocusRingController::SplitIntoParagraphShape( |
| + const std::vector<gfx::Rect>& rects, |
| + gfx::Rect* top, |
| + gfx::Rect* middle, |
| + gfx::Rect* bottom) const { |
| + size_t n = rects.size(); |
| + |
| + // Figure out how many rects belong in the top portion. |
| + gfx::Rect top_rect = rects[0]; |
| + int top_middle = (top_rect.y() + top_rect.bottom()) / 2; |
| + size_t top_count = 1; |
| + while (top_count < n && rects[top_count].y() < top_middle) { |
| + top_rect.Union(rects[top_count]); |
| + top_middle = (top_rect.y() + top_rect.bottom()) / 2; |
| + top_count++; |
| + } |
| + |
| + // Figure out how many rects belong in the bottom portion. |
| + gfx::Rect bottom_rect = rects[n - 1]; |
| + int bottom_middle = (bottom_rect.y() + bottom_rect.bottom()) / 2; |
| + size_t bottom_count = std::min(static_cast<size_t>(1), n - top_count); |
| + while (bottom_count + top_count < n && |
| + rects[n - bottom_count - 1].bottom() > bottom_middle) { |
| + bottom_rect.Union(rects[n - bottom_count - 1]); |
| + bottom_middle = (bottom_rect.y() + bottom_rect.bottom()) / 2; |
| + bottom_count++; |
| + } |
| + |
| + // Whatever's left goes to the middle rect, but if there's no middle or |
| + // bottom rect, split the existing rects evenly to make one. |
| + gfx::Rect middle_rect; |
| + if (top_count + bottom_count < n) { |
| + middle_rect = rects[top_count]; |
| + for (size_t i = top_count + 1; i < n - bottom_count; i++) |
| + middle_rect.Union(rects[i]); |
| + } else if (bottom_count > 0) { |
| + gfx::Rect enclosing_rect = top_rect; |
| + enclosing_rect.Union(bottom_rect); |
| + int middle_top = (top_rect.y() + top_rect.bottom() * 2) / 3; |
| + int middle_bottom = (bottom_rect.y() * 2 + bottom_rect.bottom()) / 3; |
| + top_rect.set_height(middle_top - top_rect.y()); |
| + bottom_rect.set_height(bottom_rect.bottom() - middle_bottom); |
| + bottom_rect.set_y(middle_bottom); |
| + middle_rect = gfx::Rect(enclosing_rect.x(), middle_top, |
| + enclosing_rect.width(), middle_bottom - middle_top); |
| + } else { |
| + int middle_top = (top_rect.y() * 2 + top_rect.bottom()) / 3; |
| + int middle_bottom = (top_rect.y() + top_rect.bottom() * 2) / 3; |
| + middle_rect = gfx::Rect(top_rect.x(), middle_top, |
| + top_rect.width(), middle_bottom - middle_top); |
| + bottom_rect = gfx::Rect( |
| + top_rect.x(), middle_bottom, |
| + top_rect.width(), top_rect.bottom() - middle_bottom); |
| + top_rect.set_height(middle_top - top_rect.y()); |
| + } |
| + |
| + if (middle_rect.y() > top_rect.bottom()) { |
| + middle_rect.set_height( |
| + middle_rect.height() + middle_rect.y() - top_rect.bottom()); |
| + middle_rect.set_y(top_rect.bottom()); |
| + } |
| + |
| + if (middle_rect.bottom() < bottom_rect.y()) { |
| + middle_rect.set_height(bottom_rect.y() - middle_rect.y()); |
| + } |
| + |
| + *top = top_rect; |
| + *middle = middle_rect; |
| + *bottom = bottom_rect; |
| +} |
| + |
| +AccessibilityFocusRing AccessibilityFocusRingController::RingFromSortedRects( |
| + const std::vector<gfx::Rect>& rects) const { |
| + if (rects.size() == 1) |
| + return AccessibilityFocusRing::CreateWithRect(rects[0], GetMargin()); |
| + |
| + gfx::Rect top; |
| + gfx::Rect middle; |
| + gfx::Rect bottom; |
| + SplitIntoParagraphShape(rects, &top, &middle, &bottom); |
| + |
| + return AccessibilityFocusRing::CreateWithParagraphShape( |
| + top, middle, bottom, GetMargin()); |
| +} |
| + |
| +bool AccessibilityFocusRingController::Intersects( |
| + const gfx::Rect& r1, const gfx::Rect& r2) const { |
| + int slop = GetMargin(); |
| + return (r2.x() <= r1.right() + slop && |
| + r2.right() >= r1.x() - slop && |
| + r2.y() <= r1.bottom() + slop && |
| + r2.bottom() >= r1.y() - slop); |
| +} |
| + |
| +void AccessibilityFocusRingController::OnDeviceScaleFactorChanged() { |
| + Update(); |
| +} |
| + |
| +} // namespace chromeos |