Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3914)

Unified Diff: chrome/browser/chromeos/ui/accessibility_focus_ring_controller.cc

Issue 557393003: Introduce AccessibilityFocusRing. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@accessibility_private_histograms
Patch Set: Address feedback Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..aaca4c5199bbd39b983223edf8f44c68596c5b8c
--- /dev/null
+++ b/chrome/browser/chromeos/ui/accessibility_focus_ring_controller.cc
@@ -0,0 +1,247 @@
+// 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 "chrome/browser/chromeos/ui/focus_ring_layer.h"
+
+namespace chromeos {
+
+namespace {
+
+// 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;
+
+// A Region is an unordered collection of Rects that maintains its
+// bounding box. Used in the middle of an algorithm that groups
+// adjacent and overlapping rects.
+struct Region {
+ explicit Region(gfx::Rect initial_rect) {
+ bounds = initial_rect;
+ rects.push_back(initial_rect);
+ }
+ gfx::Rect bounds;
+ std::vector<gfx::Rect> rects;
+};
+
+} // namespace
+
+// 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_.empty())
+ main_focus_ring_layer_->Set(rings_[0]);
+}
+
+void AccessibilityFocusRingController::RectsToRings(
+ const std::vector<gfx::Rect>& rects,
+ std::vector<AccessibilityFocusRing>* rings) const {
+ if (rects.empty())
+ 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)) {
+ regions[j].rects.push_back(rects[i]);
+ regions[j].bounds.Union(rects[i]);
+ found = true;
+ }
+ }
+ 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

Powered by Google App Engine
This is Rietveld 408576698