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