From afeecbf9c6549bdfb0e00492d206f47a3088b40f Mon Sep 17 00:00:00 2001 From: sky Date: Wed, 18 Nov 2015 18:42:05 -0800 Subject: Gets mustash frames looking like that of ash This is basically a fork of various portions of ash frame code. I left parts we have no support for yet out, but kept some things that we'll need to support eventually. BUG=548426 TEST=none R=ben@chromium.org Review URL: https://codereview.chromium.org/1459653002 Cr-Commit-Position: refs/heads/master@{#360501} --- mash/wm/BUILD.gn | 51 ++- mash/wm/DEPS | 3 + .../frame/caption_buttons/caption_button_types.h | 27 ++ .../frame/caption_buttons/frame_caption_button.cc | 195 ++++++++++++ .../frame/caption_buttons/frame_caption_button.h | 102 ++++++ .../frame_caption_button_container_view.cc | 353 +++++++++++++++++++++ .../frame_caption_button_container_view.h | 135 ++++++++ mash/wm/frame/default_header_painter.cc | 352 ++++++++++++++++++++ mash/wm/frame/default_header_painter.h | 122 +++++++ mash/wm/frame/frame_border_hit_test_controller.cc | 82 +++++ mash/wm/frame/frame_border_hit_test_controller.h | 40 +++ mash/wm/frame/header_painter.h | 52 +++ mash/wm/frame/header_painter_util.cc | 80 +++++ mash/wm/frame/header_painter_util.h | 56 ++++ mash/wm/frame/move_loop.cc | 229 +++++++++++++ mash/wm/frame/move_loop.h | 121 +++++++ mash/wm/frame/move_loop_unittest.cc | 185 +++++++++++ mash/wm/frame/non_client_frame_view_mash.cc | 351 ++++++++++++++++++++ mash/wm/frame/non_client_frame_view_mash.h | 107 +++++++ mash/wm/move_loop.cc | 223 ------------- mash/wm/move_loop.h | 115 ------- mash/wm/move_loop_unittest.cc | 179 ----------- mash/wm/non_client_frame_controller.cc | 14 +- mash/wm/non_client_frame_controller.h | 7 + mash/wm/non_client_frame_view_impl.cc | 101 ------ mash/wm/non_client_frame_view_impl.h | 49 --- mash/wm/window_manager_application.cc | 2 +- mash/wm/window_manager_impl.cc | 17 +- 28 files changed, 2659 insertions(+), 691 deletions(-) create mode 100644 mash/wm/DEPS create mode 100644 mash/wm/frame/caption_buttons/caption_button_types.h create mode 100644 mash/wm/frame/caption_buttons/frame_caption_button.cc create mode 100644 mash/wm/frame/caption_buttons/frame_caption_button.h create mode 100644 mash/wm/frame/caption_buttons/frame_caption_button_container_view.cc create mode 100644 mash/wm/frame/caption_buttons/frame_caption_button_container_view.h create mode 100644 mash/wm/frame/default_header_painter.cc create mode 100644 mash/wm/frame/default_header_painter.h create mode 100644 mash/wm/frame/frame_border_hit_test_controller.cc create mode 100644 mash/wm/frame/frame_border_hit_test_controller.h create mode 100644 mash/wm/frame/header_painter.h create mode 100644 mash/wm/frame/header_painter_util.cc create mode 100644 mash/wm/frame/header_painter_util.h create mode 100644 mash/wm/frame/move_loop.cc create mode 100644 mash/wm/frame/move_loop.h create mode 100644 mash/wm/frame/move_loop_unittest.cc create mode 100644 mash/wm/frame/non_client_frame_view_mash.cc create mode 100644 mash/wm/frame/non_client_frame_view_mash.h delete mode 100644 mash/wm/move_loop.cc delete mode 100644 mash/wm/move_loop.h delete mode 100644 mash/wm/move_loop_unittest.cc delete mode 100644 mash/wm/non_client_frame_view_impl.cc delete mode 100644 mash/wm/non_client_frame_view_impl.h (limited to 'mash') diff --git a/mash/wm/BUILD.gn b/mash/wm/BUILD.gn index 373ef7a..fc8d2e86 100644 --- a/mash/wm/BUILD.gn +++ b/mash/wm/BUILD.gn @@ -5,6 +5,7 @@ import("//build/config/ui.gni") import("//mojo/public/mojo_application.gni") import("//mojo/public/tools/bindings/mojom.gni") +import("//tools/grit/repack.gni") group("wm") { testonly = true @@ -18,14 +19,26 @@ source_set("example_wm_lib") { sources = [ "background_layout.cc", "background_layout.h", + "frame/caption_buttons/caption_button_types.h", + "frame/caption_buttons/frame_caption_button.cc", + "frame/caption_buttons/frame_caption_button.h", + "frame/caption_buttons/frame_caption_button_container_view.cc", + "frame/caption_buttons/frame_caption_button_container_view.h", + "frame/default_header_painter.cc", + "frame/default_header_painter.h", + "frame/frame_border_hit_test_controller.cc", + "frame/frame_border_hit_test_controller.h", + "frame/header_painter.h", + "frame/header_painter_util.cc", + "frame/header_painter_util.h", + "frame/move_loop.cc", + "frame/move_loop.h", + "frame/non_client_frame_view_mash.cc", + "frame/non_client_frame_view_mash.h", "layout_manager.cc", "layout_manager.h", - "move_loop.cc", - "move_loop.h", "non_client_frame_controller.cc", "non_client_frame_controller.h", - "non_client_frame_view_impl.cc", - "non_client_frame_view_impl.h", "property_util.cc", "property_util.h", "shelf_layout.cc", @@ -44,13 +57,19 @@ source_set("example_wm_lib") { "//components/mus/public/cpp", "//components/mus/public/interfaces", "//mash/wm/public/interfaces", + "//mash/wm/resources", "//mojo/application/public/cpp", "//mojo/common:common_base", "//mojo/converters/geometry", "//mojo/converters/input_events", "//mojo/services/tracing/public/cpp", "//skia", + "//ui/aura", + "//ui/events", + "//ui/gfx", + "//ui/gfx/geometry", "//ui/mojo/init", + "//ui/strings", "//ui/views", "//ui/views/mus:for_mojo_application", ] @@ -63,15 +82,33 @@ mojo_native_application("example_wm") { deps = [ ":example_wm_lib", + ":resources", "//mojo/application/public/cpp", - "//ui/views/mus:resources", ] data_deps = [ "//components/mus", ] - resources = [ "$root_out_dir/views_mus_resources.pak" ] + resources = [ "$root_out_dir/mash_wm_resources.pak" ] +} + +repack("resources") { + sources = [ + "$root_gen_dir/mash/wm/resources/mash_wm_resources_100_percent.pak", + "$root_gen_dir/ui/resources/ui_resources_100_percent.pak", + "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak", + "$root_gen_dir/ui/strings/ui_strings_en-US.pak", + "$root_gen_dir/ui/views/resources/views_resources_100_percent.pak", + ] + output = "$root_out_dir/mash_wm_resources.pak" + deps = [ + "//mash/wm/resources", + "//ui/resources", + "//ui/strings", + "//ui/views/mus:resources", + "//ui/views/resources", + ] } mojo_native_application("apptests") { @@ -105,7 +142,7 @@ source_set("unittests") { testonly = true sources = [ - "move_loop_unittest.cc", + "frame/move_loop_unittest.cc", ] deps = [ diff --git a/mash/wm/DEPS b/mash/wm/DEPS new file mode 100644 index 0000000..810a46a --- /dev/null +++ b/mash/wm/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+grit/mash_wm_resources.h", +] diff --git a/mash/wm/frame/caption_buttons/caption_button_types.h b/mash/wm/frame/caption_buttons/caption_button_types.h new file mode 100644 index 0000000..3bda201 --- /dev/null +++ b/mash/wm/frame/caption_buttons/caption_button_types.h @@ -0,0 +1,27 @@ +// Copyright 2015 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 MASH_WM_FRAME_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_ +#define MASH_WM_FRAME_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_ + +namespace mash { +namespace wm { + +// These are the icon types that a caption button can have. The size button's +// action (SnapType) can be different from its icon. +enum CaptionButtonIcon { + CAPTION_BUTTON_ICON_MINIMIZE, + CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, + CAPTION_BUTTON_ICON_CLOSE, + CAPTION_BUTTON_ICON_LEFT_SNAPPED, + CAPTION_BUTTON_ICON_RIGHT_SNAPPED, + CAPTION_BUTTON_ICON_BACK, + CAPTION_BUTTON_ICON_LOCATION, + CAPTION_BUTTON_ICON_COUNT +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_ diff --git a/mash/wm/frame/caption_buttons/frame_caption_button.cc b/mash/wm/frame/caption_buttons/frame_caption_button.cc new file mode 100644 index 0000000..ca1501f --- /dev/null +++ b/mash/wm/frame/caption_buttons/frame_caption_button.cc @@ -0,0 +1,195 @@ +// Copyright 2015 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 "mash/wm/frame/caption_buttons/frame_caption_button.h" + +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/animation/throb_animation.h" +#include "ui/gfx/canvas.h" + +namespace mash { +namespace wm { + +namespace { + +// The duration of the crossfade animation when swapping the button's images. +const int kSwapImagesAnimationDurationMs = 200; + +// The duration of the fade out animation of the old icon during a crossfade +// animation as a ratio of |kSwapImagesAnimationDurationMs|. +const float kFadeOutRatio = 0.5f; + +// The alpha to draw inactive icons with. +const float kInactiveIconAlpha = 0.2f; + +} // namespace + +// static +const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton"; + +FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener, + CaptionButtonIcon icon) + : CustomButton(listener), + icon_(icon), + paint_as_active_(false), + alpha_(255), + icon_image_id_(-1), + hovered_background_image_id_(-1), + pressed_background_image_id_(-1), + swap_images_animation_(new gfx::SlideAnimation(this)) { + swap_images_animation_->Reset(1); + + // Do not flip the gfx::Canvas passed to the OnPaint() method. The snap left + // and snap right button icons should not be flipped. The other icons are + // horizontally symmetrical. +} + +FrameCaptionButton::~FrameCaptionButton() {} + +void FrameCaptionButton::SetImages(CaptionButtonIcon icon, + Animate animate, + int icon_image_id, + int hovered_background_image_id, + int pressed_background_image_id) { + // The early return is dependant on |animate| because callers use SetImages() + // with ANIMATE_NO to progress the crossfade animation to the end. + if (icon == icon_ && + (animate == ANIMATE_YES || !swap_images_animation_->is_animating()) && + icon_image_id == icon_image_id_ && + hovered_background_image_id == hovered_background_image_id_ && + pressed_background_image_id == pressed_background_image_id_) { + return; + } + + if (animate == ANIMATE_YES) + crossfade_icon_image_ = icon_image_; + + icon_ = icon; + icon_image_id_ = icon_image_id; + // TODO(sky): it doesn't seem like these are used. + hovered_background_image_id_ = hovered_background_image_id; + pressed_background_image_id_ = pressed_background_image_id; + + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + icon_image_ = *rb.GetImageSkiaNamed(icon_image_id); + hovered_background_image_ = + *rb.GetImageSkiaNamed(hovered_background_image_id); + pressed_background_image_ = + *rb.GetImageSkiaNamed(pressed_background_image_id); + + if (animate == ANIMATE_YES) { + swap_images_animation_->Reset(0); + swap_images_animation_->SetSlideDuration(kSwapImagesAnimationDurationMs); + swap_images_animation_->Show(); + } else { + swap_images_animation_->Reset(1); + } + PreferredSizeChanged(); + SchedulePaint(); +} + +bool FrameCaptionButton::IsAnimatingImageSwap() const { + return swap_images_animation_->is_animating(); +} + +void FrameCaptionButton::SetAlpha(int alpha) { + if (alpha_ != alpha) { + alpha_ = alpha; + SchedulePaint(); + } +} + +gfx::Size FrameCaptionButton::GetPreferredSize() const { + return hovered_background_image_.isNull() ? gfx::Size() + : hovered_background_image_.size(); +} + +const char* FrameCaptionButton::GetClassName() const { + return kViewClassName; +} + +void FrameCaptionButton::OnPaint(gfx::Canvas* canvas) { + if (hover_animation_->is_animating() || state() == STATE_HOVERED) { + int hovered_background_alpha = + hover_animation_->is_animating() + ? hover_animation_->CurrentValueBetween(0, 255) + : 255; + SkPaint paint; + paint.setAlpha(hovered_background_alpha); + canvas->DrawImageInt(hovered_background_image_, 0, 0, paint); + } else if (state() == STATE_PRESSED) { + canvas->DrawImageInt(pressed_background_image_, 0, 0); + } + + int icon_alpha = swap_images_animation_->CurrentValueBetween(0, 255); + int crossfade_icon_alpha = 0; + if (icon_alpha < static_cast(kFadeOutRatio * 255)) + crossfade_icon_alpha = static_cast(255 - icon_alpha / kFadeOutRatio); + + if (crossfade_icon_alpha > 0 && !crossfade_icon_image_.isNull()) { + gfx::Canvas icon_canvas(icon_image_.size(), canvas->image_scale(), false); + SkPaint paint; + paint.setAlpha(icon_alpha); + icon_canvas.DrawImageInt(icon_image_, 0, 0, paint); + + paint.setAlpha(crossfade_icon_alpha); + paint.setXfermodeMode(SkXfermode::kPlus_Mode); + icon_canvas.DrawImageInt(crossfade_icon_image_, 0, 0, paint); + + PaintCentered(canvas, gfx::ImageSkia(icon_canvas.ExtractImageRep()), + alpha_); + } else { + if (!swap_images_animation_->is_animating()) + icon_alpha = alpha_; + PaintCentered(canvas, icon_image_, icon_alpha); + } +} + +void FrameCaptionButton::OnGestureEvent(ui::GestureEvent* event) { + // CustomButton does not become pressed when the user drags off and then back + // onto the button. Make FrameCaptionButton pressed in this case because this + // behavior is more consistent with AlternateFrameSizeButton. + if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || + event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { + if (HitTestPoint(event->location())) { + SetState(STATE_PRESSED); + RequestFocus(); + event->StopPropagation(); + } else { + SetState(STATE_NORMAL); + } + } else if (event->type() == ui::ET_GESTURE_SCROLL_END) { + if (HitTestPoint(event->location())) { + SetState(STATE_HOVERED); + NotifyClick(*event); + event->StopPropagation(); + } + } + CustomButton::OnGestureEvent(event); +} + +void FrameCaptionButton::PaintCentered(gfx::Canvas* canvas, + const gfx::ImageSkia& to_center, + int alpha) { + if (!paint_as_active_) { + // Paint icons as active when they are hovered over or pressed. + double inactive_alpha = kInactiveIconAlpha; + if (hover_animation_->is_animating()) { + inactive_alpha = + hover_animation_->CurrentValueBetween(inactive_alpha, 1.0f); + } else if (state() == STATE_PRESSED || state() == STATE_HOVERED) { + inactive_alpha = 1.0f; + } + alpha *= inactive_alpha; + } + + SkPaint paint; + paint.setAlpha(alpha); + canvas->DrawImageInt(to_center, (width() - to_center.width()) / 2, + (height() - to_center.height()) / 2, paint); +} + +} // namespace wm +} // namespace mash diff --git a/mash/wm/frame/caption_buttons/frame_caption_button.h b/mash/wm/frame/caption_buttons/frame_caption_button.h new file mode 100644 index 0000000..6708327 --- /dev/null +++ b/mash/wm/frame/caption_buttons/frame_caption_button.h @@ -0,0 +1,102 @@ +// Copyright 2015 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 MASH_WM_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_ +#define MASH_WM_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "mash/wm/frame/caption_buttons/caption_button_types.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/views/controls/button/custom_button.h" + +namespace gfx { +class SlideAnimation; +} + +namespace mash { +namespace wm { + +// Base class for the window caption buttons (minimize, maximize, restore, +// close). +class FrameCaptionButton : public views::CustomButton { + public: + enum Animate { ANIMATE_YES, ANIMATE_NO }; + + static const char kViewClassName[]; + + FrameCaptionButton(views::ButtonListener* listener, CaptionButtonIcon icon); + ~FrameCaptionButton() override; + + // Sets the images to use to paint the button. If |animate| is ANIMATE_YES, + // the button crossfades to the new visuals. If the image ids match those + // currently used by the button and |animate| is ANIMATE_NO the crossfade + // animation is progressed to the end. + void SetImages(CaptionButtonIcon icon, + Animate animate, + int icon_image_id, + int hovered_background_image_id, + int pressed_background_image_id); + + // Returns true if the button is crossfading to new visuals set in + // SetImages(). + bool IsAnimatingImageSwap() const; + + // Sets the alpha to use for painting. Used to animate visibility changes. + void SetAlpha(int alpha); + + // views::View overrides: + gfx::Size GetPreferredSize() const override; + const char* GetClassName() const override; + void OnPaint(gfx::Canvas* canvas) override; + + void set_paint_as_active(bool paint_as_active) { + paint_as_active_ = paint_as_active; + } + + CaptionButtonIcon icon() const { return icon_; } + + int icon_image_id() const { return icon_image_id_; } + + protected: + // views::CustomButton override: + void OnGestureEvent(ui::GestureEvent* event) override; + + private: + // Paints |to_center| centered within the button with |alpha|. + void PaintCentered(gfx::Canvas* canvas, + const gfx::ImageSkia& to_center, + int alpha); + + // The button's current icon. + CaptionButtonIcon icon_; + + // Whether the button should be painted as active. + bool paint_as_active_; + + // Current alpha to use for painting. + int alpha_; + + // The images and image ids used to paint the button. + int icon_image_id_; + int hovered_background_image_id_; + int pressed_background_image_id_; + gfx::ImageSkia icon_image_; + gfx::ImageSkia hovered_background_image_; + gfx::ImageSkia pressed_background_image_; + + // The icon image to crossfade from. + gfx::ImageSkia crossfade_icon_image_; + + // Crossfade animation started when the button's images are changed by + // SetImages(). + scoped_ptr swap_images_animation_; + + DISALLOW_COPY_AND_ASSIGN(FrameCaptionButton); +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_ diff --git a/mash/wm/frame/caption_buttons/frame_caption_button_container_view.cc b/mash/wm/frame/caption_buttons/frame_caption_button_container_view.cc new file mode 100644 index 0000000..3bd443b --- /dev/null +++ b/mash/wm/frame/caption_buttons/frame_caption_button_container_view.cc @@ -0,0 +1,353 @@ +// Copyright 2015 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 "mash/wm/frame/caption_buttons/frame_caption_button_container_view.h" + +#include +#include + +#include "mash/wm/frame/caption_buttons/frame_caption_button.h" +#include "ui/base/hit_test.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/animation/tween.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/point.h" +#include "ui/strings/grit/ui_strings.h" // Accessibility names +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace mash { +namespace wm { + +namespace { + +// Duration of the animation of the position of |minimize_button_|. +const int kPositionAnimationDurationMs = 500; + +// Duration of the animation of the alpha of |size_button_|. +const int kAlphaAnimationDurationMs = 250; + +// Delay during |maximize_mode_animation_| hide to wait before beginning to +// animate the position of |minimize_button_|. +const int kHidePositionDelayMs = 100; + +// Duration of |maximize_mode_animation_| hiding. +// Hiding size button 250 +// |------------------------| +// Delay 100 Slide minimize button 500 +// |---------|-------------------------------------------------| +const int kHideAnimationDurationMs = + kHidePositionDelayMs + kPositionAnimationDurationMs; + +// Delay during |maximize_mode_animation_| show to wait before beginning to +// animate the alpha of |size_button_|. +const int kShowAnimationAlphaDelayMs = 100; + +// Duration of |maximize_mode_animation_| showing. +// Slide minimize button 500 +// |-------------------------------------------------| +// Delay 100 Show size button 250 +// |---------|-----------------------| +const int kShowAnimationDurationMs = kPositionAnimationDurationMs; + +// Value of |maximize_mode_animation_| showing to begin animating alpha of +// |size_button_|. +float SizeButtonShowStartValue() { + return static_cast(kShowAnimationAlphaDelayMs) / + kShowAnimationDurationMs; +} + +// Amount of |maximize_mode_animation_| showing to animate the alpha of +// |size_button_|. +float SizeButtonShowDuration() { + return static_cast(kAlphaAnimationDurationMs) / + kShowAnimationDurationMs; +} + +// Amount of |maximize_mode_animation_| hiding to animate the alpha of +// |size_button_|. +float SizeButtonHideDuration() { + return static_cast(kAlphaAnimationDurationMs) / + kHideAnimationDurationMs; +} + +// Value of |maximize_mode_animation_| hiding to begin animating the position of +// |minimize_button_|. +float HidePositionStartValue() { + return 1.0f - + static_cast(kHidePositionDelayMs) / kHideAnimationDurationMs; +} + +// Converts |point| from |src| to |dst| and hittests against |dst|. +bool ConvertPointToViewAndHitTest(const views::View* src, + const views::View* dst, + const gfx::Point& point) { + gfx::Point converted(point); + views::View::ConvertPointToTarget(src, dst, &converted); + return dst->HitTestPoint(converted); +} + +// Bounds animation values to the range 0.0 - 1.0. Allows for mapping of offset +// animations to the expected range so that gfx::Tween::CalculateValue() can be +// used. +double CapAnimationValue(double value) { + return std::min(1.0, std::max(0.0, value)); +} + +} // namespace + +// static +const char FrameCaptionButtonContainerView::kViewClassName[] = + "FrameCaptionButtonContainerView"; + +FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( + views::Widget* frame) + : frame_(frame), + minimize_button_(NULL), + size_button_(NULL), + close_button_(NULL) { + const bool size_button_visibility = ShouldSizeButtonBeVisible(); + maximize_mode_animation_.reset(new gfx::SlideAnimation(this)); + maximize_mode_animation_->SetTweenType(gfx::Tween::LINEAR); + + // Ensure animation tracks visibility of size button. + if (size_button_visibility) + maximize_mode_animation_->Reset(1.0f); + + // Insert the buttons left to right. + minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); + minimize_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); + minimize_button_->SetVisible(frame_->widget_delegate()->CanMinimize()); + AddChildView(minimize_button_); + + size_button_ = + new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE); + size_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); + size_button_->SetVisible(size_button_visibility); + AddChildView(size_button_); + + close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE); + close_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); + AddChildView(close_button_); +} + +FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() {} + +void FrameCaptionButtonContainerView::SetButtonImages( + CaptionButtonIcon icon, + int icon_image_id, + int hovered_background_image_id, + int pressed_background_image_id) { + button_icon_id_map_[icon] = ButtonIconIds( + icon_image_id, hovered_background_image_id, pressed_background_image_id); + FrameCaptionButton* buttons[] = {minimize_button_, size_button_, + close_button_}; + for (size_t i = 0; i < arraysize(buttons); ++i) { + if (buttons[i]->icon() == icon) { + buttons[i]->SetImages(icon, FrameCaptionButton::ANIMATE_NO, icon_image_id, + hovered_background_image_id, + pressed_background_image_id); + } + } +} + +void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) { + minimize_button_->set_paint_as_active(paint_as_active); + size_button_->set_paint_as_active(paint_as_active); + close_button_->set_paint_as_active(paint_as_active); +} + +void FrameCaptionButtonContainerView::ResetWindowControls() { + SetButtonsToNormal(ANIMATE_NO); +} + +int FrameCaptionButtonContainerView::NonClientHitTest( + const gfx::Point& point) const { + if (close_button_->visible() && + ConvertPointToViewAndHitTest(this, close_button_, point)) { + return HTCLOSE; + } else if (size_button_->visible() && + ConvertPointToViewAndHitTest(this, size_button_, point)) { + return HTMAXBUTTON; + } else if (minimize_button_->visible() && + ConvertPointToViewAndHitTest(this, minimize_button_, point)) { + return HTMINBUTTON; + } + return HTNOWHERE; +} + +void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() { + bool visible = ShouldSizeButtonBeVisible(); + if (visible) { + size_button_->SetVisible(true); + maximize_mode_animation_->SetSlideDuration(kShowAnimationDurationMs); + maximize_mode_animation_->Show(); + } else { + maximize_mode_animation_->SetSlideDuration(kHideAnimationDurationMs); + maximize_mode_animation_->Hide(); + } +} + +gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const { + int width = 0; + for (int i = 0; i < child_count(); ++i) { + const views::View* child = child_at(i); + if (child->visible()) + width += child_at(i)->GetPreferredSize().width(); + } + return gfx::Size(width, close_button_->GetPreferredSize().height()); +} + +void FrameCaptionButtonContainerView::Layout() { + int x = 0; + for (int i = 0; i < child_count(); ++i) { + views::View* child = child_at(i); + if (!child->visible()) + continue; + + gfx::Size size = child->GetPreferredSize(); + child->SetBounds(x, 0, size.width(), size.height()); + x += size.width(); + } + if (maximize_mode_animation_->is_animating()) { + AnimationProgressed(maximize_mode_animation_.get()); + } +} + +const char* FrameCaptionButtonContainerView::GetClassName() const { + return kViewClassName; +} + +void FrameCaptionButtonContainerView::AnimationEnded( + const gfx::Animation* animation) { + // Ensure that position is calculated at least once. + AnimationProgressed(animation); + + double current_value = maximize_mode_animation_->GetCurrentValue(); + if (current_value == 0.0) { + size_button_->SetVisible(false); + PreferredSizeChanged(); + } +} + +void FrameCaptionButtonContainerView::AnimationProgressed( + const gfx::Animation* animation) { + double current_value = animation->GetCurrentValue(); + int size_alpha = 0; + int minimize_x = 0; + if (maximize_mode_animation_->IsShowing()) { + double scaled_value = + CapAnimationValue((current_value - SizeButtonShowStartValue()) / + SizeButtonShowDuration()); + double tweened_value_alpha = + gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value); + size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 0, 255); + + double tweened_value_slide = + gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, current_value); + minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_slide, + size_button_->x(), 0); + } else { + double scaled_value_alpha = + CapAnimationValue((1.0f - current_value) / SizeButtonHideDuration()); + double tweened_value_alpha = + gfx::Tween::CalculateValue(gfx::Tween::EASE_IN, scaled_value_alpha); + size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 255, 0); + + double scaled_value_position = CapAnimationValue( + (HidePositionStartValue() - current_value) / HidePositionStartValue()); + double tweened_value_position = + gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value_position); + minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_position, 0, + size_button_->x()); + } + size_button_->SetAlpha(size_alpha); + minimize_button_->SetX(minimize_x); +} + +void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, + CaptionButtonIcon icon, + Animate animate) { + // The early return is dependant on |animate| because callers use + // SetButtonIcon() with ANIMATE_NO to progress |button|'s crossfade animation + // to the end. + if (button->icon() == icon && + (animate == ANIMATE_YES || !button->IsAnimatingImageSwap())) { + return; + } + + FrameCaptionButton::Animate fcb_animate = + (animate == ANIMATE_YES) ? FrameCaptionButton::ANIMATE_YES + : FrameCaptionButton::ANIMATE_NO; + std::map::const_iterator it = + button_icon_id_map_.find(icon); + if (it != button_icon_id_map_.end()) { + button->SetImages(icon, fcb_animate, it->second.icon_image_id, + it->second.hovered_background_image_id, + it->second.pressed_background_image_id); + } +} + +bool FrameCaptionButtonContainerView::ShouldSizeButtonBeVisible() const { + return frame_->widget_delegate()->CanMaximize(); +} + +void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) { + SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE, + animate); + minimize_button_->SetState(views::Button::STATE_NORMAL); + size_button_->SetState(views::Button::STATE_NORMAL); + close_button_->SetState(views::Button::STATE_NORMAL); +} + +void FrameCaptionButtonContainerView::SetButtonIcons( + CaptionButtonIcon minimize_button_icon, + CaptionButtonIcon close_button_icon, + Animate animate) { + SetButtonIcon(minimize_button_, minimize_button_icon, animate); + SetButtonIcon(close_button_, close_button_icon, animate); +} + +void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender, + const ui::Event& event) { + // Abort any animations of the button icons. + SetButtonsToNormal(ANIMATE_NO); + + if (sender == minimize_button_) { + frame_->Minimize(); + } else if (sender == size_button_) { + if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen. + frame_->Restore(); + } else if (frame_->IsMaximized()) { + frame_->Restore(); + } else { + frame_->Maximize(); + } + } else if (sender == close_button_) { + frame_->Close(); + } +} + +FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds() + : icon_image_id(-1), + hovered_background_image_id(-1), + pressed_background_image_id(-1) {} + +FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds( + int icon_id, + int hovered_background_id, + int pressed_background_id) + : icon_image_id(icon_id), + hovered_background_image_id(hovered_background_id), + pressed_background_image_id(pressed_background_id) {} + +FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() {} + +} // namespace wm +} // namespace mash diff --git a/mash/wm/frame/caption_buttons/frame_caption_button_container_view.h b/mash/wm/frame/caption_buttons/frame_caption_button_container_view.h new file mode 100644 index 0000000..73ae6a8 --- /dev/null +++ b/mash/wm/frame/caption_buttons/frame_caption_button_container_view.h @@ -0,0 +1,135 @@ +// Copyright 2015 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 MASH_WM_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_ +#define MASH_WM_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_ + +#include + +#include "base/macros.h" +#include "mash/wm/frame/caption_buttons/caption_button_types.h" +#include "ui/gfx/animation/animation_delegate.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/view.h" + +namespace gfx { +class SlideAnimation; +} + +namespace views { +class Widget; +} + +namespace mash { +namespace wm { + +class FrameCaptionButton; + +// Container view for the frame caption buttons. It performs the appropriate +// action when a caption button is clicked. +class FrameCaptionButtonContainerView : public views::View, + public views::ButtonListener, + public gfx::AnimationDelegate { + public: + enum Animate { ANIMATE_YES, ANIMATE_NO }; + + static const char kViewClassName[]; + + // |frame| is the views::Widget that the caption buttons act on. + explicit FrameCaptionButtonContainerView(views::Widget* frame); + ~FrameCaptionButtonContainerView() override; + + // Sets the resource ids of the images to paint the button for |icon|. The + // FrameCaptionButtonContainerView will keep track of the images to use for + // |icon| even if none of the buttons currently use |icon|. + void SetButtonImages(CaptionButtonIcon icon, + int icon_image_id, + int hovered_background_image_id, + int pressed_background_image_id); + + // Sets whether the buttons should be painted as active. Does not schedule + // a repaint. + void SetPaintAsActive(bool paint_as_active); + + // Tell the window controls to reset themselves to the normal state. + void ResetWindowControls(); + + // Determines the window HT* code for the caption button at |point|. Returns + // HTNOWHERE if |point| is not over any of the caption buttons. |point| must + // be in the coordinates of the FrameCaptionButtonContainerView. + int NonClientHitTest(const gfx::Point& point) const; + + // Updates the size button's visibility based on whether |frame_| can be + // maximized and if maximize mode is enabled. A parent view should relayout + // to reflect the change in visibility. + void UpdateSizeButtonVisibility(); + + // views::View: + gfx::Size GetPreferredSize() const override; + void Layout() override; + const char* GetClassName() const override; + + // Overridden from gfx::AnimationDelegate: + void AnimationEnded(const gfx::Animation* animation) override; + void AnimationProgressed(const gfx::Animation* animation) override; + + private: + friend class FrameCaptionButtonContainerViewTest; + + struct ButtonIconIds { + ButtonIconIds(); + ButtonIconIds(int icon_id, + int hovered_background_id, + int pressed_background_id); + ~ButtonIconIds(); + + int icon_image_id; + int hovered_background_image_id; + int pressed_background_image_id; + }; + + // Sets |button|'s icon to |icon|. If |animate| is ANIMATE_YES, the button + // will crossfade to the new icon. If |animate| is ANIMATE_NO and + // |icon| == |button|->icon(), the crossfade animation is progressed to the + // end. + void SetButtonIcon(FrameCaptionButton* button, + CaptionButtonIcon icon, + Animate animate); + + // Returns true if maximize mode is not enabled, and |frame_| widget delegate + // can be maximized. + bool ShouldSizeButtonBeVisible() const; + + void SetButtonsToNormal(Animate animate); + void SetButtonIcons(CaptionButtonIcon minimize_button_icon, + CaptionButtonIcon close_button_icon, + Animate animate); + + // views::ButtonListener: + void ButtonPressed(views::Button* sender, const ui::Event& event) override; + + // The widget that the buttons act on. + views::Widget* frame_; + + // The buttons. In the normal button style, at most one of |minimize_button_| + // and |size_button_| is visible. + FrameCaptionButton* minimize_button_; + FrameCaptionButton* size_button_; + FrameCaptionButton* close_button_; + + // Mapping of the images needed to paint a button for each of the values of + // CaptionButtonIcon. + std::map button_icon_id_map_; + + // Animation that affects the position of |minimize_button_| and the + // visibility of |size_button_|. + scoped_ptr maximize_mode_animation_; + + DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerView); +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_ diff --git a/mash/wm/frame/default_header_painter.cc b/mash/wm/frame/default_header_painter.cc new file mode 100644 index 0000000..755ad7f --- /dev/null +++ b/mash/wm/frame/default_header_painter.cc @@ -0,0 +1,352 @@ +// Copyright 2015 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 "mash/wm/frame/default_header_painter.h" + +#include "base/debug/leak_annotations.h" +#include "base/logging.h" +#include "grit/mash_wm_resources.h" +#include "mash/wm/frame/caption_buttons/frame_caption_button_container_view.h" +#include "mash/wm/frame/header_painter_util.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/animation/slide_animation.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" +#include "ui/gfx/font_list.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/scoped_canvas.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/view.h" +#include "ui/views/widget/native_widget_aura.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +using views::Widget; + +namespace { + +// Color for the window title text. +const SkColor kTitleTextColor = SkColorSetRGB(40, 40, 40); +// Color of the active window header/content separator line. +const SkColor kHeaderContentSeparatorColor = SkColorSetRGB(150, 150, 152); +// Color of the inactive window header/content separator line. +const SkColor kHeaderContentSeparatorInactiveColor = + SkColorSetRGB(180, 180, 182); +// The default color of the frame. +const SkColor kDefaultFrameColor = SkColorSetRGB(242, 242, 242); +// Duration of crossfade animation for activating and deactivating frame. +const int kActivationCrossfadeDurationMs = 200; +// Luminance below which to use white caption buttons. +const int kMaxLuminanceForLightButtons = 125; + +// Tiles an image into an area, rounding the top corners. +void TileRoundRect(gfx::Canvas* canvas, + const SkPaint& paint, + const gfx::Rect& bounds, + int corner_radius) { + SkRect rect = gfx::RectToSkRect(bounds); + const SkScalar corner_radius_scalar = SkIntToScalar(corner_radius); + SkScalar radii[8] = {corner_radius_scalar, + corner_radius_scalar, // top-left + corner_radius_scalar, + corner_radius_scalar, // top-right + 0, + 0, // bottom-right + 0, + 0}; // bottom-left + SkPath path; + path.addRoundRect(rect, radii, SkPath::kCW_Direction); + canvas->DrawPath(path, paint); +} + +// Returns the FontList to use for the title. +const gfx::FontList& GetTitleFontList() { + static const gfx::FontList* title_font_list = + new gfx::FontList(views::NativeWidgetAura::GetWindowTitleFontList()); + ANNOTATE_LEAKING_OBJECT_PTR(title_font_list); + return *title_font_list; +} + +} // namespace + +namespace mash { +namespace wm { + +/////////////////////////////////////////////////////////////////////////////// +// DefaultHeaderPainter, public: + +DefaultHeaderPainter::DefaultHeaderPainter() + : frame_(NULL), + view_(NULL), + left_header_view_(NULL), + left_view_x_inset_(HeaderPainterUtil::GetDefaultLeftViewXInset()), + active_frame_color_(kDefaultFrameColor), + inactive_frame_color_(kDefaultFrameColor), + caption_button_container_(NULL), + painted_height_(0), + mode_(MODE_INACTIVE), + initial_paint_(true), + activation_animation_(new gfx::SlideAnimation(this)) {} + +DefaultHeaderPainter::~DefaultHeaderPainter() {} + +void DefaultHeaderPainter::Init( + views::Widget* frame, + views::View* header_view, + FrameCaptionButtonContainerView* caption_button_container) { + DCHECK(frame); + DCHECK(header_view); + DCHECK(caption_button_container); + frame_ = frame; + view_ = header_view; + caption_button_container_ = caption_button_container; + UpdateAllButtonImages(); +} + +int DefaultHeaderPainter::GetMinimumHeaderWidth() const { + // Ensure we have enough space for the window icon and buttons. We allow + // the title string to collapse to zero width. + return GetTitleBounds().x() + + caption_button_container_->GetMinimumSize().width(); +} + +void DefaultHeaderPainter::PaintHeader(gfx::Canvas* canvas, Mode mode) { + Mode old_mode = mode_; + mode_ = mode; + + if (mode_ != old_mode) { + UpdateAllButtonImages(); + if (!initial_paint_ && HeaderPainterUtil::CanAnimateActivation(frame_)) { + activation_animation_->SetSlideDuration(kActivationCrossfadeDurationMs); + if (mode_ == MODE_ACTIVE) + activation_animation_->Show(); + else + activation_animation_->Hide(); + } else { + if (mode_ == MODE_ACTIVE) + activation_animation_->Reset(1); + else + activation_animation_->Reset(0); + } + initial_paint_ = false; + } + + int corner_radius = (frame_->IsMaximized() || frame_->IsFullscreen()) + ? 0 + : HeaderPainterUtil::GetTopCornerRadiusWhenRestored(); + + SkPaint paint; + int active_alpha = activation_animation_->CurrentValueBetween(0, 255); + paint.setColor(color_utils::AlphaBlend(active_frame_color_, + inactive_frame_color_, active_alpha)); + + TileRoundRect(canvas, paint, GetLocalBounds(), corner_radius); + + if (!frame_->IsMaximized() && !frame_->IsFullscreen() && + mode_ == MODE_INACTIVE && !UsesCustomFrameColors()) { + PaintHighlightForInactiveRestoredWindow(canvas); + } + if (frame_->widget_delegate() && + frame_->widget_delegate()->ShouldShowWindowTitle()) { + PaintTitleBar(canvas); + } + if (!UsesCustomFrameColors()) + PaintHeaderContentSeparator(canvas); +} + +void DefaultHeaderPainter::LayoutHeader() { + UpdateSizeButtonImages(ShouldUseLightImages()); + caption_button_container_->Layout(); + + gfx::Size caption_button_container_size = + caption_button_container_->GetPreferredSize(); + caption_button_container_->SetBounds( + view_->width() - caption_button_container_size.width(), 0, + caption_button_container_size.width(), + caption_button_container_size.height()); + + LayoutLeftHeaderView(); + + // The header/content separator line overlays the caption buttons. + SetHeaderHeightForPainting(caption_button_container_->height()); +} + +int DefaultHeaderPainter::GetHeaderHeight() const { + return caption_button_container_->height(); +} + +int DefaultHeaderPainter::GetHeaderHeightForPainting() const { + return painted_height_; +} + +void DefaultHeaderPainter::SetHeaderHeightForPainting(int height) { + painted_height_ = height; +} + +void DefaultHeaderPainter::SchedulePaintForTitle() { + view_->SchedulePaintInRect(GetTitleBounds()); +} + +void DefaultHeaderPainter::UpdateLeftViewXInset(int left_view_x_inset) { + if (left_view_x_inset_ != left_view_x_inset) { + left_view_x_inset_ = left_view_x_inset; + LayoutLeftHeaderView(); + } +} + +void DefaultHeaderPainter::SetFrameColors(SkColor active_frame_color, + SkColor inactive_frame_color) { + active_frame_color_ = active_frame_color; + inactive_frame_color_ = inactive_frame_color; + UpdateAllButtonImages(); +} + +void DefaultHeaderPainter::UpdateLeftHeaderView(views::View* left_header_view) { + left_header_view_ = left_header_view; +} + +/////////////////////////////////////////////////////////////////////////////// +// gfx::AnimationDelegate overrides: + +void DefaultHeaderPainter::AnimationProgressed( + const gfx::Animation* animation) { + view_->SchedulePaintInRect(GetLocalBounds()); +} + +/////////////////////////////////////////////////////////////////////////////// +// DefaultHeaderPainter, private: + +void DefaultHeaderPainter::PaintHighlightForInactiveRestoredWindow( + gfx::Canvas* canvas) { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + gfx::ImageSkia top_edge = + *rb.GetImageSkiaNamed(IDR_MASH_WM_WINDOW_HEADER_SHADE_INACTIVE_TOP); + gfx::ImageSkia left_edge = + *rb.GetImageSkiaNamed(IDR_MASH_WM_WINDOW_HEADER_SHADE_INACTIVE_LEFT); + gfx::ImageSkia right_edge = + *rb.GetImageSkiaNamed(IDR_MASH_WM_WINDOW_HEADER_SHADE_INACTIVE_RIGHT); + gfx::ImageSkia bottom_edge = + *rb.GetImageSkiaNamed(IDR_MASH_WM_WINDOW_HEADER_SHADE_INACTIVE_BOTTOM); + + int left_edge_width = left_edge.width(); + int right_edge_width = right_edge.width(); + canvas->DrawImageInt(left_edge, 0, 0); + canvas->DrawImageInt(right_edge, view_->width() - right_edge_width, 0); + canvas->TileImageInt(top_edge, left_edge_width, 0, + view_->width() - left_edge_width - right_edge_width, + top_edge.height()); + + DCHECK_EQ(left_edge.height(), right_edge.height()); + int bottom = left_edge.height(); + int bottom_height = bottom_edge.height(); + canvas->TileImageInt(bottom_edge, left_edge_width, bottom - bottom_height, + view_->width() - left_edge_width - right_edge_width, + bottom_height); +} + +void DefaultHeaderPainter::PaintTitleBar(gfx::Canvas* canvas) { + // The window icon is painted by its own views::View. + gfx::Rect title_bounds = GetTitleBounds(); + title_bounds.set_x(view_->GetMirroredXForRect(title_bounds)); + canvas->DrawStringRectWithFlags( + frame_->widget_delegate()->GetWindowTitle(), GetTitleFontList(), + kTitleTextColor, title_bounds, gfx::Canvas::NO_SUBPIXEL_RENDERING); +} + +void DefaultHeaderPainter::PaintHeaderContentSeparator(gfx::Canvas* canvas) { + gfx::ScopedCanvas scoped_canvas(canvas); + const float scale = canvas->UndoDeviceScaleFactor(); + gfx::RectF rect(0, painted_height_ * scale - 1, view_->width() * scale, 1); + SkPaint paint; + paint.setColor((mode_ == MODE_ACTIVE) ? kHeaderContentSeparatorColor + : kHeaderContentSeparatorInactiveColor); + canvas->sk_canvas()->drawRect(gfx::RectFToSkRect(rect), paint); +} + +void DefaultHeaderPainter::LayoutLeftHeaderView() { + if (left_header_view_) { + // Vertically center the left header view with respect to the caption button + // container. + // Floor when computing the center of |caption_button_container_|. + gfx::Size size = left_header_view_->GetPreferredSize(); + int icon_offset_y = + caption_button_container_->height() / 2 - size.height() / 2; + left_header_view_->SetBounds(left_view_x_inset_, icon_offset_y, + size.width(), size.height()); + } +} + +bool DefaultHeaderPainter::ShouldUseLightImages() { + int luminance = color_utils::GetLuminanceForColor( + mode_ == MODE_INACTIVE ? inactive_frame_color_ : active_frame_color_); + return luminance < kMaxLuminanceForLightButtons; +} + +void DefaultHeaderPainter::UpdateAllButtonImages() { + bool use_light_images = ShouldUseLightImages(); + caption_button_container_->SetButtonImages( + CAPTION_BUTTON_ICON_MINIMIZE, + use_light_images ? IDR_MASH_WM_WINDOW_CONTROL_ICON_MINIMIZE_WHITE + : IDR_MASH_WM_WINDOW_CONTROL_ICON_MINIMIZE, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_H, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_P); + + UpdateSizeButtonImages(use_light_images); + + caption_button_container_->SetButtonImages( + CAPTION_BUTTON_ICON_CLOSE, + use_light_images ? IDR_MASH_WM_WINDOW_CONTROL_ICON_CLOSE_WHITE + : IDR_MASH_WM_WINDOW_CONTROL_ICON_CLOSE, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_H, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_P); + + caption_button_container_->SetButtonImages( + CAPTION_BUTTON_ICON_LEFT_SNAPPED, + use_light_images ? IDR_MASH_WM_WINDOW_CONTROL_ICON_LEFT_SNAPPED_WHITE + : IDR_MASH_WM_WINDOW_CONTROL_ICON_LEFT_SNAPPED, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_H, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_P); + + caption_button_container_->SetButtonImages( + CAPTION_BUTTON_ICON_RIGHT_SNAPPED, + use_light_images ? IDR_MASH_WM_WINDOW_CONTROL_ICON_RIGHT_SNAPPED_WHITE + : IDR_MASH_WM_WINDOW_CONTROL_ICON_RIGHT_SNAPPED, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_H, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_P); +} + +void DefaultHeaderPainter::UpdateSizeButtonImages(bool use_light_images) { + int icon_id = 0; + if (frame_->IsMaximized() || frame_->IsFullscreen()) { + icon_id = use_light_images ? IDR_MASH_WM_WINDOW_CONTROL_ICON_RESTORE_WHITE + : IDR_MASH_WM_WINDOW_CONTROL_ICON_RESTORE; + } else { + icon_id = use_light_images ? IDR_MASH_WM_WINDOW_CONTROL_ICON_MAXIMIZE_WHITE + : IDR_MASH_WM_WINDOW_CONTROL_ICON_MAXIMIZE; + } + caption_button_container_->SetButtonImages( + CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, icon_id, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_H, + IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_P); +} + +gfx::Rect DefaultHeaderPainter::GetLocalBounds() const { + return gfx::Rect(view_->width(), painted_height_); +} + +gfx::Rect DefaultHeaderPainter::GetTitleBounds() const { + return HeaderPainterUtil::GetTitleBounds( + left_header_view_, caption_button_container_, GetTitleFontList()); +} + +bool DefaultHeaderPainter::UsesCustomFrameColors() const { + return active_frame_color_ != kDefaultFrameColor || + inactive_frame_color_ != kDefaultFrameColor; +} + +} // namespace wm +} // namespace mash diff --git a/mash/wm/frame/default_header_painter.h b/mash/wm/frame/default_header_painter.h new file mode 100644 index 0000000..5e59cb8 --- /dev/null +++ b/mash/wm/frame/default_header_painter.h @@ -0,0 +1,122 @@ +// Copyright 2015 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 MASH_WM_FRAME_DEFAULT_HEADER_PAINTER_H_ +#define MASH_WM_FRAME_DEFAULT_HEADER_PAINTER_H_ + +#include "base/basictypes.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "mash/wm/frame/header_painter.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/animation/animation_delegate.h" + +namespace gfx { +class ImageSkia; +class Rect; +class SlideAnimation; +} +namespace views { +class View; +class Widget; +} + +namespace mash { +namespace wm { + +class FrameCaptionButtonContainerView; + +// Helper class for painting the default window header. +class DefaultHeaderPainter : public HeaderPainter, + public gfx::AnimationDelegate { + public: + DefaultHeaderPainter(); + ~DefaultHeaderPainter() override; + + // DefaultHeaderPainter does not take ownership of any of the parameters. + void Init(views::Widget* frame, + views::View* header_view, + FrameCaptionButtonContainerView* caption_button_container); + + // HeaderPainter overrides: + int GetMinimumHeaderWidth() const override; + void PaintHeader(gfx::Canvas* canvas, Mode mode) override; + void LayoutHeader() override; + int GetHeaderHeight() const override; + int GetHeaderHeightForPainting() const override; + void SetHeaderHeightForPainting(int height) override; + void SchedulePaintForTitle() override; + void UpdateLeftViewXInset(int left_view_x_inset) override; + + // Sets the left header view for the header. Passing NULL removes the view. + void UpdateLeftHeaderView(views::View* left_header_view); + + // Sets the active and inactive frame colors. Note the inactive frame color + // will have some transparency added when the frame is drawn. + void SetFrameColors(SkColor active_frame_color, SkColor inactive_frame_color); + + private: + // gfx::AnimationDelegate override: + void AnimationProgressed(const gfx::Animation* animation) override; + + // Paints highlight around the edge of the header for inactive restored + // windows. + void PaintHighlightForInactiveRestoredWindow(gfx::Canvas* canvas); + + // Paints the title bar, primarily the title string. + void PaintTitleBar(gfx::Canvas* canvas); + + // Paints the header/content separator. + void PaintHeaderContentSeparator(gfx::Canvas* canvas); + + // Layout the left header view. + void LayoutLeftHeaderView(); + + // Whether light caption images should be used. This is the case when the + // background of the frame is dark. + bool ShouldUseLightImages(); + + // Update all the images in the caption buttons. + void UpdateAllButtonImages(); + + // Updates the size button's images. + void UpdateSizeButtonImages(bool use_light_images); + + // Returns the header bounds in the coordinates of |view_|. The header is + // assumed to be positioned at the top left corner of |view_| and to have the + // same width as |view_|. + gfx::Rect GetLocalBounds() const; + + // Returns the bounds for the title. + gfx::Rect GetTitleBounds() const; + + // Returns whether the frame uses custom frame coloring. + bool UsesCustomFrameColors() const; + + views::Widget* frame_; + views::View* view_; + views::View* left_header_view_; // May be NULL. + int left_view_x_inset_; + SkColor active_frame_color_; + SkColor inactive_frame_color_; + FrameCaptionButtonContainerView* caption_button_container_; + + // The height of the header to paint. + int painted_height_; + + // Whether the header should be painted as active. + Mode mode_; + + // Whether the header is painted for the first time. + bool initial_paint_; + + scoped_ptr activation_animation_; + + DISALLOW_COPY_AND_ASSIGN(DefaultHeaderPainter); +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_DEFAULT_HEADER_PAINTER_H_ diff --git a/mash/wm/frame/frame_border_hit_test_controller.cc b/mash/wm/frame/frame_border_hit_test_controller.cc new file mode 100644 index 0000000..cd233f8 --- /dev/null +++ b/mash/wm/frame/frame_border_hit_test_controller.cc @@ -0,0 +1,82 @@ +// Copyright 2015 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 "mash/wm/frame/frame_border_hit_test_controller.h" + +#include "mash/wm/frame/caption_buttons/frame_caption_button_container_view.h" +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#include "ui/base/hit_test.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/window/non_client_view.h" + +namespace mash { +namespace wm { + +// In the window corners, the resize areas don't actually expand bigger, but the +// 16 px at the end of each edge triggers diagonal resizing. +const int kResizeAreaCornerSize = 16; + +// Windows do not have a traditional visible window frame. Window content +// extends to the edge of the window. We consider a small region outside the +// window bounds and an even smaller region overlapping the window to be the +// "non-client" area and use it for resizing. +const int kResizeOutsideBoundsSize = 6; +const int kResizeOutsideBoundsScaleForTouch = 5; +const int kResizeInsideBoundsSize = 1; + +// static +int FrameBorderHitTestController::NonClientHitTest( + views::NonClientFrameView* view, + FrameCaptionButtonContainerView* caption_button_container, + const gfx::Point& point_in_widget) { + gfx::Rect expanded_bounds = view->bounds(); + int outside_bounds = kResizeOutsideBoundsSize; + + if (aura::Env::GetInstance()->is_touch_down()) + outside_bounds *= kResizeOutsideBoundsScaleForTouch; + expanded_bounds.Inset(-outside_bounds, -outside_bounds); + + if (!expanded_bounds.Contains(point_in_widget)) + return HTNOWHERE; + + // Check the frame first, as we allow a small area overlapping the contents + // to be used for resize handles. + views::Widget* frame = view->GetWidget(); + bool can_ever_resize = frame->widget_delegate()->CanResize(); + // Don't allow overlapping resize handles when the window is maximized or + // fullscreen, as it can't be resized in those states. + int resize_border = kResizeInsideBoundsSize; + if (frame->IsMaximized() || frame->IsFullscreen()) { + resize_border = 0; + can_ever_resize = false; + } + int frame_component = view->GetHTComponentForFrame( + point_in_widget, resize_border, resize_border, kResizeAreaCornerSize, + kResizeAreaCornerSize, can_ever_resize); + if (frame_component != HTNOWHERE) + return frame_component; + + int client_component = + frame->client_view()->NonClientHitTest(point_in_widget); + if (client_component != HTNOWHERE) + return client_component; + + if (caption_button_container->visible()) { + gfx::Point point_in_caption_button_container(point_in_widget); + views::View::ConvertPointFromWidget(caption_button_container, + &point_in_caption_button_container); + int caption_button_component = caption_button_container->NonClientHitTest( + point_in_caption_button_container); + if (caption_button_component != HTNOWHERE) + return caption_button_component; + } + + // Caption is a safe default. + return HTCAPTION; +} + +} // namespace wm +} // namespace mash diff --git a/mash/wm/frame/frame_border_hit_test_controller.h b/mash/wm/frame/frame_border_hit_test_controller.h new file mode 100644 index 0000000..bcfbb77 --- /dev/null +++ b/mash/wm/frame/frame_border_hit_test_controller.h @@ -0,0 +1,40 @@ +// Copyright 2015 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 MASH_WM_FRAME_FRAME_BORDER_HITTEST_CONTROLLER_H_ +#define MASH_WM_FRAME_FRAME_BORDER_HITTEST_CONTROLLER_H_ + +#include "base/macros.h" + +namespace gfx { +class Point; +} + +namespace views { +class NonClientFrameView; +class Widget; +} + +namespace mash { +namespace wm { +class FrameCaptionButtonContainerView; + +// Class which manages the hittest override bounds for |frame|. +class FrameBorderHitTestController { + public: + // Does the non client hit test on behalf of |view|. |point_in_widget| must be + // in the coordinates of |view|'s widget. + static int NonClientHitTest( + views::NonClientFrameView* view, + FrameCaptionButtonContainerView* caption_button_container, + const gfx::Point& point_in_widget); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(FrameBorderHitTestController); +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_FRAME_BORDER_HITTEST_CONTROLLER_H_ diff --git a/mash/wm/frame/header_painter.h b/mash/wm/frame/header_painter.h new file mode 100644 index 0000000..84eef20 --- /dev/null +++ b/mash/wm/frame/header_painter.h @@ -0,0 +1,52 @@ +// Copyright 2015 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 MASH_WM_FRAME_HEADER_PAINTER_H_ +#define MASH_WM_FRAME_HEADER_PAINTER_H_ + +namespace gfx { +class Canvas; +} + +namespace mash { +namespace wm { + +// Helper class for painting the window header. +// TODO(sky): keep this only if we're going to actually need different +// subclasses. +class HeaderPainter { + public: + enum Mode { MODE_ACTIVE, MODE_INACTIVE }; + + virtual ~HeaderPainter() {} + + // Returns the header's minimum width. + virtual int GetMinimumHeaderWidth() const = 0; + + // Paints the header. + virtual void PaintHeader(gfx::Canvas* canvas, Mode mode) = 0; + + // Performs layout for the header. + virtual void LayoutHeader() = 0; + + // Get the height of the header. + virtual int GetHeaderHeight() const = 0; + + // Gets / sets how much of the header is painted. This allows the header to + // paint under things (like the tabstrip) which have transparent / + // non-painting sections. This height does not affect LayoutHeader(). + virtual int GetHeaderHeightForPainting() const = 0; + virtual void SetHeaderHeightForPainting(int height_for_painting) = 0; + + // Schedule a re-paint of the entire title. + virtual void SchedulePaintForTitle() = 0; + + // Updates the x inset of the leftmost view in the header. + virtual void UpdateLeftViewXInset(int left_view_x_inset) = 0; +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_HEADER_PAINTER_H_ diff --git a/mash/wm/frame/header_painter_util.cc b/mash/wm/frame/header_painter_util.cc new file mode 100644 index 0000000..a658d16 --- /dev/null +++ b/mash/wm/frame/header_painter_util.cc @@ -0,0 +1,80 @@ +// Copyright 2015 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 "mash/wm/frame/header_painter_util.h" + +#include + +#include "ui/gfx/font_list.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace { + +// Radius of the header's top corners when the window is restored. +const int kTopCornerRadiusWhenRestored = 2; + +// Distance between left edge of the window and the leftmost view. +const int kDefaultLeftViewXInset = 9; + +// Space between the title text and the caption buttons. +const int kTitleCaptionButtonSpacing = 5; + +// Space between window icon and title text. +const int kTitleIconOffsetX = 5; + +// Space between window edge and title text, when there is no icon. +const int kTitleNoIconOffsetX = 8; + +// In the pre-Ash era the web content area had a frame along the left edge, so +// user-generated theme images for the new tab page assume they are shifted +// right relative to the header. Now that we have removed the left edge frame +// we need to copy the theme image for the window header from a few pixels +// inset to preserve alignment with the NTP image, or else we'll break a bunch +// of existing themes. We do something similar on OS X for the same reason. +const int kThemeFrameImageInsetX = 5; + +} // namespace + +namespace mash { +namespace wm { + +// static +int HeaderPainterUtil::GetTopCornerRadiusWhenRestored() { + return kTopCornerRadiusWhenRestored; +} + +// static +int HeaderPainterUtil::GetDefaultLeftViewXInset() { + return kDefaultLeftViewXInset; +} + +// static +int HeaderPainterUtil::GetThemeBackgroundXInset() { + return kThemeFrameImageInsetX; +} + +// static +gfx::Rect HeaderPainterUtil::GetTitleBounds( + const views::View* left_view, + const views::View* right_view, + const gfx::FontList& title_font_list) { + int x = left_view ? left_view->bounds().right() + kTitleIconOffsetX + : kTitleNoIconOffsetX; + int height = title_font_list.GetHeight(); + // Floor when computing the center of |caption_button_container| and when + // computing the center of the text. + int y = std::max(0, (right_view->height() / 2) - (height / 2)); + int width = std::max(0, right_view->x() - kTitleCaptionButtonSpacing - x); + return gfx::Rect(x, y, width, height); +} + +// static +bool HeaderPainterUtil::CanAnimateActivation(views::Widget* widget) { + return true; +} + +} // namespace wm +} // namespace mash diff --git a/mash/wm/frame/header_painter_util.h b/mash/wm/frame/header_painter_util.h new file mode 100644 index 0000000..230c832 --- /dev/null +++ b/mash/wm/frame/header_painter_util.h @@ -0,0 +1,56 @@ +// Copyright 2015 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 MASH_WM_FRAME_HEADER_PAINTER_UTIL_H_ +#define MASH_WM_FRAME_HEADER_PAINTER_UTIL_H_ + +#include "base/macros.h" + +namespace gfx { +class FontList; +class Rect; +} +namespace views { +class View; +class Widget; +} + +namespace mash { +namespace wm { + +// Static-only helper class for functionality used accross multiple +// implementations of HeaderPainter. +class HeaderPainterUtil { + public: + // Returns the radius of the header's corners when the window is restored. + static int GetTopCornerRadiusWhenRestored(); + + // Returns the default distance between the left edge of the window and the + // leftmost view in the header. + static int GetDefaultLeftViewXInset(); + + // Returns the amount that the frame background is inset from the left edge of + // the window. + static int GetThemeBackgroundXInset(); + + // Returns the bounds for the header's title given the views to the left and + // right of the title, and the font used. + // |left_view| should be NULL if there is no view to the left of the title. + static gfx::Rect GetTitleBounds(const views::View* left_view, + const views::View* right_view, + const gfx::FontList& title_font_list); + + // Returns true if the header for |widget| can animate to new visuals when the + // widget's activation changes. Returns false if the header should switch to + // new visuals instantaneously. + static bool CanAnimateActivation(views::Widget* widget); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderPainterUtil); +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_HEADER_PAINTER_UTIL_H_ diff --git a/mash/wm/frame/move_loop.cc b/mash/wm/frame/move_loop.cc new file mode 100644 index 0000000..786ff76 --- /dev/null +++ b/mash/wm/frame/move_loop.cc @@ -0,0 +1,229 @@ +// Copyright 2015 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 "mash/wm/frame/move_loop.h" + +#include "base/auto_reset.h" +#include "components/mus/public/cpp/window.h" +#include "components/mus/public/interfaces/input_event_constants.mojom.h" +#include "mash/wm/property_util.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/rect.h" + +namespace mash { +namespace wm { + +namespace { + +gfx::Point EventLocationToPoint(const mus::mojom::Event& event) { + return gfx::ToFlooredPoint(gfx::PointF(event.pointer_data->location->x, + event.pointer_data->location->y)); +} + +gfx::Point EventScreenLocationToPoint(const mus::mojom::Event& event) { + return gfx::ToFlooredPoint( + gfx::PointF(event.pointer_data->location->screen_x, + event.pointer_data->location->screen_y)); +} + +mus::mojom::EventFlags MouseOnlyEventFlags(mus::mojom::EventFlags flags) { + return static_cast( + flags & (mus::mojom::EVENT_FLAGS_LEFT_MOUSE_BUTTON | + mus::mojom::EVENT_FLAGS_MIDDLE_MOUSE_BUTTON | + mus::mojom::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)); +} + +gfx::Rect ClientAreaBounds(const mus::Window* window) { + gfx::Rect client_area(window->bounds().size()); + client_area.Inset(window->client_area()); + return client_area; +} + +} // namespace + +MoveLoop::~MoveLoop() { + if (target_) + target_->RemoveObserver(this); +} + +// static +scoped_ptr MoveLoop::Create(mus::Window* target, + const mus::mojom::Event& event) { + DCHECK_EQ(event.action, mus::mojom::EVENT_TYPE_POINTER_DOWN); + const gfx::Point location(EventLocationToPoint(event)); + if (!gfx::Rect(target->bounds().size()).Contains(location) || + ClientAreaBounds(target).Contains(location)) { + return nullptr; + } + + // Start a move on left mouse, or any other type of pointer. + if (event.pointer_data->kind == mus::mojom::POINTER_KIND_MOUSE && + MouseOnlyEventFlags(event.flags) != + mus::mojom::EVENT_FLAGS_LEFT_MOUSE_BUTTON) { + return nullptr; + } + + Type type; + HorizontalLocation h_loc; + VerticalLocation v_loc; + DetermineType(target, location, &type, &h_loc, &v_loc); + + return make_scoped_ptr(new MoveLoop(target, event, type, h_loc, v_loc)); +} + +MoveLoop::MoveResult MoveLoop::Move(const mus::mojom::Event& event) { + switch (event.action) { + case mus::mojom::EVENT_TYPE_POINTER_CANCEL: + if (event.pointer_data->pointer_id == pointer_id_) { + if (target_) + Revert(); + return MoveResult::DONE; + } + return MoveResult::CONTINUE; + + case mus::mojom::EVENT_TYPE_POINTER_MOVE: + if (target_ && event.pointer_data->pointer_id == pointer_id_) + MoveImpl(event); + return MoveResult::CONTINUE; + + case mus::mojom::EVENT_TYPE_POINTER_UP: + if (event.pointer_data->pointer_id == pointer_id_) { + // TODO(sky): need to support changed_flags. + if (target_) + MoveImpl(event); + return MoveResult::DONE; + } + return MoveResult::CONTINUE; + + default: + break; + } + + return MoveResult::CONTINUE; +} + +MoveLoop::MoveLoop(mus::Window* target, + const mus::mojom::Event& event, + Type type, + HorizontalLocation h_loc, + VerticalLocation v_loc) + : target_(target), + type_(type), + h_loc_(h_loc), + v_loc_(v_loc), + pointer_id_(event.pointer_data->pointer_id), + initial_event_screen_location_(EventScreenLocationToPoint(event)), + initial_window_bounds_(target->bounds()), + initial_user_set_bounds_(GetWindowUserSetBounds(target)), + changing_bounds_(false) { + target->AddObserver(this); +} + +// static +void MoveLoop::DetermineType(mus::Window* target, + const gfx::Point& location, + Type* type, + HorizontalLocation* h_loc, + VerticalLocation* v_loc) { + *h_loc = HorizontalLocation::OTHER; + *v_loc = VerticalLocation::OTHER; + const int resize_size = static_cast( + kResizeSize * + std::max(1.f, target->viewport_metrics().device_pixel_ratio)); + + const gfx::Rect client_area(ClientAreaBounds(target)); + if (location.x() < client_area.x()) + *h_loc = HorizontalLocation::LEFT; + else if (location.x() >= client_area.right()) + *h_loc = HorizontalLocation::RIGHT; + else + *h_loc = HorizontalLocation::OTHER; + + if (location.y() < resize_size) + *v_loc = VerticalLocation::TOP; + else if (location.y() >= client_area.bottom()) + *v_loc = VerticalLocation::BOTTOM; + else + *v_loc = VerticalLocation::OTHER; + + if (*v_loc == VerticalLocation::OTHER && location.y() >= resize_size && + *h_loc == HorizontalLocation::OTHER) { + *type = Type::MOVE; + return; + } + *type = Type::RESIZE; + DCHECK(*h_loc != HorizontalLocation::OTHER || + *v_loc != VerticalLocation::OTHER); +} + +void MoveLoop::MoveImpl(const mus::mojom::Event& event) { + const gfx::Vector2d delta = + EventScreenLocationToPoint(event) - initial_event_screen_location_; + const gfx::Rect new_bounds(DetermineBoundsFromDelta(delta)); + base::AutoReset resetter(&changing_bounds_, true); + target_->SetBounds(new_bounds); + SetWindowUserSetBounds(target_, new_bounds); +} + +void MoveLoop::Cancel() { + target_->RemoveObserver(this); + target_ = nullptr; +} + +void MoveLoop::Revert() { + base::AutoReset resetter(&changing_bounds_, true); + target_->SetBounds(initial_window_bounds_); + SetWindowUserSetBounds(target_, initial_user_set_bounds_); +} + +gfx::Rect MoveLoop::DetermineBoundsFromDelta(const gfx::Vector2d& delta) { + if (type_ == Type::MOVE) { + return gfx::Rect(initial_window_bounds_.origin() + delta, + initial_window_bounds_.size()); + } + + // TODO(sky): support better min sizes, make sure doesn't get bigger than + // screen and max. Also make sure keep some portion on screen. + gfx::Rect bounds(initial_window_bounds_); + if (h_loc_ == HorizontalLocation::LEFT) { + const int x = std::min(bounds.right() - 1, bounds.x() + delta.x()); + const int width = bounds.right() - x; + bounds.set_x(x); + bounds.set_width(width); + } else if (h_loc_ == HorizontalLocation::RIGHT) { + bounds.set_width(std::max(1, bounds.width() + delta.x())); + } + + if (v_loc_ == VerticalLocation::TOP) { + const int y = std::min(bounds.bottom() - 1, bounds.y() + delta.y()); + const int height = bounds.bottom() - y; + bounds.set_y(y); + bounds.set_height(height); + } else if (v_loc_ == VerticalLocation::BOTTOM) { + bounds.set_height(std::max(1, bounds.height() + delta.y())); + } + + return bounds; +} + +void MoveLoop::OnTreeChanged(const TreeChangeParams& params) { + if (params.target == target_) + Cancel(); +} + +void MoveLoop::OnWindowBoundsChanged(mus::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + DCHECK_EQ(window, target_); + if (!changing_bounds_) + Cancel(); +} + +void MoveLoop::OnWindowVisibilityChanged(mus::Window* window) { + DCHECK_EQ(window, target_); + Cancel(); +} + +} // namespace wm +} // namespace mash diff --git a/mash/wm/frame/move_loop.h b/mash/wm/frame/move_loop.h new file mode 100644 index 0000000..12ca46e --- /dev/null +++ b/mash/wm/frame/move_loop.h @@ -0,0 +1,121 @@ +// Copyright 2015 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 MASH_WM_FRAME_MOVE_LOOP_H_ +#define MASH_WM_FRAME_MOVE_LOOP_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "components/mus/public/cpp/window_observer.h" +#include "components/mus/public/interfaces/input_events.mojom.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" + +namespace mash { +namespace wm { + +// MoveLoop is responsible for moving/resizing windows. +class MoveLoop : public mus::WindowObserver { + public: + enum MoveResult { + // The move is still ongoing. + CONTINUE, + + // The move is done and the MoveLoop should be destroyed. + DONE, + }; + + enum class HorizontalLocation { + LEFT, + RIGHT, + OTHER, + }; + + enum class VerticalLocation { + TOP, + BOTTOM, + OTHER, + }; + + // Number of pixels in which a resize is triggered. + static const int kResizeSize = 8; + + ~MoveLoop() override; + + // If a move/resize loop should occur for the specified parameters creates + // and returns a new MoveLoop. All events should be funneled to the MoveLoop + // until done (Move()). + static scoped_ptr Create(mus::Window* target, + const mus::mojom::Event& event); + + // Processes an event for a move/resize loop. + MoveResult Move(const mus::mojom::Event& event); + + private: + enum class Type { + MOVE, + RESIZE, + }; + + MoveLoop(mus::Window* target, + const mus::mojom::Event& event, + Type type, + HorizontalLocation h_loc, + VerticalLocation v_loc); + + static void DetermineType(mus::Window* target, + const gfx::Point& location, + Type* type, + HorizontalLocation* h_loc, + VerticalLocation* v_loc); + + // Does the actual move/resize. + void MoveImpl(const mus::mojom::Event& event); + + // Cancels the loop. This sets |target_| to null and removes the observer. + // After this the MoveLoop is still ongoing and won't stop until the + // appropriate event is received. + void Cancel(); + + void Revert(); + + gfx::Rect DetermineBoundsFromDelta(const gfx::Vector2d& delta); + + // mus::WindowObserver: + void OnTreeChanged(const TreeChangeParams& params) override; + void OnWindowBoundsChanged(mus::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) override; + void OnWindowVisibilityChanged(mus::Window* window) override; + + // The window this MoveLoop is acting on. |target_| is set to null if the + // window unexpectedly changes while the move is in progress. + mus::Window* target_; + + const Type type_; + const HorizontalLocation h_loc_; + const VerticalLocation v_loc_; + + // The id of the pointer that triggered the move. + const int32_t pointer_id_; + + // Location of the event (in screen coordinates) that triggered the move. + const gfx::Point initial_event_screen_location_; + + // Original bounds of the window. + const gfx::Rect initial_window_bounds_; + + const gfx::Rect initial_user_set_bounds_; + + // Set to true when MoveLoop changes the bounds of |target_|. The move is + // canceled if the bounds change unexpectedly during the move. + bool changing_bounds_; + + DISALLOW_COPY_AND_ASSIGN(MoveLoop); +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_MOVE_LOOP_H_ diff --git a/mash/wm/frame/move_loop_unittest.cc b/mash/wm/frame/move_loop_unittest.cc new file mode 100644 index 0000000..86ca32c --- /dev/null +++ b/mash/wm/frame/move_loop_unittest.cc @@ -0,0 +1,185 @@ +// Copyright 2015 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 "mash/wm/frame/move_loop.h" + +#include "components/mus/public/cpp/tests/test_window.h" +#include "mojo/converters/input_events/input_events_type_converters.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" + +using MoveLoopTest = testing::Test; + +namespace mash { +namespace wm { + +namespace { + +// Sets the client area for |window|. Padding is provided on the top so that +// there is a movable region. All other edges are sized to +// |MoveLoop::kResizeSize|. +void SetClientArea(mus::Window* window) { + if (window->bounds().IsEmpty()) + window->SetBounds(gfx::Rect(100, 200, 300, 400)); + + window->SetClientArea( + gfx::Insets(MoveLoop::kResizeSize + 10, MoveLoop::kResizeSize, + MoveLoop::kResizeSize, MoveLoop::kResizeSize)); +} + +mus::mojom::EventPtr CreatePointerDownEvent(const gfx::Point& location) { + const ui::TouchEvent event(ui::ET_TOUCH_PRESSED, location, 1, + base::TimeDelta()); + return mus::mojom::Event::From(static_cast(event)); +} + +mus::mojom::EventPtr CreatePointerMove(const gfx::Point& location) { + const ui::TouchEvent event(ui::ET_TOUCH_MOVED, location, 1, + base::TimeDelta()); + return mus::mojom::Event::From(static_cast(event)); +} + +enum class HorizontalLocation { + LEFT, + MIDDLE, + RIGHT, +}; + +enum class VerticalLocation { + TOP_MOVE, + TOP_RESIZE, + MIDDLE, + BOTTOM, +}; + +int HorizontalLocationToPoint(const mus::Window* window, + HorizontalLocation loc) { + if (loc == HorizontalLocation::LEFT) + return 0; + return loc == HorizontalLocation::MIDDLE ? window->bounds().width() / 2 + : window->bounds().width() - 1; +} + +int VerticalLocationToPoint(const mus::Window* window, VerticalLocation loc) { + switch (loc) { + case VerticalLocation::TOP_MOVE: + return MoveLoop::kResizeSize + 1; + case VerticalLocation::TOP_RESIZE: + return 0; + case VerticalLocation::MIDDLE: + return window->bounds().height() / 2; + case VerticalLocation::BOTTOM: + return window->bounds().height() - 1; + } + return 0; +} + +gfx::Point LocationToPoint(const mus::Window* window, + HorizontalLocation h_loc, + VerticalLocation v_loc) { + return gfx::Point(HorizontalLocationToPoint(window, h_loc), + VerticalLocationToPoint(window, v_loc)); +} + +} // namespace + +TEST_F(MoveLoopTest, Move) { + struct TestData { + HorizontalLocation initial_h_loc; + VerticalLocation initial_v_loc; + // Number of values in delta_* that are valid. For example, if this is 1 + // then only one move is generated with thye point |delta_x[0]|, + // |delta_y[0]|. + int delta_count; + int delta_x[3]; + int delta_y[3]; + gfx::Rect expected_bounds; + } data[] = { + // Move. + {HorizontalLocation::MIDDLE, + VerticalLocation::TOP_MOVE, + 1, + {1, 0, 0}, + {2, 0, 0}, + gfx::Rect(101, 202, 300, 400)}, + + // Resize from top, left. + {HorizontalLocation::LEFT, + VerticalLocation::TOP_RESIZE, + 1, + {1, 0, 0}, + {2, 0, 0}, + gfx::Rect(101, 202, 299, 398)}, + + // Resize from left. + {HorizontalLocation::LEFT, + VerticalLocation::MIDDLE, + 1, + {1, 0, 0}, + {2, 0, 0}, + gfx::Rect(101, 200, 299, 400)}, + + // Resize from bottom, left. + {HorizontalLocation::LEFT, + VerticalLocation::BOTTOM, + 1, + {-1, 0, 0}, + {-2, 0, 0}, + gfx::Rect(99, 200, 301, 398)}, + + // Resize from bottom. + {HorizontalLocation::MIDDLE, + VerticalLocation::BOTTOM, + 1, + {-1, 0, 0}, + {4, 0, 0}, + gfx::Rect(100, 200, 300, 404)}, + + // Resize from bottom, right. + {HorizontalLocation::RIGHT, + VerticalLocation::BOTTOM, + 1, + {-3, 0, 0}, + {4, 0, 0}, + gfx::Rect(100, 200, 297, 404)}, + + // Resize from right. + {HorizontalLocation::RIGHT, + VerticalLocation::MIDDLE, + 1, + {6, 0, 0}, + {-4, 0, 0}, + gfx::Rect(100, 200, 306, 400)}, + + // Resize from top, right. + {HorizontalLocation::RIGHT, + VerticalLocation::TOP_RESIZE, + 1, + {6, 0, 0}, + {5, 0, 0}, + gfx::Rect(100, 205, 306, 395)}, + }; + + for (size_t i = 0; i < arraysize(data); ++i) { + mus::TestWindow window; + SetClientArea(&window); + gfx::Point pointer_location( + LocationToPoint(&window, data[i].initial_h_loc, data[i].initial_v_loc)); + scoped_ptr move_loop = + MoveLoop::Create(&window, *CreatePointerDownEvent(pointer_location)); + ASSERT_TRUE(move_loop.get()) << i; + for (int j = 0; j < data[i].delta_count; ++j) { + pointer_location.Offset(data[i].delta_x[j], data[i].delta_y[j]); + ASSERT_EQ(MoveLoop::MoveResult::CONTINUE, + move_loop->Move(*CreatePointerMove(pointer_location))) + << i << " " << j; + } + ASSERT_EQ(data[i].expected_bounds, window.bounds()); + } +} + +} // namespace wm +} // namespace mash diff --git a/mash/wm/frame/non_client_frame_view_mash.cc b/mash/wm/frame/non_client_frame_view_mash.cc new file mode 100644 index 0000000..f3bee2d --- /dev/null +++ b/mash/wm/frame/non_client_frame_view_mash.cc @@ -0,0 +1,351 @@ +// Copyright 2015 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 "mash/wm/frame/non_client_frame_view_mash.h" + +#include +#include + +#include "components/mus/public/cpp/window.h" +#include "grit/mash_wm_resources.h" +#include "mash/wm/frame/caption_buttons/frame_caption_button_container_view.h" +#include "mash/wm/frame/default_header_painter.h" +#include "mash/wm/frame/frame_border_hit_test_controller.h" +#include "mash/wm/frame/header_painter.h" +#include "mash/wm/frame/move_loop.h" +#include "mojo/converters/input_events/input_events_type_converters.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/compositor/paint_recorder.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace mash { +namespace wm { + +/////////////////////////////////////////////////////////////////////////////// +// NonClientFrameViewMash::HeaderView + +// View which paints the header. +class NonClientFrameViewMash::HeaderView : public views::View { + public: + // |frame| is the widget that the caption buttons act on. + explicit HeaderView(views::Widget* frame); + ~HeaderView() override; + + // Schedules a repaint for the entire title. + void SchedulePaintForTitle(); + + // Tells the window controls to reset themselves to the normal state. + void ResetWindowControls(); + + // Returns the view's preferred height. + int GetPreferredHeight() const; + + // Returns the view's minimum width. + int GetMinimumWidth() const; + + void SizeConstraintsChanged(); + + void SetFrameColors(SkColor active_frame_color, SkColor inactive_frame_color); + + // views::View: + void Layout() override; + void OnPaint(gfx::Canvas* canvas) override; + void ChildPreferredSizeChanged(views::View* child) override; + + FrameCaptionButtonContainerView* caption_button_container() { + return caption_button_container_; + } + + private: + // The widget that the caption buttons act on. + views::Widget* frame_; + + // Helper for painting the header. + scoped_ptr header_painter_; + + // View which contains the window caption buttons. + FrameCaptionButtonContainerView* caption_button_container_; + + DISALLOW_COPY_AND_ASSIGN(HeaderView); +}; + +NonClientFrameViewMash::HeaderView::HeaderView(views::Widget* frame) + : frame_(frame), + header_painter_(new DefaultHeaderPainter), + caption_button_container_(nullptr) { + caption_button_container_ = new FrameCaptionButtonContainerView(frame_); + caption_button_container_->UpdateSizeButtonVisibility(); + AddChildView(caption_button_container_); + + header_painter_->Init(frame_, this, caption_button_container_); +} + +NonClientFrameViewMash::HeaderView::~HeaderView() {} + +void NonClientFrameViewMash::HeaderView::SchedulePaintForTitle() { + header_painter_->SchedulePaintForTitle(); +} + +void NonClientFrameViewMash::HeaderView::ResetWindowControls() { + caption_button_container_->ResetWindowControls(); +} + +int NonClientFrameViewMash::HeaderView::GetPreferredHeight() const { + return header_painter_->GetHeaderHeightForPainting(); +} + +int NonClientFrameViewMash::HeaderView::GetMinimumWidth() const { + return header_painter_->GetMinimumHeaderWidth(); +} + +void NonClientFrameViewMash::HeaderView::SizeConstraintsChanged() { + caption_button_container_->ResetWindowControls(); + caption_button_container_->UpdateSizeButtonVisibility(); + Layout(); +} + +void NonClientFrameViewMash::HeaderView::SetFrameColors( + SkColor active_frame_color, + SkColor inactive_frame_color) { + header_painter_->SetFrameColors(active_frame_color, inactive_frame_color); +} + +/////////////////////////////////////////////////////////////////////////////// +// NonClientFrameViewMash::HeaderView, views::View overrides: + +void NonClientFrameViewMash::HeaderView::Layout() { + header_painter_->LayoutHeader(); +} + +void NonClientFrameViewMash::HeaderView::OnPaint(gfx::Canvas* canvas) { + bool paint_as_active = + frame_->non_client_view()->frame_view()->ShouldPaintAsActive(); + caption_button_container_->SetPaintAsActive(paint_as_active); + + HeaderPainter::Mode header_mode = paint_as_active + ? HeaderPainter::MODE_ACTIVE + : HeaderPainter::MODE_INACTIVE; + header_painter_->PaintHeader(canvas, header_mode); +} + +void NonClientFrameViewMash::HeaderView::ChildPreferredSizeChanged( + views::View* child) { + // FrameCaptionButtonContainerView animates the visibility changes in + // UpdateSizeButtonVisibility(false). Due to this a new size is not available + // until the completion of the animation. Layout in response to the preferred + // size changes. + if (child != caption_button_container_) + return; + parent()->Layout(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameViewMash, public: + +// static +const char NonClientFrameViewMash::kViewClassName[] = "NonClientFrameViewMash"; + +NonClientFrameViewMash::NonClientFrameViewMash(views::Widget* frame, + mus::Window* window) + : frame_(frame), window_(window), header_view_(new HeaderView(frame)) { + // |header_view_| is set as the non client view's overlay view so that it can + // overlay the web contents in immersive fullscreen. + AddChildView(header_view_); + window_->AddObserver(this); +} + +NonClientFrameViewMash::~NonClientFrameViewMash() { + if (window_) + window_->RemoveObserver(this); +} + +// static +gfx::Insets NonClientFrameViewMash::GetPreferredClientAreaInsets() { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + const int header_height = + rb.GetImageSkiaNamed(IDR_MASH_WM_WINDOW_CONTROL_BACKGROUND_P) + ->size() + .height(); + return gfx::Insets(header_height, 0, 0, 0); +} + +void NonClientFrameViewMash::SetFrameColors(SkColor active_frame_color, + SkColor inactive_frame_color) { + header_view_->SetFrameColors(active_frame_color, inactive_frame_color); +} + +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameViewMash, views::NonClientFrameView overrides: + +gfx::Rect NonClientFrameViewMash::GetBoundsForClientView() const { + gfx::Rect result(GetLocalBounds()); + result.Inset(window_->client_area()); + return result; +} + +gfx::Rect NonClientFrameViewMash::GetWindowBoundsForClientBounds( + const gfx::Rect& client_bounds) const { + gfx::Rect window_bounds = client_bounds; + window_bounds.Inset( + window_->client_area().left(), window_->client_area().top(), + window_->client_area().right(), window_->client_area().bottom()); + return window_bounds; +} + +int NonClientFrameViewMash::NonClientHitTest(const gfx::Point& point) { + return FrameBorderHitTestController::NonClientHitTest( + this, header_view_->caption_button_container(), point); +} + +void NonClientFrameViewMash::GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) {} + +void NonClientFrameViewMash::ResetWindowControls() { + header_view_->ResetWindowControls(); +} + +void NonClientFrameViewMash::UpdateWindowIcon() {} + +void NonClientFrameViewMash::UpdateWindowTitle() { + header_view_->SchedulePaintForTitle(); +} + +void NonClientFrameViewMash::SizeConstraintsChanged() { + header_view_->SizeConstraintsChanged(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameViewMash, views::View overrides: + +void NonClientFrameViewMash::Layout() { + header_view_->SetBounds(0, 0, width(), header_view_->GetPreferredHeight()); + header_view_->Layout(); +} + +gfx::Size NonClientFrameViewMash::GetPreferredSize() const { + gfx::Size pref = frame_->client_view()->GetPreferredSize(); + return frame_->non_client_view() + ->GetWindowBoundsForClientBounds(gfx::Rect(pref)) + .size(); +} + +const char* NonClientFrameViewMash::GetClassName() const { + return kViewClassName; +} + +gfx::Size NonClientFrameViewMash::GetMinimumSize() const { + gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize()); + return gfx::Size( + std::max(header_view_->GetMinimumWidth(), min_client_view_size.width()), + NonClientTopBorderHeight() + min_client_view_size.height()); +} + +gfx::Size NonClientFrameViewMash::GetMaximumSize() const { + gfx::Size max_client_size(frame_->client_view()->GetMaximumSize()); + int width = 0; + int height = 0; + + if (max_client_size.width() > 0) + width = std::max(header_view_->GetMinimumWidth(), max_client_size.width()); + if (max_client_size.height() > 0) + height = NonClientTopBorderHeight() + max_client_size.height(); + + return gfx::Size(width, height); +} + +void NonClientFrameViewMash::OnPaint(gfx::Canvas* canvas) { + canvas->Save(); + NonClientFrameView::OnPaint(canvas); + canvas->Restore(); + + // The client app draws the client area. Make ours totally transparent so + // we only see the client apps client area. + canvas->FillRect(GetBoundsForClientView(), SK_ColorBLACK, + SkXfermode::kSrc_Mode); +} + +void NonClientFrameViewMash::PaintChildren(const ui::PaintContext& context) { + NonClientFrameView::PaintChildren(context); + + // The client app draws the client area. Make ours totally transparent so + // we only see the client apps client area. + ui::PaintRecorder recorder(context, size(), &paint_cache_); + recorder.canvas()->FillRect(GetBoundsForClientView(), SK_ColorBLACK, + SkXfermode::kSrc_Mode); +} + +bool NonClientFrameViewMash::OnMousePressed(const ui::MouseEvent& event) { + return StartMoveLoopIfNecessary(event); +} + +bool NonClientFrameViewMash::OnMouseDragged(const ui::MouseEvent& event) { + ContinueMove(event); + return move_loop_.get() != nullptr; +} + +void NonClientFrameViewMash::OnMouseReleased(const ui::MouseEvent& event) { + ContinueMove(event); +} + +void NonClientFrameViewMash::OnMouseCaptureLost() { + StopMove(); +} + +void NonClientFrameViewMash::OnWindowClientAreaChanged( + mus::Window* window, + const gfx::Insets& old_client_area) { + Layout(); + // NonClientView (our parent) positions the client view based on bounds from + // us. We need to layout from parent to trigger a layout of the client view. + if (parent()) + parent()->Layout(); + SchedulePaint(); +} + +void NonClientFrameViewMash::OnWindowDestroyed(mus::Window* window) { + window_->RemoveObserver(this); + window_ = nullptr; +} + +bool NonClientFrameViewMash::StartMoveLoopIfNecessary(const ui::Event& event) { + if (move_loop_) + return false; + // TODO(sky): convert MoveLoop to take ui::Event. + // TODO(sky): pass in hit test result. + move_loop_ = MoveLoop::Create(window_, *mus::mojom::Event::From(event)); + return true; +} + +void NonClientFrameViewMash::ContinueMove(const ui::Event& event) { + // TODO(sky): convert MoveLoop to take ui::Event. + if (move_loop_ && + move_loop_->Move(*mus::mojom::Event::From(event)) == MoveLoop::DONE) { + move_loop_.reset(); + } +} + +void NonClientFrameViewMash::StopMove() { + move_loop_.reset(); +} + +views::View* NonClientFrameViewMash::GetHeaderView() { + return header_view_; +} + +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameViewMash, private: + +int NonClientFrameViewMash::NonClientTopBorderHeight() const { + return header_view_->GetPreferredHeight(); +} + +} // namespace wm +} // namespace mash diff --git a/mash/wm/frame/non_client_frame_view_mash.h b/mash/wm/frame/non_client_frame_view_mash.h new file mode 100644 index 0000000..31b6880 --- /dev/null +++ b/mash/wm/frame/non_client_frame_view_mash.h @@ -0,0 +1,107 @@ +// Copyright 2015 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 MASH_WM_FRAME_NON_CLIENT_FRAME_VIEW_MASH_H_ +#define MASH_WM_FRAME_NON_CLIENT_FRAME_VIEW_MASH_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "components/mus/public/cpp/window_observer.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/compositor/paint_cache.h" +#include "ui/views/window/non_client_view.h" + +namespace gfx { +class Insets; +} + +namespace mus { +class Window; +} + +namespace views { +class Widget; +} + +namespace mash { +namespace wm { + +class FrameCaptionButtonContainerView; +class MoveLoop; + +class NonClientFrameViewMash : public views::NonClientFrameView, + public mus::WindowObserver { + public: + // Internal class name. + static const char kViewClassName[]; + + NonClientFrameViewMash(views::Widget* frame, mus::Window* window); + ~NonClientFrameViewMash() override; + + static gfx::Insets GetPreferredClientAreaInsets(); + + // Sets the active and inactive frame colors. Note the inactive frame color + // will have some transparency added when the frame is drawn. + void SetFrameColors(SkColor active_frame_color, SkColor inactive_frame_color); + + // views::NonClientFrameView: + gfx::Rect GetBoundsForClientView() const override; + gfx::Rect GetWindowBoundsForClientBounds( + const gfx::Rect& client_bounds) const override; + int NonClientHitTest(const gfx::Point& point) override; + void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override; + void ResetWindowControls() override; + void UpdateWindowIcon() override; + void UpdateWindowTitle() override; + void SizeConstraintsChanged() override; + + // views::View: + void Layout() override; + gfx::Size GetPreferredSize() const override; + const char* GetClassName() const override; + gfx::Size GetMinimumSize() const override; + gfx::Size GetMaximumSize() const override; + void OnPaint(gfx::Canvas* canvas) override; + void PaintChildren(const ui::PaintContext& context) override; + bool OnMousePressed(const ui::MouseEvent& event) override; + bool OnMouseDragged(const ui::MouseEvent& event) override; + void OnMouseReleased(const ui::MouseEvent& event) override; + void OnMouseCaptureLost() override; + + // mus::WindowObserver: + void OnWindowClientAreaChanged(mus::Window* window, + const gfx::Insets& old_client_area) override; + void OnWindowDestroyed(mus::Window* window) override; + + // Get the view of the header. + views::View* GetHeaderView(); + + private: + class OverlayView; + + // Height from top of window to top of client area. + int NonClientTopBorderHeight() const; + + bool StartMoveLoopIfNecessary(const ui::Event& event); + void ContinueMove(const ui::Event& event); + void StopMove(); + + // Not owned. + views::Widget* frame_; + + mus::Window* window_; + ui::PaintCache paint_cache_; + scoped_ptr move_loop_; + + // View which contains the title and window controls. + class HeaderView; + HeaderView* header_view_; + + DISALLOW_COPY_AND_ASSIGN(NonClientFrameViewMash); +}; + +} // namespace wm +} // namespace mash + +#endif // MASH_WM_FRAME_NON_CLIENT_FRAME_VIEW_MASH_H_ diff --git a/mash/wm/move_loop.cc b/mash/wm/move_loop.cc deleted file mode 100644 index 6c3ee7d..0000000 --- a/mash/wm/move_loop.cc +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2015 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 "mash/wm/move_loop.h" - -#include "base/auto_reset.h" -#include "components/mus/public/cpp/window.h" -#include "components/mus/public/interfaces/input_event_constants.mojom.h" -#include "mash/wm/property_util.h" -#include "ui/gfx/geometry/point_conversions.h" -#include "ui/gfx/geometry/rect.h" - -namespace { - -gfx::Point EventLocationToPoint(const mus::mojom::Event& event) { - return gfx::ToFlooredPoint(gfx::PointF(event.pointer_data->location->x, - event.pointer_data->location->y)); -} - -gfx::Point EventScreenLocationToPoint(const mus::mojom::Event& event) { - return gfx::ToFlooredPoint( - gfx::PointF(event.pointer_data->location->screen_x, - event.pointer_data->location->screen_y)); -} - -mus::mojom::EventFlags MouseOnlyEventFlags(mus::mojom::EventFlags flags) { - return static_cast( - flags & (mus::mojom::EVENT_FLAGS_LEFT_MOUSE_BUTTON | - mus::mojom::EVENT_FLAGS_MIDDLE_MOUSE_BUTTON | - mus::mojom::EVENT_FLAGS_RIGHT_MOUSE_BUTTON)); -} - -gfx::Rect ClientAreaBounds(const mus::Window* window) { - gfx::Rect client_area(window->bounds().size()); - client_area.Inset(window->client_area()); - return client_area; -} - -} // namespace - -MoveLoop::~MoveLoop() { - if (target_) - target_->RemoveObserver(this); -} - -// static -scoped_ptr MoveLoop::Create(mus::Window* target, - const mus::mojom::Event& event) { - DCHECK_EQ(event.action, mus::mojom::EVENT_TYPE_POINTER_DOWN); - const gfx::Point location(EventLocationToPoint(event)); - if (!gfx::Rect(target->bounds().size()).Contains(location) || - ClientAreaBounds(target).Contains(location)) { - return nullptr; - } - - // Start a move on left mouse, or any other type of pointer. - if (event.pointer_data->kind == mus::mojom::POINTER_KIND_MOUSE && - MouseOnlyEventFlags(event.flags) != - mus::mojom::EVENT_FLAGS_LEFT_MOUSE_BUTTON) { - return nullptr; - } - - Type type; - HorizontalLocation h_loc; - VerticalLocation v_loc; - DetermineType(target, location, &type, &h_loc, &v_loc); - - return make_scoped_ptr(new MoveLoop(target, event, type, h_loc, v_loc)); -} - -MoveLoop::MoveResult MoveLoop::Move(const mus::mojom::Event& event) { - switch (event.action) { - case mus::mojom::EVENT_TYPE_POINTER_CANCEL: - if (event.pointer_data->pointer_id == pointer_id_) { - if (target_) - Revert(); - return MoveResult::DONE; - } - return MoveResult::CONTINUE; - - case mus::mojom::EVENT_TYPE_POINTER_MOVE: - if (target_ && event.pointer_data->pointer_id == pointer_id_) - MoveImpl(event); - return MoveResult::CONTINUE; - - case mus::mojom::EVENT_TYPE_POINTER_UP: - if (event.pointer_data->pointer_id == pointer_id_) { - // TODO(sky): need to support changed_flags. - if (target_) - MoveImpl(event); - return MoveResult::DONE; - } - return MoveResult::CONTINUE; - - default: - break; - } - - return MoveResult::CONTINUE; -} - -MoveLoop::MoveLoop(mus::Window* target, - const mus::mojom::Event& event, - Type type, - HorizontalLocation h_loc, - VerticalLocation v_loc) - : target_(target), - type_(type), - h_loc_(h_loc), - v_loc_(v_loc), - pointer_id_(event.pointer_data->pointer_id), - initial_event_screen_location_(EventScreenLocationToPoint(event)), - initial_window_bounds_(target->bounds()), - initial_user_set_bounds_(GetWindowUserSetBounds(target)), - changing_bounds_(false) { - target->AddObserver(this); -} - -// static -void MoveLoop::DetermineType(mus::Window* target, - const gfx::Point& location, - Type* type, - HorizontalLocation* h_loc, - VerticalLocation* v_loc) { - *h_loc = HorizontalLocation::OTHER; - *v_loc = VerticalLocation::OTHER; - const int resize_size = static_cast( - kResizeSize * - std::max(1.f, target->viewport_metrics().device_pixel_ratio)); - - const gfx::Rect client_area(ClientAreaBounds(target)); - if (location.x() < client_area.x()) - *h_loc = HorizontalLocation::LEFT; - else if (location.x() >= client_area.right()) - *h_loc = HorizontalLocation::RIGHT; - else - *h_loc = HorizontalLocation::OTHER; - - if (location.y() < resize_size) - *v_loc = VerticalLocation::TOP; - else if (location.y() >= client_area.bottom()) - *v_loc = VerticalLocation::BOTTOM; - else - *v_loc = VerticalLocation::OTHER; - - if (*v_loc == VerticalLocation::OTHER && location.y() >= resize_size && - *h_loc == HorizontalLocation::OTHER) { - *type = Type::MOVE; - return; - } - *type = Type::RESIZE; - DCHECK(*h_loc != HorizontalLocation::OTHER || - *v_loc != VerticalLocation::OTHER); -} - -void MoveLoop::MoveImpl(const mus::mojom::Event& event) { - const gfx::Vector2d delta = - EventScreenLocationToPoint(event) - initial_event_screen_location_; - const gfx::Rect new_bounds(DetermineBoundsFromDelta(delta)); - base::AutoReset resetter(&changing_bounds_, true); - target_->SetBounds(new_bounds); - SetWindowUserSetBounds(target_, new_bounds); -} - -void MoveLoop::Cancel() { - target_->RemoveObserver(this); - target_ = nullptr; -} - -void MoveLoop::Revert() { - base::AutoReset resetter(&changing_bounds_, true); - target_->SetBounds(initial_window_bounds_); - SetWindowUserSetBounds(target_, initial_user_set_bounds_); -} - -gfx::Rect MoveLoop::DetermineBoundsFromDelta(const gfx::Vector2d& delta) { - if (type_ == Type::MOVE) { - return gfx::Rect(initial_window_bounds_.origin() + delta, - initial_window_bounds_.size()); - } - - // TODO(sky): support better min sizes, make sure doesn't get bigger than - // screen and max. Also make sure keep some portion on screen. - gfx::Rect bounds(initial_window_bounds_); - if (h_loc_ == HorizontalLocation::LEFT) { - const int x = std::min(bounds.right() - 1, bounds.x() + delta.x()); - const int width = bounds.right() - x; - bounds.set_x(x); - bounds.set_width(width); - } else if (h_loc_ == HorizontalLocation::RIGHT) { - bounds.set_width(std::max(1, bounds.width() + delta.x())); - } - - if (v_loc_ == VerticalLocation::TOP) { - const int y = std::min(bounds.bottom() - 1, bounds.y() + delta.y()); - const int height = bounds.bottom() - y; - bounds.set_y(y); - bounds.set_height(height); - } else if (v_loc_ == VerticalLocation::BOTTOM) { - bounds.set_height(std::max(1, bounds.height() + delta.y())); - } - - return bounds; -} - -void MoveLoop::OnTreeChanged(const TreeChangeParams& params) { - if (params.target == target_) - Cancel(); -} - -void MoveLoop::OnWindowBoundsChanged(mus::Window* window, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) { - DCHECK_EQ(window, target_); - if (!changing_bounds_) - Cancel(); -} - -void MoveLoop::OnWindowVisibilityChanged(mus::Window* window) { - DCHECK_EQ(window, target_); - Cancel(); -} diff --git a/mash/wm/move_loop.h b/mash/wm/move_loop.h deleted file mode 100644 index fd1a3e7..0000000 --- a/mash/wm/move_loop.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2015 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 MASH_WM_MOVE_LOOP_H_ -#define MASH_WM_MOVE_LOOP_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "components/mus/public/cpp/window_observer.h" -#include "components/mus/public/interfaces/input_events.mojom.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/rect.h" - -// MoveLoop is responsible for moving/resizing windows. -class MoveLoop : public mus::WindowObserver { - public: - enum MoveResult { - // The move is still ongoing. - CONTINUE, - - // The move is done and the MoveLoop should be destroyed. - DONE, - }; - - enum class HorizontalLocation { - LEFT, - RIGHT, - OTHER, - }; - - enum class VerticalLocation { - TOP, - BOTTOM, - OTHER, - }; - - // Number of pixels in which a resize is triggered. - static const int kResizeSize = 8; - - ~MoveLoop() override; - - // If a move/resize loop should occur for the specified parameters creates - // and returns a new MoveLoop. All events should be funneled to the MoveLoop - // until done (Move()). - static scoped_ptr Create(mus::Window* target, - const mus::mojom::Event& event); - - // Processes an event for a move/resize loop. - MoveResult Move(const mus::mojom::Event& event); - - private: - enum class Type { - MOVE, - RESIZE, - }; - - MoveLoop(mus::Window* target, - const mus::mojom::Event& event, - Type type, - HorizontalLocation h_loc, - VerticalLocation v_loc); - - static void DetermineType(mus::Window* target, - const gfx::Point& location, - Type* type, - HorizontalLocation* h_loc, - VerticalLocation* v_loc); - - // Does the actual move/resize. - void MoveImpl(const mus::mojom::Event& event); - - // Cancels the loop. This sets |target_| to null and removes the observer. - // After this the MoveLoop is still ongoing and won't stop until the - // appropriate event is received. - void Cancel(); - - void Revert(); - - gfx::Rect DetermineBoundsFromDelta(const gfx::Vector2d& delta); - - // mus::WindowObserver: - void OnTreeChanged(const TreeChangeParams& params) override; - void OnWindowBoundsChanged(mus::Window* window, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) override; - void OnWindowVisibilityChanged(mus::Window* window) override; - - // The window this MoveLoop is acting on. |target_| is set to null if the - // window unexpectedly changes while the move is in progress. - mus::Window* target_; - - const Type type_; - const HorizontalLocation h_loc_; - const VerticalLocation v_loc_; - - // The id of the pointer that triggered the move. - const int32_t pointer_id_; - - // Location of the event (in screen coordinates) that triggered the move. - const gfx::Point initial_event_screen_location_; - - // Original bounds of the window. - const gfx::Rect initial_window_bounds_; - - const gfx::Rect initial_user_set_bounds_; - - // Set to true when MoveLoop changes the bounds of |target_|. The move is - // canceled if the bounds change unexpectedly during the move. - bool changing_bounds_; - - DISALLOW_COPY_AND_ASSIGN(MoveLoop); -}; - -#endif // MASH_WM_MOVE_LOOP_H_ diff --git a/mash/wm/move_loop_unittest.cc b/mash/wm/move_loop_unittest.cc deleted file mode 100644 index 462c7df7..0000000 --- a/mash/wm/move_loop_unittest.cc +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2015 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 "mash/wm/move_loop.h" - -#include "components/mus/public/cpp/tests/test_window.h" -#include "mojo/converters/input_events/input_events_type_converters.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/events/event.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/rect.h" - -using MoveLoopTest = testing::Test; - -namespace { - -// Sets the client area for |window|. Padding is provided on the top so that -// there is a movable region. All other edges are sized to -// |MoveLoop::kResizeSize|. -void SetClientArea(mus::Window* window) { - if (window->bounds().IsEmpty()) - window->SetBounds(gfx::Rect(100, 200, 300, 400)); - - window->SetClientArea( - gfx::Insets(MoveLoop::kResizeSize + 10, MoveLoop::kResizeSize, - MoveLoop::kResizeSize, MoveLoop::kResizeSize)); -} - -mus::mojom::EventPtr CreatePointerDownEvent(const gfx::Point& location) { - const ui::TouchEvent event(ui::ET_TOUCH_PRESSED, location, 1, - base::TimeDelta()); - return mus::mojom::Event::From(static_cast(event)); -} - -mus::mojom::EventPtr CreatePointerMove(const gfx::Point& location) { - const ui::TouchEvent event(ui::ET_TOUCH_MOVED, location, 1, - base::TimeDelta()); - return mus::mojom::Event::From(static_cast(event)); -} - -enum class HorizontalLocation { - LEFT, - MIDDLE, - RIGHT, -}; - -enum class VerticalLocation { - TOP_MOVE, - TOP_RESIZE, - MIDDLE, - BOTTOM, -}; - -int HorizontalLocationToPoint(const mus::Window* window, - HorizontalLocation loc) { - if (loc == HorizontalLocation::LEFT) - return 0; - return loc == HorizontalLocation::MIDDLE ? window->bounds().width() / 2 - : window->bounds().width() - 1; -} - -int VerticalLocationToPoint(const mus::Window* window, VerticalLocation loc) { - switch (loc) { - case VerticalLocation::TOP_MOVE: - return MoveLoop::kResizeSize + 1; - case VerticalLocation::TOP_RESIZE: - return 0; - case VerticalLocation::MIDDLE: - return window->bounds().height() / 2; - case VerticalLocation::BOTTOM: - return window->bounds().height() - 1; - } - return 0; -} - -gfx::Point LocationToPoint(const mus::Window* window, - HorizontalLocation h_loc, - VerticalLocation v_loc) { - return gfx::Point(HorizontalLocationToPoint(window, h_loc), - VerticalLocationToPoint(window, v_loc)); -} - -} // namespace - -TEST_F(MoveLoopTest, Move) { - struct TestData { - HorizontalLocation initial_h_loc; - VerticalLocation initial_v_loc; - // Number of values in delta_* that are valid. For example, if this is 1 - // then only one move is generated with thye point |delta_x[0]|, - // |delta_y[0]|. - int delta_count; - int delta_x[3]; - int delta_y[3]; - gfx::Rect expected_bounds; - } data[] = { - // Move. - {HorizontalLocation::MIDDLE, - VerticalLocation::TOP_MOVE, - 1, - {1, 0, 0}, - {2, 0, 0}, - gfx::Rect(101, 202, 300, 400)}, - - // Resize from top, left. - {HorizontalLocation::LEFT, - VerticalLocation::TOP_RESIZE, - 1, - {1, 0, 0}, - {2, 0, 0}, - gfx::Rect(101, 202, 299, 398)}, - - // Resize from left. - {HorizontalLocation::LEFT, - VerticalLocation::MIDDLE, - 1, - {1, 0, 0}, - {2, 0, 0}, - gfx::Rect(101, 200, 299, 400)}, - - // Resize from bottom, left. - {HorizontalLocation::LEFT, - VerticalLocation::BOTTOM, - 1, - {-1, 0, 0}, - {-2, 0, 0}, - gfx::Rect(99, 200, 301, 398)}, - - // Resize from bottom. - {HorizontalLocation::MIDDLE, - VerticalLocation::BOTTOM, - 1, - {-1, 0, 0}, - {4, 0, 0}, - gfx::Rect(100, 200, 300, 404)}, - - // Resize from bottom, right. - {HorizontalLocation::RIGHT, - VerticalLocation::BOTTOM, - 1, - {-3, 0, 0}, - {4, 0, 0}, - gfx::Rect(100, 200, 297, 404)}, - - // Resize from right. - {HorizontalLocation::RIGHT, - VerticalLocation::MIDDLE, - 1, - {6, 0, 0}, - {-4, 0, 0}, - gfx::Rect(100, 200, 306, 400)}, - - // Resize from top, right. - {HorizontalLocation::RIGHT, - VerticalLocation::TOP_RESIZE, - 1, - {6, 0, 0}, - {5, 0, 0}, - gfx::Rect(100, 205, 306, 395)}, - }; - - for (size_t i = 0; i < arraysize(data); ++i) { - mus::TestWindow window; - SetClientArea(&window); - gfx::Point pointer_location( - LocationToPoint(&window, data[i].initial_h_loc, data[i].initial_v_loc)); - scoped_ptr move_loop = - MoveLoop::Create(&window, *CreatePointerDownEvent(pointer_location)); - ASSERT_TRUE(move_loop.get()) << i; - for (int j = 0; j < data[i].delta_count; ++j) { - pointer_location.Offset(data[i].delta_x[j], data[i].delta_y[j]); - ASSERT_EQ(MoveLoop::MoveResult::CONTINUE, - move_loop->Move(*CreatePointerMove(pointer_location))) - << i << " " << j; - } - ASSERT_EQ(data[i].expected_bounds, window.bounds()); - } -} diff --git a/mash/wm/non_client_frame_controller.cc b/mash/wm/non_client_frame_controller.cc index b9a95ed..b0eef4e 100644 --- a/mash/wm/non_client_frame_controller.cc +++ b/mash/wm/non_client_frame_controller.cc @@ -5,7 +5,7 @@ #include "mash/wm/non_client_frame_controller.h" #include "components/mus/public/cpp/window.h" -#include "mash/wm/non_client_frame_view_impl.h" +#include "mash/wm/frame/non_client_frame_view_mash.h" #include "mash/wm/property_util.h" #include "ui/views/mus/native_widget_mus.h" #include "ui/views/widget/widget.h" @@ -25,9 +25,10 @@ class WmNativeWidgetMus : public views::NativeWidgetMus { // NativeWidgetMus: views::NonClientFrameView* CreateNonClientFrameView() override { - NonClientFrameViewImpl* frame_view = new NonClientFrameViewImpl(window()); - frame_view->Init( - static_cast(this)->GetWidget()); + views::Widget* widget = + static_cast(this)->GetWidget(); + mash::wm::NonClientFrameViewMash* frame_view = + new mash::wm::NonClientFrameViewMash(widget, window()); return frame_view; } void CenterWindow(const gfx::Size& size) override { @@ -56,6 +57,11 @@ NonClientFrameController::NonClientFrameController(mojo::Shell* shell, widget_->Show(); } +// static +gfx::Insets NonClientFrameController::GetPreferredClientAreaInsets() { + return mash::wm::NonClientFrameViewMash::GetPreferredClientAreaInsets(); +} + NonClientFrameController::~NonClientFrameController() { if (window_) window_->RemoveObserver(this); diff --git a/mash/wm/non_client_frame_controller.h b/mash/wm/non_client_frame_controller.h index dfedd07..5e5918c 100644 --- a/mash/wm/non_client_frame_controller.h +++ b/mash/wm/non_client_frame_controller.h @@ -9,6 +9,10 @@ #include "components/mus/public/cpp/window_observer.h" #include "ui/views/widget/widget_delegate.h" +namespace gfx { +class Insets; +} + namespace mojo { class Shell; } @@ -24,6 +28,9 @@ class NonClientFrameController : public views::WidgetDelegateView, // NonClientFrameController deletes itself when |window| is destroyed. NonClientFrameController(mojo::Shell* shell, mus::Window* window); + // Returns the preferred client area insets. + static gfx::Insets GetPreferredClientAreaInsets(); + private: ~NonClientFrameController() override; diff --git a/mash/wm/non_client_frame_view_impl.cc b/mash/wm/non_client_frame_view_impl.cc deleted file mode 100644 index 976143b..0000000 --- a/mash/wm/non_client_frame_view_impl.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2015 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 "mash/wm/non_client_frame_view_impl.h" - -#include "components/mus/public/cpp/window.h" -#include "mash/wm/move_loop.h" -#include "mojo/converters/input_events/input_events_type_converters.h" -#include "ui/compositor/paint_recorder.h" -#include "ui/gfx/canvas.h" - -NonClientFrameViewImpl::NonClientFrameViewImpl(mus::Window* window) - : window_(window) { - window_->AddObserver(this); -} - -NonClientFrameViewImpl::~NonClientFrameViewImpl() { - if (window_) - window_->RemoveObserver(this); -} - -gfx::Rect NonClientFrameViewImpl::GetBoundsForClientView() const { - gfx::Rect result(GetLocalBounds()); - result.Inset(window_->client_area()); - return result; -} - -void NonClientFrameViewImpl::OnPaint(gfx::Canvas* canvas) { - canvas->Save(); - CustomFrameView::OnPaint(canvas); - canvas->Restore(); - - // The client app draws the client area. Make ours totally transparent so - // we only see the client apps client area. - canvas->FillRect(GetBoundsForClientView(), SK_ColorBLACK, - SkXfermode::kSrc_Mode); -} - -void NonClientFrameViewImpl::PaintChildren(const ui::PaintContext& context) { - CustomFrameView::PaintChildren(context); - - // The client app draws the client area. Make ours totally transparent so - // we only see the client apps client area. - ui::PaintRecorder recorder(context, size(), &paint_cache_); - recorder.canvas()->FillRect(GetBoundsForClientView(), SK_ColorBLACK, - SkXfermode::kSrc_Mode); -} - -bool NonClientFrameViewImpl::OnMousePressed(const ui::MouseEvent& event) { - return StartMoveLoopIfNecessary(event); -} - -bool NonClientFrameViewImpl::OnMouseDragged(const ui::MouseEvent& event) { - ContinueMove(event); - return move_loop_.get() != nullptr; -} - -void NonClientFrameViewImpl::OnMouseReleased(const ui::MouseEvent& event) { - ContinueMove(event); -} - -void NonClientFrameViewImpl::OnMouseCaptureLost() { - StopMove(); -} - -void NonClientFrameViewImpl::OnWindowClientAreaChanged( - mus::Window* window, - const gfx::Insets& old_client_area) { - Layout(); - // NonClientView (our parent) positions the client view based on bounds from - // us. We need to layout from parent to trigger a layout of the client view. - if (parent()) - parent()->Layout(); - SchedulePaint(); -} - -void NonClientFrameViewImpl::OnWindowDestroyed(mus::Window* window) { - window_->RemoveObserver(this); - window_ = nullptr; -} - -bool NonClientFrameViewImpl::StartMoveLoopIfNecessary(const ui::Event& event) { - if (move_loop_) - return false; - // TODO(sky): convert MoveLoop to take ui::Event. - move_loop_ = MoveLoop::Create(window_, *mus::mojom::Event::From(event)); - return true; -} - -void NonClientFrameViewImpl::ContinueMove(const ui::Event& event) { - // TODO(sky): convert MoveLoop to take ui::Event. - if (move_loop_ && - move_loop_->Move(*mus::mojom::Event::From(event)) == MoveLoop::DONE) { - move_loop_.reset(); - } -} - -void NonClientFrameViewImpl::StopMove() { - move_loop_.reset(); -} diff --git a/mash/wm/non_client_frame_view_impl.h b/mash/wm/non_client_frame_view_impl.h deleted file mode 100644 index e7e3eef..0000000 --- a/mash/wm/non_client_frame_view_impl.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2015 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 MASH_WM_NON_CLIENT_FRAME_VIEW_IMPL_H_ -#define MASH_WM_NON_CLIENT_FRAME_VIEW_IMPL_H_ - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "components/mus/public/cpp/window_observer.h" -#include "ui/compositor/paint_cache.h" -#include "ui/views/window/custom_frame_view.h" - -class MoveLoop; - -class NonClientFrameViewImpl : public views::CustomFrameView, - public mus::WindowObserver { - public: - explicit NonClientFrameViewImpl(mus::Window* window); - ~NonClientFrameViewImpl() override; - - private: - // CustomFrameView: - gfx::Rect GetBoundsForClientView() const override; - void OnPaint(gfx::Canvas* canvas) override; - void PaintChildren(const ui::PaintContext& context) override; - bool OnMousePressed(const ui::MouseEvent& event) override; - bool OnMouseDragged(const ui::MouseEvent& event) override; - void OnMouseReleased(const ui::MouseEvent& event) override; - void OnMouseCaptureLost() override; - - // mus::WindowObserver: - void OnWindowClientAreaChanged(mus::Window* window, - const gfx::Insets& old_client_area) override; - void OnWindowDestroyed(mus::Window* window) override; - - private: - bool StartMoveLoopIfNecessary(const ui::Event& event); - void ContinueMove(const ui::Event& event); - void StopMove(); - - mus::Window* window_; - ui::PaintCache paint_cache_; - scoped_ptr move_loop_; - - DISALLOW_COPY_AND_ASSIGN(NonClientFrameViewImpl); -}; - -#endif // MASH_WM_NON_CLIENT_FRAME_VIEW_IMPL_H_ diff --git a/mash/wm/window_manager_application.cc b/mash/wm/window_manager_application.cc index 65ec47c..bf58d79 100644 --- a/mash/wm/window_manager_application.cc +++ b/mash/wm/window_manager_application.cc @@ -63,7 +63,7 @@ void WindowManagerApplication::OnEmbed(mus::Window* root) { window_manager_.reset(new WindowManagerImpl(this)); ui_init_.reset(new ui::mojo::UIInit(views::GetDisplaysFromWindow(root))); - aura_init_.reset(new views::AuraInit(app_, "views_mus_resources.pak")); + aura_init_.reset(new views::AuraInit(app_, "mash_wm_resources.pak")); for (auto request : requests_) window_manager_binding_.AddBinding(window_manager_.get(), request->Pass()); diff --git a/mash/wm/window_manager_impl.cc b/mash/wm/window_manager_impl.cc index b5c2b09..46919aa 100644 --- a/mash/wm/window_manager_impl.cc +++ b/mash/wm/window_manager_impl.cc @@ -10,7 +10,6 @@ #include "components/mus/public/cpp/window_property.h" #include "components/mus/public/cpp/window_tree_connection.h" #include "components/mus/public/interfaces/input_events.mojom.h" -#include "mash/wm/move_loop.h" #include "mash/wm/non_client_frame_controller.h" #include "mash/wm/property_util.h" #include "mash/wm/public/interfaces/container.mojom.h" @@ -102,17 +101,11 @@ void WindowManagerImpl::GetConfig(const GetConfigCallback& callback) { // The insets are roughly what is needed by CustomFrameView. The expectation // is at some point we'll write our own NonClientFrameView and get the insets // from it. - config->normal_client_area_insets = mojo::Insets::New(); - config->normal_client_area_insets->top = 23; - config->normal_client_area_insets->left = 5; - config->normal_client_area_insets->right = 5; - config->normal_client_area_insets->bottom = 5; - - config->maximized_client_area_insets = mojo::Insets::New(); - config->maximized_client_area_insets->top = 21; - config->maximized_client_area_insets->left = 0; - config->maximized_client_area_insets->right = 0; - config->maximized_client_area_insets->bottom = 0; + const gfx::Insets client_area_insets = + NonClientFrameController::GetPreferredClientAreaInsets(); + config->normal_client_area_insets = mojo::Insets::From(client_area_insets); + + config->maximized_client_area_insets = mojo::Insets::From(client_area_insets); callback.Run(config.Pass()); } -- cgit v1.1