header_view.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/frame/header_view.h"
  5. #include <memory>
  6. #include "ash/shell.h"
  7. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  8. #include "ash/wm/window_state.h"
  9. #include "base/auto_reset.h"
  10. #include "chromeos/ui/base/window_properties.h"
  11. #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
  12. #include "chromeos/ui/frame/caption_buttons/frame_back_button.h"
  13. #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
  14. #include "chromeos/ui/frame/default_frame_header.h"
  15. #include "ui/aura/client/aura_constants.h"
  16. #include "ui/base/metadata/metadata_impl_macros.h"
  17. #include "ui/base/ui_base_features.h"
  18. #include "ui/compositor/layer.h"
  19. #include "ui/views/controls/image_view.h"
  20. #include "ui/views/widget/widget.h"
  21. #include "ui/views/window/non_client_view.h"
  22. namespace ash {
  23. using ::chromeos::DefaultFrameHeader;
  24. using ::chromeos::kFrameActiveColorKey;
  25. using ::chromeos::kFrameInactiveColorKey;
  26. // The view used to draw the content (background and title string)
  27. // of the header. This is a separate view so that it can use
  28. // different scaling strategy than the rest of the frame such
  29. // as caption buttons.
  30. class HeaderView::HeaderContentView : public views::View {
  31. public:
  32. HeaderContentView(HeaderView* header_view) : header_view_(header_view) {}
  33. HeaderContentView(const HeaderContentView&) = delete;
  34. HeaderContentView& operator=(const HeaderContentView&) = delete;
  35. ~HeaderContentView() override = default;
  36. // views::View:
  37. views::PaintInfo::ScaleType GetPaintScaleType() const override {
  38. return scale_type_;
  39. }
  40. void OnPaint(gfx::Canvas* canvas) override {
  41. header_view_->PaintHeaderContent(canvas);
  42. }
  43. void SetScaleType(views::PaintInfo::ScaleType scale_type) {
  44. scale_type_ = scale_type;
  45. }
  46. private:
  47. HeaderView* header_view_;
  48. views::PaintInfo::ScaleType scale_type_ =
  49. views::PaintInfo::ScaleType::kScaleWithEdgeSnapping;
  50. };
  51. HeaderView::HeaderView(views::Widget* target_widget,
  52. views::NonClientFrameView* frame_view)
  53. : target_widget_(target_widget) {
  54. header_content_view_ =
  55. AddChildView(std::make_unique<HeaderContentView>(this));
  56. caption_button_container_ =
  57. AddChildView(std::make_unique<chromeos::FrameCaptionButtonContainerView>(
  58. target_widget_));
  59. caption_button_container_->UpdateCaptionButtonState(false /*=animate*/);
  60. frame_header_ = std::make_unique<DefaultFrameHeader>(
  61. target_widget,
  62. (frame_view ? static_cast<views::View*>(frame_view) : this),
  63. caption_button_container_);
  64. }
  65. void HeaderView::Init() {
  66. UpdateBackButton();
  67. UpdateCenterButton();
  68. frame_header_->UpdateFrameColors();
  69. aura::Window* window = target_widget_->GetNativeWindow();
  70. window_observation_.Observe(window);
  71. Shell::Get()->tablet_mode_controller()->AddObserver(this);
  72. }
  73. HeaderView::~HeaderView() {
  74. if (Shell::Get()->tablet_mode_controller())
  75. Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
  76. }
  77. void HeaderView::SchedulePaintForTitle() {
  78. frame_header_->SchedulePaintForTitle();
  79. }
  80. void HeaderView::ResetWindowControls() {
  81. caption_button_container_->ResetWindowControls();
  82. }
  83. int HeaderView::GetPreferredOnScreenHeight() {
  84. if (in_immersive_mode_) {
  85. return static_cast<int>(GetPreferredHeight() *
  86. fullscreen_visible_fraction_);
  87. }
  88. return (target_widget_ && target_widget_->IsFullscreen())
  89. ? 0
  90. : GetPreferredHeight();
  91. }
  92. int HeaderView::GetPreferredHeight() {
  93. // Calculating the preferred height requires at least one Layout().
  94. if (!did_layout_)
  95. Layout();
  96. return frame_header_->GetHeaderHeightForPainting();
  97. }
  98. int HeaderView::GetMinimumWidth() const {
  99. return frame_header_->GetMinimumHeaderWidth();
  100. }
  101. void HeaderView::SetAvatarIcon(const gfx::ImageSkia& avatar) {
  102. const bool show = !avatar.isNull();
  103. if (!show) {
  104. if (!avatar_icon_)
  105. return;
  106. delete avatar_icon_;
  107. avatar_icon_ = nullptr;
  108. } else {
  109. DCHECK_EQ(avatar.width(), avatar.height());
  110. if (!avatar_icon_) {
  111. avatar_icon_ = new views::ImageView();
  112. AddChildView(avatar_icon_);
  113. }
  114. avatar_icon_->SetImage(avatar);
  115. }
  116. frame_header_->SetLeftHeaderView(avatar_icon_);
  117. Layout();
  118. }
  119. void HeaderView::UpdateCaptionButtons() {
  120. caption_button_container_->ResetWindowControls();
  121. caption_button_container_->UpdateCaptionButtonState(true /*=animate*/);
  122. UpdateBackButton();
  123. UpdateCenterButton();
  124. Layout();
  125. }
  126. void HeaderView::SetWidthInPixels(int width_in_pixels) {
  127. frame_header_->SetWidthInPixels(width_in_pixels);
  128. // If the width is given in pixels, use uniform scaling
  129. // so that UndoDeviceScaleFactor can correctly undo the scaling.
  130. header_content_view_->SetScaleType(
  131. width_in_pixels > 0
  132. ? views::PaintInfo::ScaleType::kUniformScaling
  133. : views::PaintInfo::ScaleType::kScaleWithEdgeSnapping);
  134. }
  135. void HeaderView::Layout() {
  136. did_layout_ = true;
  137. header_content_view_->SetBoundsRect(GetLocalBounds());
  138. frame_header_->LayoutHeader();
  139. }
  140. void HeaderView::ChildPreferredSizeChanged(views::View* child) {
  141. if (child != caption_button_container_)
  142. return;
  143. // May be null during view initialization.
  144. if (parent())
  145. parent()->Layout();
  146. }
  147. bool HeaderView::IsDrawn() const {
  148. if (is_drawn_override_)
  149. return true;
  150. return views::View::IsDrawn();
  151. }
  152. void HeaderView::OnTabletModeStarted() {
  153. UpdateCaptionButtonsVisibility();
  154. caption_button_container_->UpdateCaptionButtonState(true /*=animate*/);
  155. parent()->Layout();
  156. if (target_widget_ &&
  157. Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars(
  158. target_widget_)) {
  159. target_widget_->non_client_view()->Layout();
  160. }
  161. }
  162. void HeaderView::OnTabletModeEnded() {
  163. UpdateCaptionButtonsVisibility();
  164. caption_button_container_->UpdateCaptionButtonState(true /*=animate*/);
  165. parent()->Layout();
  166. if (target_widget_)
  167. target_widget_->non_client_view()->Layout();
  168. }
  169. void HeaderView::OnWindowPropertyChanged(aura::Window* window,
  170. const void* key,
  171. intptr_t old) {
  172. if (!target_widget_)
  173. return;
  174. DCHECK_EQ(target_widget_->GetNativeWindow(), window);
  175. if (key == aura::client::kAvatarIconKey) {
  176. gfx::ImageSkia* const avatar_icon =
  177. window->GetProperty(aura::client::kAvatarIconKey);
  178. SetAvatarIcon(avatar_icon ? *avatar_icon : gfx::ImageSkia());
  179. } else if (key == kFrameActiveColorKey || key == kFrameInactiveColorKey) {
  180. frame_header_->UpdateFrameColors();
  181. } else if (key == aura::client::kShowStateKey) {
  182. frame_header_->OnShowStateChanged(
  183. window->GetProperty(aura::client::kShowStateKey));
  184. } else if (key == chromeos::kWindowStateTypeKey) {
  185. // Float state is an ash specific state that changes the header UI. It isn't
  186. // a show state so we need to watch the window state type key as well.
  187. if (window->GetProperty(chromeos::kWindowStateTypeKey) ==
  188. chromeos::WindowStateType::kFloated ||
  189. static_cast<chromeos::WindowStateType>(old) ==
  190. chromeos::WindowStateType::kFloated) {
  191. frame_header_->OnFloatStateChanged();
  192. }
  193. }
  194. }
  195. void HeaderView::OnWindowDestroying(aura::Window* window) {
  196. DCHECK(window_observation_.IsObservingSource(window));
  197. window_observation_.Reset();
  198. // A HeaderView may outlive the target widget.
  199. target_widget_ = nullptr;
  200. }
  201. void HeaderView::OnDisplayMetricsChanged(const display::Display& display,
  202. uint32_t changed_metrics) {
  203. if ((changed_metrics & chromeos::TabletState::DISPLAY_METRIC_ROTATION) &&
  204. frame_header_) {
  205. frame_header_->LayoutHeader();
  206. }
  207. }
  208. views::View* HeaderView::avatar_icon() const {
  209. return avatar_icon_;
  210. }
  211. void HeaderView::SetShouldPaintHeader(bool paint) {
  212. if (should_paint_ == paint)
  213. return;
  214. should_paint_ = paint;
  215. UpdateCaptionButtonsVisibility();
  216. SchedulePaint();
  217. }
  218. views::FrameCaptionButton* HeaderView::GetBackButton() {
  219. return frame_header_->GetBackButton();
  220. }
  221. void HeaderView::OnImmersiveRevealStarted() {
  222. fullscreen_visible_fraction_ = 0;
  223. add_layer_for_immersive_ = !layer();
  224. if (add_layer_for_immersive_)
  225. SetPaintToLayer();
  226. // AppWindow may call this before being added to the widget.
  227. // https://crbug.com/825260.
  228. if (layer()->parent()) {
  229. // The immersive layer should always be top.
  230. layer()->parent()->StackAtTop(layer());
  231. }
  232. parent()->Layout();
  233. }
  234. void HeaderView::OnImmersiveRevealEnded() {
  235. fullscreen_visible_fraction_ = 0;
  236. if (add_layer_for_immersive_)
  237. DestroyLayer();
  238. parent()->Layout();
  239. }
  240. void HeaderView::OnImmersiveFullscreenEntered() {
  241. in_immersive_mode_ = true;
  242. parent()->InvalidateLayout();
  243. if (!immersive_mode_changed_callback_.is_null())
  244. immersive_mode_changed_callback_.Run();
  245. }
  246. void HeaderView::OnImmersiveFullscreenExited() {
  247. in_immersive_mode_ = false;
  248. fullscreen_visible_fraction_ = 0;
  249. if (add_layer_for_immersive_)
  250. DestroyLayer();
  251. parent()->InvalidateLayout();
  252. if (!immersive_mode_changed_callback_.is_null())
  253. immersive_mode_changed_callback_.Run();
  254. }
  255. void HeaderView::SetVisibleFraction(double visible_fraction) {
  256. if (fullscreen_visible_fraction_ != visible_fraction) {
  257. fullscreen_visible_fraction_ = visible_fraction;
  258. parent()->Layout();
  259. }
  260. }
  261. std::vector<gfx::Rect> HeaderView::GetVisibleBoundsInScreen() const {
  262. // TODO(pkotwicz): Implement views::View::ConvertRectToScreen().
  263. base::AutoReset<bool> reset(&is_drawn_override_, true);
  264. gfx::Rect visible_bounds(GetVisibleBounds());
  265. gfx::Point visible_origin_in_screen(visible_bounds.origin());
  266. views::View::ConvertPointToScreen(this, &visible_origin_in_screen);
  267. std::vector<gfx::Rect> bounds_in_screen;
  268. bounds_in_screen.push_back(
  269. gfx::Rect(visible_origin_in_screen, visible_bounds.size()));
  270. return bounds_in_screen;
  271. }
  272. void HeaderView::Relayout() {
  273. parent()->Layout();
  274. }
  275. void HeaderView::PaintHeaderContent(gfx::Canvas* canvas) {
  276. if (!should_paint_ || !target_widget_)
  277. return;
  278. frame_header_->PaintHeader(canvas);
  279. }
  280. void HeaderView::UpdateBackButton() {
  281. bool has_back_button = caption_button_container_->model()->IsVisible(
  282. views::CAPTION_BUTTON_ICON_BACK);
  283. views::FrameCaptionButton* back_button = frame_header_->GetBackButton();
  284. if (has_back_button) {
  285. if (!back_button) {
  286. back_button = new chromeos::FrameBackButton();
  287. AddChildView(back_button);
  288. frame_header_->SetBackButton(back_button);
  289. }
  290. back_button->SetEnabled(caption_button_container_->model()->IsEnabled(
  291. views::CAPTION_BUTTON_ICON_BACK));
  292. } else {
  293. delete back_button;
  294. frame_header_->SetBackButton(nullptr);
  295. }
  296. }
  297. void HeaderView::UpdateCenterButton() {
  298. bool is_center_button_visible = caption_button_container_->model()->IsVisible(
  299. views::CAPTION_BUTTON_ICON_CENTER);
  300. auto* center_button = frame_header_->GetCenterButton();
  301. if (!center_button)
  302. return;
  303. if (is_center_button_visible) {
  304. if (!center_button->parent())
  305. AddChildView(center_button);
  306. center_button->SetVisible(true);
  307. } else {
  308. center_button->SetVisible(false);
  309. }
  310. }
  311. void HeaderView::UpdateCaptionButtonsVisibility() {
  312. if (!target_widget_)
  313. return;
  314. caption_button_container_->SetVisible(should_paint_);
  315. }
  316. BEGIN_METADATA(HeaderView, views::View)
  317. END_METADATA
  318. } // namespace ash