1 // Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be found
3 // in the LICENSE file.
4
5 #ifndef CEF_LIBCEF_BROWSER_VIEWS_VIEW_VIEW_H_
6 #define CEF_LIBCEF_BROWSER_VIEWS_VIEW_VIEW_H_
7 #pragma once
8
9 #include "include/views/cef_view.h"
10 #include "include/views/cef_view_delegate.h"
11
12 #include "libcef/browser/thread_util.h"
13 #include "libcef/browser/views/view_util.h"
14
15 #include "base/logging.h"
16 #include "ui/views/accessibility/accessibility_paint_checks.h"
17 #include "ui/views/background.h"
18 #include "ui/views/view.h"
19
20 // Helpers for template boiler-plate.
21 #define CEF_VIEW_VIEW_T \
22 template <class ViewsViewClass, class CefViewDelegateClass>
23 #define CEF_VIEW_VIEW_A ViewsViewClass, CefViewDelegateClass
24 #define CEF_VIEW_VIEW_D CefViewView<CEF_VIEW_VIEW_A>
25
26 // Base template for implementing views::View-derived classes. The views::View-
27 // derived type passed to this template must provide a no-argument constructor
28 // (for example, see LabelButtonEx from basic_label_button_view.h). See comments
29 // in view_impl.h for a usage overview.
30 CEF_VIEW_VIEW_T class CefViewView : public ViewsViewClass {
31 public:
32 using ParentClass = ViewsViewClass;
33
34 // Should be created from CreateRootView() in a CefViewImpl-derived class.
35 // Do not call complex views::View-derived methods from a CefViewView-derived
36 // constructor as they may attempt to call back into CefViewImpl before
37 // registration has been performed. |cef_delegate| may be nullptr.
38 template <typename... Args>
CefViewView(CefViewDelegateClass * cef_delegate,Args...args)39 explicit CefViewView(CefViewDelegateClass* cef_delegate, Args... args)
40 : ParentClass(args...), cef_delegate_(cef_delegate) {}
41
42 // Should be called from InitializeRootView() in the CefViewImpl-derived
43 // class that created this object. This method will be called after
44 // CefViewImpl registration has completed so it is safe to call complex
45 // views::View-derived methods here.
Initialize()46 virtual void Initialize() {
47 // Use our defaults instead of the Views framework defaults.
48 ParentClass::SetBackground(
49 views::CreateSolidBackground(view_util::kDefaultBackgroundColor));
50
51 // TODO(crbug.com/1218186): Remove this, if this view is focusable then it
52 // needs to add a name so that the screen reader knows what to announce.
53 ParentClass::SetProperty(views::kSkipAccessibilityPaintChecks, true);
54 }
55
56 // Returns the CefViewDelegate-derived delegate associated with this view.
57 // May return nullptr.
cef_delegate()58 CefViewDelegateClass* cef_delegate() const { return cef_delegate_; }
59
60 // Returns the CefView associated with this view. May return nullptr during
61 // CefViewImpl initialization. If callbacks to the CefViewImpl-derived class
62 // are required define an interface that the CefViewImpl-derived class can
63 // implement and pass as an unowned instance to this object's constructor (see
64 // for example CefWindowView).
GetCefView()65 CefRefPtr<CefView> GetCefView() const {
66 CefRefPtr<CefView> view = view_util::GetFor(this, false);
67 DCHECK(view);
68 return view;
69 }
70
71 // views::View methods:
72 gfx::Size CalculatePreferredSize() const override;
73 gfx::Size GetMinimumSize() const override;
74 gfx::Size GetMaximumSize() const override;
75 int GetHeightForWidth(int w) const override;
76 void Layout() override;
77 void ViewHierarchyChanged(
78 const views::ViewHierarchyChangedDetails& details) override;
79 void AddedToWidget() override;
80 void RemovedFromWidget() override;
81 void OnFocus() override;
82 void OnBlur() override;
83
84 // Return true if this View is expected to have a minimum size (for example,
85 // a button where the minimum size is based on the label).
HasMinimumSize()86 virtual bool HasMinimumSize() const { return false; }
87
88 private:
89 void NotifyChildViewChanged(
90 const views::ViewHierarchyChangedDetails& details);
91 void NotifyParentViewChanged(
92 const views::ViewHierarchyChangedDetails& details);
93
94 // Not owned by this object.
95 CefViewDelegateClass* const cef_delegate_;
96 };
97
CalculatePreferredSize()98 CEF_VIEW_VIEW_T gfx::Size CEF_VIEW_VIEW_D::CalculatePreferredSize() const {
99 gfx::Size result;
100 if (cef_delegate()) {
101 CefSize cef_size = cef_delegate()->GetPreferredSize(GetCefView());
102 if (!cef_size.IsEmpty())
103 result = gfx::Size(cef_size.width, cef_size.height);
104 }
105 if (result.IsEmpty())
106 result = ParentClass::CalculatePreferredSize();
107 if (result.IsEmpty()) {
108 // Some layouts like BoxLayout expect the preferred size to be non-empty.
109 // The user may have set the size explicitly. Therefore return the current
110 // size as the preferred size.
111 result = ParentClass::size();
112 }
113 return result;
114 }
115
GetMinimumSize()116 CEF_VIEW_VIEW_T gfx::Size CEF_VIEW_VIEW_D::GetMinimumSize() const {
117 gfx::Size result;
118 if (cef_delegate()) {
119 CefSize cef_size = cef_delegate()->GetMinimumSize(GetCefView());
120 if (!cef_size.IsEmpty())
121 result = gfx::Size(cef_size.width, cef_size.height);
122 }
123 // We don't want to call ParentClass::GetMinimumSize() in all cases because
124 // the default views::View implementation will call GetPreferredSize(). That
125 // may result in size() being returned which keeps the View from shrinking.
126 if (result.IsEmpty() && HasMinimumSize())
127 result = ParentClass::GetMinimumSize();
128 return result;
129 }
130
GetMaximumSize()131 CEF_VIEW_VIEW_T gfx::Size CEF_VIEW_VIEW_D::GetMaximumSize() const {
132 gfx::Size result;
133 if (cef_delegate()) {
134 CefSize cef_size = cef_delegate()->GetMaximumSize(GetCefView());
135 if (!cef_size.IsEmpty())
136 result = gfx::Size(cef_size.width, cef_size.height);
137 }
138 if (result.IsEmpty())
139 result = ParentClass::GetMaximumSize();
140 return result;
141 }
142
GetHeightForWidth(int w)143 CEF_VIEW_VIEW_T int CEF_VIEW_VIEW_D::GetHeightForWidth(int w) const {
144 int result = 0;
145 if (cef_delegate())
146 result = cef_delegate()->GetHeightForWidth(GetCefView(), w);
147 if (result == 0)
148 result = ParentClass::GetHeightForWidth(w);
149 if (result == 0) {
150 // Some layouts like FillLayout will ignore the preferred size if this view
151 // has no children. We want to use the preferred size if not otherwise
152 // specified.
153 result = ParentClass::GetPreferredSize().height();
154 }
155 return result;
156 }
157
Layout()158 CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::Layout() {
159 ParentClass::Layout();
160
161 // If Layout() did not provide a size then use the preferred size.
162 if (ParentClass::size().IsEmpty())
163 ParentClass::SizeToPreferredSize();
164
165 if (cef_delegate()) {
166 const auto new_bounds = ParentClass::bounds();
167 CefRect new_rect(new_bounds.x(), new_bounds.y(), new_bounds.width(),
168 new_bounds.height());
169 cef_delegate()->OnLayoutChanged(GetCefView(), new_rect);
170 }
171 }
172
ViewHierarchyChanged(const views::ViewHierarchyChangedDetails & details)173 CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::ViewHierarchyChanged(
174 const views::ViewHierarchyChangedDetails& details) {
175 NotifyChildViewChanged(details);
176 NotifyParentViewChanged(details);
177 ParentClass::ViewHierarchyChanged(details);
178 }
179
AddedToWidget()180 CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::AddedToWidget() {
181 ParentClass::AddedToWidget();
182 if (cef_delegate())
183 cef_delegate()->OnWindowChanged(GetCefView(), /*added=*/true);
184 }
185
RemovedFromWidget()186 CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::RemovedFromWidget() {
187 if (cef_delegate())
188 cef_delegate()->OnWindowChanged(GetCefView(), /*added=*/false);
189 ParentClass::RemovedFromWidget();
190 }
191
OnFocus()192 CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::OnFocus() {
193 if (cef_delegate())
194 cef_delegate()->OnFocus(GetCefView());
195 ParentClass::OnFocus();
196 }
197
OnBlur()198 CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::OnBlur() {
199 if (cef_delegate())
200 cef_delegate()->OnBlur(GetCefView());
201 ParentClass::OnBlur();
202 }
203
NotifyChildViewChanged(const views::ViewHierarchyChangedDetails & details)204 CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::NotifyChildViewChanged(
205 const views::ViewHierarchyChangedDetails& details) {
206 if (!cef_delegate())
207 return;
208
209 // Only interested with the parent is |this| object and the notification is
210 // about an immediate child (notifications are also sent for grandchildren).
211 if (details.parent != this || details.child->parent() != this)
212 return;
213
214 // Only notify for children that have a known CEF root view. For example,
215 // don't notify when ScrollView adds child scroll bars.
216 CefRefPtr<CefView> child = view_util::GetFor(details.child, false);
217 if (child) {
218 cef_delegate()->OnChildViewChanged(GetCefView(), details.is_add, child);
219 }
220 }
221
NotifyParentViewChanged(const views::ViewHierarchyChangedDetails & details)222 CEF_VIEW_VIEW_T void CEF_VIEW_VIEW_D::NotifyParentViewChanged(
223 const views::ViewHierarchyChangedDetails& details) {
224 if (!cef_delegate())
225 return;
226
227 // Only interested when the child is |this| object and notification is about
228 // the immediate parent (notifications are sent for all parents).
229 if (details.child != this || details.parent != ParentClass::parent())
230 return;
231
232 // The immediate parent might be an intermediate view so find the closest
233 // known CEF root view. |parent| might be nullptr for overlays.
234 CefRefPtr<CefView> parent = view_util::GetFor(details.parent, true);
235 if (parent) {
236 cef_delegate()->OnParentViewChanged(GetCefView(), details.is_add, parent);
237 }
238 }
239
240 #endif // CEF_LIBCEF_BROWSER_VIEWS_VIEW_VIEW_H_
241