// Copyright 2016 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. #ifndef UI_VIEWS_CONTROLS_FOCUS_RING_H_ #define UI_VIEWS_CONTROLS_FOCUS_RING_H_ #include #include "base/scoped_observation.h" #include "ui/base/class_property.h" #include "ui/native_theme/native_theme.h" #include "ui/views/controls/focusable_border.h" #include "ui/views/view.h" #include "ui/views/view_observer.h" #include "ui/views/views_export.h" namespace views { class HighlightPathGenerator; // FocusRing is a View that is designed to act as an indicator of focus for its // parent. It is a view that paints to a layer which extends beyond the bounds // of its parent view. // If MyView should show a rounded rectangular focus ring when it has focus and // hide the ring when it loses focus, no other configuration is necessary. In // other cases, it might be necessary to use the Set*() functions on FocusRing; // these take care of repainting it when the state changes. // TODO(tluk): FocusRing should not be a view but instead a new concept which // only participates in view painting ( https://crbug.com/840796 ). class VIEWS_EXPORT FocusRing : public View, public ViewObserver { public: METADATA_HEADER(FocusRing); using ViewPredicate = std::function; // Creates a FocusRing and adds it to `host`. static void Install(View* host); // Gets the FocusRing, if present, from `host`. static FocusRing* Get(View* host); static const FocusRing* Get(const View* host); // Removes the FocusRing, if present, from `host`. static void Remove(View* host); // The thickness and inset amount of focus ring halos. static const float kHaloThickness; static const float kHaloInset; ~FocusRing() override; // Sets the HighlightPathGenerator to draw this FocusRing around. // Note: This method should only be used if the focus ring needs to differ // from the highlight shape used for InkDrops. // Otherwise install a HighlightPathGenerator on the parent and FocusRing will // use it as well. void SetPathGenerator(std::unique_ptr generator); // Sets whether the FocusRing should show an invalid state for the View it // encloses. void SetInvalid(bool invalid); // Sets the predicate function used to tell when the parent has focus. The // parent is passed into this predicate; it should return whether the parent // should be treated as focused. This is useful when, for example, the parent // wraps an inner view and the inner view is the one that actually receives // focus, but the FocusRing sits on the parent instead of the inner view. void SetHasFocusPredicate(const ViewPredicate& predicate); void SetColor(absl::optional color); // View: void Layout() override; void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; void OnPaint(gfx::Canvas* canvas) override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; // ViewObserver: void OnViewFocused(View* view) override; void OnViewBlurred(View* view) override; private: FocusRing(); SkPath GetPath() const; SkRRect GetRingRoundRect() const; void RefreshLayer(); // Translates the provided SkRect or SkRRect, which is in the parent's // coordinate system, into this view's coordinate system, then insets it // appropriately to produce the focus ring "halo" effect. If the supplied rect // is an SkRect, it will have the default focus ring corner radius applied as // well. SkRRect RingRectFromPathRect(const SkRect& rect) const; SkRRect RingRectFromPathRect(const SkRRect& rect) const; // The path generator used to draw this focus ring. std::unique_ptr path_generator_; // Whether the enclosed View is in an invalid state, which controls whether // the focus ring shows an invalid appearance (usually a different color). bool invalid_ = false; // Overriding color for the focus ring. absl::optional color_; // The predicate used to determine whether the parent has focus. absl::optional has_focus_predicate_; base::ScopedObservation view_observation_{this}; DISALLOW_COPY_AND_ASSIGN(FocusRing); }; VIEWS_EXPORT SkPath GetHighlightPath(const View* view); // Set this on the FocusRing host to have the FocusRing paint an outline around // itself. This ensures that the FocusRing has sufficient contrast with its // surroundings (this is used for prominent MdTextButtons because they are blue, // while the background is light/dark, and the FocusRing doesn't contrast well // with both the interior and exterior of the button). This may need some polish // (such as blur?) in order to be expandable to all controls. For now it solves // color contrast on prominent buttons which is an a11y issue. See // https://crbug.com/1197631. // TODO(pbos): Consider polishing this well enough that this can be // unconditional. This may require rolling out `kCascadingBackgroundColor` to // more surfaces to have an accurate background color. VIEWS_EXPORT extern const ui::ClassProperty* const kDrawFocusRingBackgroundOutline; } // namespace views #endif // UI_VIEWS_CONTROLS_FOCUS_RING_H_