diff options
-rw-r--r-- | ash/mus/shelf_delegate_mus.cc | 40 | ||||
-rw-r--r-- | ash/mus/shelf_delegate_mus.h | 2 | ||||
-rw-r--r-- | components/mus/public/cpp/lib/property_type_converters.cc | 84 | ||||
-rw-r--r-- | components/mus/public/cpp/property_type_converters.h | 15 | ||||
-rw-r--r-- | components/mus/public/cpp/tests/BUILD.gn | 1 | ||||
-rw-r--r-- | components/mus/public/cpp/tests/property_type_converters_unittest.cc | 89 | ||||
-rw-r--r-- | components/mus/public/interfaces/window_manager.mojom | 3 | ||||
-rw-r--r-- | mash/task_viewer/BUILD.gn | 1 | ||||
-rw-r--r-- | mash/task_viewer/task_viewer.cc | 9 | ||||
-rw-r--r-- | mash/wm/non_client_frame_controller.cc | 8 | ||||
-rw-r--r-- | mash/wm/public/interfaces/user_window_controller.mojom | 4 | ||||
-rw-r--r-- | mash/wm/user_window_controller_impl.cc | 45 | ||||
-rw-r--r-- | mash/wm/user_window_controller_impl.h | 4 | ||||
-rw-r--r-- | mash/wm/window_manager.cc | 1 | ||||
-rw-r--r-- | ui/views/mus/BUILD.gn | 3 | ||||
-rw-r--r-- | ui/views/mus/native_widget_mus.cc | 30 | ||||
-rw-r--r-- | ui/views/mus/native_widget_mus_unittest.cc | 128 |
17 files changed, 446 insertions, 21 deletions
diff --git a/ash/mus/shelf_delegate_mus.cc b/ash/mus/shelf_delegate_mus.cc index 510bb1b..5b06eda 100644 --- a/ash/mus/shelf_delegate_mus.cc +++ b/ash/mus/shelf_delegate_mus.cc @@ -19,6 +19,7 @@ #include "mojo/shell/public/cpp/connector.h" #include "ui/aura/mus/mus_util.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image_skia.h" #include "ui/resources/grit/ui_resources.h" #include "ui/views/mus/window_manager_connection.h" @@ -78,6 +79,25 @@ class ShelfItemDelegateMus : public ShelfItemDelegate { DISALLOW_COPY_AND_ASSIGN(ShelfItemDelegateMus); }; +// Returns an icon image from a serialized SkBitmap, or the default shelf icon +// image if the bitmap is empty. Assumes the bitmap is a 1x icon. +// TODO(jamescook): Support other scale factors. +gfx::ImageSkia GetShelfIconFromBitmap( + const mojo::Array<uint8_t>& serialized_bitmap) { + // Convert the data to an ImageSkia. + SkBitmap bitmap = mojo::ConvertTo<SkBitmap, const std::vector<uint8_t>>( + serialized_bitmap.storage()); + gfx::ImageSkia icon_image; + if (!bitmap.isNull()) { + icon_image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap); + } else { + // Use default icon. + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + icon_image = *rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON); + } + return icon_image; +} + } // namespace ShelfDelegateMus::ShelfDelegateMus(ShelfModel* model) @@ -150,9 +170,7 @@ void ShelfDelegateMus::OnUserWindowAdded( ShelfItem item; item.type = TYPE_PLATFORM_APP; item.status = user_window->window_has_focus ? STATUS_ACTIVE : STATUS_RUNNING; - // TODO(msw): Support actual window icons. - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - item.image = *rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON); + item.image = GetShelfIconFromBitmap(user_window->window_app_icon); ShelfID shelf_id = model_->next_id(); window_id_to_shelf_id_.insert( @@ -195,6 +213,22 @@ void ShelfDelegateMus::OnUserWindowTitleChanged( model_->Set(index, *iter); } +void ShelfDelegateMus::OnUserWindowAppIconChanged( + uint32_t window_id, + mojo::Array<uint8_t> app_icon) { + // Find the shelf ID for this window. + DCHECK(window_id_to_shelf_id_.count(window_id)); + ShelfID shelf_id = window_id_to_shelf_id_[window_id]; + DCHECK_GT(shelf_id, 0); + + // Update the icon in the ShelfItem. + int index = model_->ItemIndexByID(shelf_id); + DCHECK_GE(index, 0); + ShelfItem item = *model_->ItemByID(shelf_id); + item.image = GetShelfIconFromBitmap(app_icon); + model_->Set(index, item); +} + void ShelfDelegateMus::OnUserWindowFocusChanged(uint32_t window_id, bool has_focus) { DCHECK(window_id_to_shelf_id_.count(window_id)); diff --git a/ash/mus/shelf_delegate_mus.h b/ash/mus/shelf_delegate_mus.h index 47364fc..333a84d 100644 --- a/ash/mus/shelf_delegate_mus.h +++ b/ash/mus/shelf_delegate_mus.h @@ -41,6 +41,8 @@ class ShelfDelegateMus : public ShelfDelegate, void OnUserWindowRemoved(uint32_t window_id) override; void OnUserWindowTitleChanged(uint32_t window_id, const mojo::String& window_title) override; + void OnUserWindowAppIconChanged(uint32_t window_id, + mojo::Array<uint8_t> app_icon) override; void OnUserWindowFocusChanged(uint32_t window_id, bool has_focus) override; ShelfModel* model_; diff --git a/components/mus/public/cpp/lib/property_type_converters.cc b/components/mus/public/cpp/lib/property_type_converters.cc index 2e1ff65..01c74a2 100644 --- a/components/mus/public/cpp/lib/property_type_converters.cc +++ b/components/mus/public/cpp/lib/property_type_converters.cc @@ -7,9 +7,19 @@ #include <stdint.h> #include "base/strings/utf_string_conversions.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" +namespace { + +// Maximum allowed height or width of a bitmap, in pixels. This limit prevents +// malformed bitmap headers from causing arbitrarily large memory allocations +// for pixel data. +const int kMaxBitmapSize = 4096; + +} // namespace + namespace mojo { // static @@ -73,7 +83,7 @@ gfx::Size TypeConverter<gfx::Size, const std::vector<uint8_t>>::Convert( const std::vector<uint8_t> TypeConverter<const std::vector<uint8_t>, int32_t>::Convert( const int32_t& input) { - std::vector<uint8_t> vec(8); + std::vector<uint8_t> vec(4); vec[0] = (input >> 24) & 0xFF; vec[1] = (input >> 16) & 0xFF; vec[2] = (input >> 8) & 0xFF; @@ -118,5 +128,75 @@ TypeConverter<std::string, const std::vector<uint8_t>>::Convert( return std::string(input.begin(), input.end()); } -} // namespace mojo +// static +const std::vector<uint8_t> +TypeConverter<const std::vector<uint8_t>, SkBitmap>::Convert( + const SkBitmap& input) { + // Empty images are valid to serialize and are represented by an empty vector. + if (input.isNull()) + return std::vector<uint8_t>(); + + // Only RGBA 8888 bitmaps with premultiplied alpha are supported. + if (input.colorType() != kBGRA_8888_SkColorType || + input.alphaType() != kPremul_SkAlphaType) { + NOTREACHED(); + return std::vector<uint8_t>(); + } + + // Sanity check the bitmap size. + int width = input.width(); + int height = input.height(); + if (width < 0 || width > kMaxBitmapSize || height < 0 || + height > kMaxBitmapSize) { + NOTREACHED(); + return std::vector<uint8_t>(); + } + + // Serialize the bitmap. The size is restricted so only 2 bytes are required + // per dimension. + std::vector<uint8_t> vec(4 + input.getSize()); + vec[0] = (width >> 8) & 0xFF; + vec[1] = width & 0xFF; + vec[2] = (height >> 8) & 0xFF; + vec[3] = height & 0xFF; + if (!input.copyPixelsTo(&vec[4], input.getSize())) + return std::vector<uint8_t>(); + return vec; +} +// static +SkBitmap TypeConverter<SkBitmap, const std::vector<uint8_t>>::Convert( + const std::vector<uint8_t>& input) { + // Empty images are represented by empty vectors. + if (input.empty()) + return SkBitmap(); + + // Read and sanity check size. + int width = input[0] << 8 | input[1]; + int height = input[2] << 8 | input[3]; + if (width < 0 || width > kMaxBitmapSize || height < 0 || + height > kMaxBitmapSize) { + NOTREACHED(); + return SkBitmap(); + } + + // Try to allocate a bitmap of the appropriate size. + SkBitmap bitmap; + if (!bitmap.tryAllocPixels(SkImageInfo::Make( + width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType))) { + return SkBitmap(); + } + + // Ensure the vector contains the right amount of data. + if (input.size() != bitmap.getSize() + 4) { + NOTREACHED(); + return SkBitmap(); + } + + // Read the pixel data. + SkAutoLockPixels lock(bitmap); + memcpy(bitmap.getPixels(), &input[4], bitmap.getSize()); + return bitmap; +} + +} // namespace mojo diff --git a/components/mus/public/cpp/property_type_converters.h b/components/mus/public/cpp/property_type_converters.h index eac34a8..cec17d6 100644 --- a/components/mus/public/cpp/property_type_converters.h +++ b/components/mus/public/cpp/property_type_converters.h @@ -11,6 +11,8 @@ #include "base/strings/string16.h" #include "mojo/public/cpp/bindings/type_converter.h" +class SkBitmap; + namespace gfx { class Rect; class Size; @@ -21,6 +23,8 @@ namespace mojo { // TODO(beng): these methods serialize types used for standard properties // to vectors of bytes used by Window::SetSharedProperty(). // replace this with serialization code generated @ bindings. +// This would be especially useful for SkBitmap, which could be +// replaced with the skia.Bitmap mojom struct serialization. template <> struct TypeConverter<const std::vector<uint8_t>, gfx::Rect> { @@ -67,6 +71,17 @@ struct TypeConverter<std::string, const std::vector<uint8_t>> { static std::string Convert(const std::vector<uint8_t>& input); }; +// NOTE: These methods only serialize and deserialize the common case of RGBA +// 8888 bitmaps with premultiplied alpha. +template <> +struct TypeConverter<const std::vector<uint8_t>, SkBitmap> { + static const std::vector<uint8_t> Convert(const SkBitmap& input); +}; +template <> +struct TypeConverter<SkBitmap, const std::vector<uint8_t>> { + static SkBitmap Convert(const std::vector<uint8_t>& input); +}; + } // namespace mojo #endif // COMPONENTS_MUS_PUBLIC_CPP_PROPERTY_TYPE_CONVERTERS_H_ diff --git a/components/mus/public/cpp/tests/BUILD.gn b/components/mus/public/cpp/tests/BUILD.gn index d0c6a05..e7bf333 100644 --- a/components/mus/public/cpp/tests/BUILD.gn +++ b/components/mus/public/cpp/tests/BUILD.gn @@ -41,6 +41,7 @@ source_set("test_support") { test("mojo_view_manager_lib_unittests") { sources = [ + "property_type_converters_unittest.cc", "run_all_unittests.cc", "test_window_tree.cc", "test_window_tree.h", diff --git a/components/mus/public/cpp/tests/property_type_converters_unittest.cc b/components/mus/public/cpp/tests/property_type_converters_unittest.cc new file mode 100644 index 0000000..7726172 --- /dev/null +++ b/components/mus/public/cpp/tests/property_type_converters_unittest.cc @@ -0,0 +1,89 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/mus/public/cpp/property_type_converters.h" + +#include <stdint.h> + +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/skia_util.h" + +namespace mojo { +namespace { + +// Use a different width and height so tests can tell the values apart. +const int kBitmapWidth = 16; +const int kBitmapHeight = 32; + +// Makes a small rectangular bitmap. +SkBitmap MakeBitmap() { + SkBitmap bitmap; + bitmap.allocN32Pixels(kBitmapWidth, kBitmapHeight); + bitmap.eraseARGB(255, 11, 22, 33); + return bitmap; +} + +// Tests that an SkBitmap can be serialized. +TEST(PropertyTypeConvertersTest, SkBitmapSerialize) { + SkBitmap bitmap = MakeBitmap(); + std::vector<uint8_t> bytes = + TypeConverter<const std::vector<uint8_t>, SkBitmap>::Convert(bitmap); + + // Size should be 4 bytes of header plus size of RGBA pixels. + ASSERT_EQ(4 + bitmap.getSize(), bytes.size()); + + // Header contains width first then height. + EXPECT_EQ(0, bytes[0]); + EXPECT_EQ(kBitmapWidth, bytes[1]); + EXPECT_EQ(0, bytes[2]); + EXPECT_EQ(kBitmapHeight, bytes[3]); + + // The rest of the bytes are the image data. + EXPECT_EQ(0, memcmp(&bytes[4], bitmap.getPixels(), bitmap.getSize())); +} + +// Tests that an SkBitmap can be deserialized. +TEST(PropertyTypeConvertersTest, SkBitmapDeserialize) { + // Make a 1x2 pixel bitmap. + std::vector<uint8_t> bytes = {0, 1, 0, 2, 11, 22, 33, 44, 55, 66, 77, 88}; + SkBitmap bitmap = + TypeConverter<SkBitmap, const std::vector<uint8_t>>::Convert(bytes); + EXPECT_EQ(1, bitmap.width()); + EXPECT_EQ(2, bitmap.height()); + // The image pixels match the vector bytes. + ASSERT_EQ(8U, bitmap.getSize()); + EXPECT_EQ(0, memcmp(bitmap.getPixels(), &bytes[4], 8)); +} + +// Tests round-trip serializing and deserializing an SkBitmap. +TEST(PropertyTypeConvertersTest, SkBitmapRoundTrip) { + SkBitmap bitmap1 = MakeBitmap(); + std::vector<uint8_t> bytes = + TypeConverter<const std::vector<uint8_t>, SkBitmap>::Convert(bitmap1); + SkBitmap bitmap2 = + TypeConverter<SkBitmap, const std::vector<uint8_t>>::Convert(bytes); + EXPECT_TRUE(gfx::BitmapsAreEqual(bitmap1, bitmap2)); +} + +// Tests that an empty SkBitmap serializes to an empty vector. +TEST(PropertyTypeConvertersTest, SkBitmapSerializeEmpty) { + SkBitmap bitmap; + std::vector<uint8_t> bytes = + TypeConverter<const std::vector<uint8_t>, SkBitmap>::Convert(bitmap); + EXPECT_TRUE(bytes.empty()); +} + +// Tests that an empty vector deserializes to a null SkBitmap. +TEST(PropertyTypeConvertersTest, SkBitmapDeserializeEmpty) { + std::vector<uint8_t> bytes; + SkBitmap bitmap = + TypeConverter<SkBitmap, const std::vector<uint8_t>>::Convert(bytes); + EXPECT_TRUE(bitmap.isNull()); +} + +} // namespace +} // namespace mojo diff --git a/components/mus/public/interfaces/window_manager.mojom b/components/mus/public/interfaces/window_manager.mojom index 724c7ac..11cf1fe 100644 --- a/components/mus/public/interfaces/window_manager.mojom +++ b/components/mus/public/interfaces/window_manager.mojom @@ -32,6 +32,9 @@ interface WindowManager { const string kShowState_Property = "prop:show-state"; // The window bounds as set by user input. Type: gfx::Rect. const string kUserSetBounds_Property = "prop:user-set-bounds"; + // The window's app icon. Type: SkBitmap + const string kWindowAppIcon_Property = "prop:window-app-icon"; + // The window type. Type: mojom::WindowType const string kWindowType_Property = "prop:window-type"; // The window's title. Type: mojom::String const string kWindowTitle_Property = "prop:window-title"; diff --git a/mash/task_viewer/BUILD.gn b/mash/task_viewer/BUILD.gn index 1d20b22..1ea8cbc 100644 --- a/mash/task_viewer/BUILD.gn +++ b/mash/task_viewer/BUILD.gn @@ -21,6 +21,7 @@ source_set("lib") { "//mojo/shell/public/cpp", "//mojo/shell/public/cpp:sources", "//mojo/shell/public/interfaces", + "//ui/resources", "//ui/views", "//ui/views/mus:for_mojo_application", ] diff --git a/mash/task_viewer/task_viewer.cc b/mash/task_viewer/task_viewer.cc index 0e01a00..fb5cb1f 100644 --- a/mash/task_viewer/task_viewer.cc +++ b/mash/task_viewer/task_viewer.cc @@ -19,6 +19,8 @@ #include "mojo/shell/public/cpp/connector.h" #include "mojo/shell/public/interfaces/shell.mojom.h" #include "ui/base/models/table_model.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/resources/grit/ui_resources.h" #include "ui/views/background.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/table/table_view.h" @@ -87,6 +89,13 @@ class TaskViewerContents : public views::WidgetDelegateView, return base::ASCIIToUTF16("Tasks"); } + gfx::ImageSkia GetWindowAppIcon() override { + // TODO(jamescook): Create a new .pak file for this app and make a custom + // icon, perhaps one that looks like the Chrome OS task viewer icon. + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + return *rb.GetImageSkiaNamed(IDR_NOTIFICATION_SETTINGS); + } + // Overridden from views::View: void Layout() override { gfx::Rect bounds = GetLocalBounds(); diff --git a/mash/wm/non_client_frame_controller.cc b/mash/wm/non_client_frame_controller.cc index ed6ad3a..774329a 100644 --- a/mash/wm/non_client_frame_controller.cc +++ b/mash/wm/non_client_frame_controller.cc @@ -125,6 +125,14 @@ class WmNativeWidgetMus : public views::NativeWidgetMus { void CenterWindow(const gfx::Size& size) override { // Do nothing. The client controls the size, not us. } + bool SetWindowTitle(const base::string16& title) override { + // Do nothing. The client controls the window title, not us. + return false; + } + void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) override { + // Do nothing. The client controls window icons, not us. + } void UpdateClientArea() override { // This pushes the client area to the WS. We don't want to do that as // the client area should come from the client, not us. diff --git a/mash/wm/public/interfaces/user_window_controller.mojom b/mash/wm/public/interfaces/user_window_controller.mojom index c1d61d8..3d84fb1 100644 --- a/mash/wm/public/interfaces/user_window_controller.mojom +++ b/mash/wm/public/interfaces/user_window_controller.mojom @@ -8,6 +8,7 @@ struct UserWindow { uint32 window_id; string window_title; bool window_has_focus; + array<uint8> window_app_icon; // Serialized SkBitmap. }; // An observer of user windows within mojom::Container::USER_WINDOWS. @@ -20,6 +21,9 @@ interface UserWindowObserver { OnUserWindowRemoved(uint32 window_id); OnUserWindowTitleChanged(uint32 window_id, string window_title); + + // |app_icon| is a serialized SkBitmap. + OnUserWindowAppIconChanged(uint32 window_id, array<uint8> app_icon); OnUserWindowFocusChanged(uint32 window_id, bool has_focus); }; diff --git a/mash/wm/user_window_controller_impl.cc b/mash/wm/user_window_controller_impl.cc index be2f24e..3e81da94 100644 --- a/mash/wm/user_window_controller_impl.cc +++ b/mash/wm/user_window_controller_impl.cc @@ -11,6 +11,7 @@ #include "mash/wm/public/interfaces/container.mojom.h" #include "mash/wm/root_window_controller.h" #include "mojo/common/common_type_converters.h" +#include "ui/resources/grit/ui_resources.h" namespace mash { namespace wm { @@ -26,6 +27,17 @@ mojo::String GetWindowTitle(mus::Window* window) { return mojo::String(std::string()); } +// Get the serialized app icon bitmap from a mus::Window. +mojo::Array<uint8_t> GetWindowAppIcon(mus::Window* window) { + if (window->HasSharedProperty( + mus::mojom::WindowManager::kWindowAppIcon_Property)) { + return mojo::Array<uint8_t>::From( + window->GetSharedProperty<const std::vector<uint8_t>>( + mus::mojom::WindowManager::kWindowAppIcon_Property)); + } + return mojo::Array<uint8_t>(); +} + // Returns |window|, or an ancestor thereof, parented to |container|, or null. mus::Window* GetTopLevelWindow(mus::Window* window, mus::Window* container) { while (window && window->parent() != container) @@ -38,6 +50,7 @@ mojom::UserWindowPtr GetUserWindow(mus::Window* window) { mojom::UserWindowPtr user_window(mojom::UserWindow::New()); user_window->window_id = window->id(); user_window->window_title = GetWindowTitle(window); + user_window->window_app_icon = GetWindowAppIcon(window); mus::Window* focused = window->connection()->GetFocusedWindow(); focused = GetTopLevelWindow(focused, window->parent()); user_window->window_has_focus = focused == window; @@ -46,13 +59,14 @@ mojom::UserWindowPtr GetUserWindow(mus::Window* window) { } // namespace -// Observes title changes on user windows. UserWindowControllerImpl uses this -// separate observer to avoid observing duplicate tree change notifications. -class WindowTitleObserver : public mus::WindowObserver { +// Observes property changes on user windows. UserWindowControllerImpl uses +// this separate observer to avoid observing duplicate tree change +// notifications. +class WindowPropertyObserver : public mus::WindowObserver { public: - explicit WindowTitleObserver(UserWindowControllerImpl* controller) + explicit WindowPropertyObserver(UserWindowControllerImpl* controller) : controller_(controller) {} - ~WindowTitleObserver() override {} + ~WindowPropertyObserver() override {} private: // mus::WindowObserver: @@ -61,15 +75,20 @@ class WindowTitleObserver : public mus::WindowObserver { const std::string& name, const std::vector<uint8_t>* old_data, const std::vector<uint8_t>* new_data) override { - if (controller_->user_window_observer() && - name == mus::mojom::WindowManager::kWindowTitle_Property) { + if (!controller_->user_window_observer()) + return; + if (name == mus::mojom::WindowManager::kWindowTitle_Property) { controller_->user_window_observer()->OnUserWindowTitleChanged( window->id(), GetWindowTitle(window)); + } else if (name == mus::mojom::WindowManager::kWindowAppIcon_Property) { + controller_->user_window_observer()->OnUserWindowAppIconChanged( + window->id(), new_data ? mojo::Array<uint8_t>::From(*new_data) + : mojo::Array<uint8_t>()); } } UserWindowControllerImpl* controller_; - DISALLOW_COPY_AND_ASSIGN(WindowTitleObserver); + DISALLOW_COPY_AND_ASSIGN(WindowPropertyObserver); }; UserWindowControllerImpl::UserWindowControllerImpl() @@ -87,7 +106,7 @@ UserWindowControllerImpl::~UserWindowControllerImpl() { user_container->RemoveObserver(this); for (auto iter : user_container->children()) - iter->RemoveObserver(window_title_observer_.get()); + iter->RemoveObserver(window_property_observer_.get()); } void UserWindowControllerImpl::Initialize( @@ -97,9 +116,9 @@ void UserWindowControllerImpl::Initialize( root_controller_ = root_controller; GetUserWindowContainer()->AddObserver(this); GetUserWindowContainer()->connection()->AddObserver(this); - window_title_observer_.reset(new WindowTitleObserver(this)); + window_property_observer_.reset(new WindowPropertyObserver(this)); for (auto iter : GetUserWindowContainer()->children()) - iter->AddObserver(window_title_observer_.get()); + iter->AddObserver(window_property_observer_.get()); } mus::Window* UserWindowControllerImpl::GetUserWindowContainer() const { @@ -110,11 +129,11 @@ mus::Window* UserWindowControllerImpl::GetUserWindowContainer() const { void UserWindowControllerImpl::OnTreeChanging(const TreeChangeParams& params) { DCHECK(root_controller_); if (params.new_parent == GetUserWindowContainer()) { - params.target->AddObserver(window_title_observer_.get()); + params.target->AddObserver(window_property_observer_.get()); if (user_window_observer_) user_window_observer_->OnUserWindowAdded(GetUserWindow(params.target)); } else if (params.old_parent == GetUserWindowContainer()) { - params.target->RemoveObserver(window_title_observer_.get()); + params.target->RemoveObserver(window_property_observer_.get()); if (user_window_observer_) user_window_observer_->OnUserWindowRemoved(params.target->id()); } diff --git a/mash/wm/user_window_controller_impl.h b/mash/wm/user_window_controller_impl.h index 3b6f9c4..c974f6f 100644 --- a/mash/wm/user_window_controller_impl.h +++ b/mash/wm/user_window_controller_impl.h @@ -17,7 +17,7 @@ namespace mash { namespace wm { class RootWindowController; -class WindowTitleObserver; +class WindowPropertyObserver; class UserWindowControllerImpl : public mojom::UserWindowController, public mus::WindowObserver, @@ -49,7 +49,7 @@ class UserWindowControllerImpl : public mojom::UserWindowController, RootWindowController* root_controller_; mojom::UserWindowObserverPtr user_window_observer_; - scoped_ptr<WindowTitleObserver> window_title_observer_; + scoped_ptr<WindowPropertyObserver> window_property_observer_; DISALLOW_COPY_AND_ASSIGN(UserWindowControllerImpl); }; diff --git a/mash/wm/window_manager.cc b/mash/wm/window_manager.cc index 5905469f..4ebeb9a 100644 --- a/mash/wm/window_manager.cc +++ b/mash/wm/window_manager.cc @@ -153,6 +153,7 @@ bool WindowManager::OnWmSetProperty( return name == mus::mojom::WindowManager::kShowState_Property || name == mus::mojom::WindowManager::kPreferredSize_Property || name == mus::mojom::WindowManager::kResizeBehavior_Property || + name == mus::mojom::WindowManager::kWindowAppIcon_Property || name == mus::mojom::WindowManager::kWindowTitle_Property; } diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn index 0c51bef..6ad0729 100644 --- a/ui/views/mus/BUILD.gn +++ b/ui/views/mus/BUILD.gn @@ -149,6 +149,7 @@ test("views_mus_unittests") { "../view_targeter_unittest.cc", "../widget/native_widget_unittest.cc", "../widget/widget_unittest.cc", + "native_widget_mus_unittest.cc", "platform_test_helper_mus.cc", "run_all_unittests_mus.cc", ] @@ -159,6 +160,8 @@ test("views_mus_unittests") { "//base:i18n", "//base/test:test_support", "//cc", + "//components/mus/public/cpp", + "//components/mus/public/interfaces", "//mojo/shell/background:lib", "//mojo/shell/background:main", "//mojo/shell/background/tests:test_support", diff --git a/ui/views/mus/native_widget_mus.cc b/ui/views/mus/native_widget_mus.cc index 10fa385..9b99d6b 100644 --- a/ui/views/mus/native_widget_mus.cc +++ b/ui/views/mus/native_widget_mus.cc @@ -196,6 +196,17 @@ int ResizeBehaviorFromDelegate(WidgetDelegate* delegate) { return behavior; } +// Returns the 1x window app icon or an empty SkBitmap if no icon is available. +// TODO(jamescook): Support other scale factors. +SkBitmap AppIconFromDelegate(WidgetDelegate* delegate) { + if (!delegate) + return SkBitmap(); + gfx::ImageSkia app_icon = delegate->GetWindowAppIcon(); + if (app_icon.isNull()) + return SkBitmap(); + return app_icon.GetRepresentation(1.f).sk_bitmap(); +} + } // namespace class NativeWidgetMus::MusWindowObserver : public mus::WindowObserver { @@ -352,6 +363,12 @@ void NativeWidgetMus::ConfigurePropertiesForNewWindow( (*properties)[mus::mojom::WindowManager::kResizeBehavior_Property] = mojo::TypeConverter<const std::vector<uint8_t>, int32_t>::Convert( ResizeBehaviorFromDelegate(init_params.delegate)); + SkBitmap app_icon = AppIconFromDelegate(init_params.delegate); + if (!app_icon.isNull()) { + (*properties)[mus::mojom::WindowManager::kWindowAppIcon_Property] = + mojo::TypeConverter<const std::vector<uint8_t>, SkBitmap>::Convert( + app_icon); + } } //////////////////////////////////////////////////////////////////////////////// @@ -542,7 +559,18 @@ bool NativeWidgetMus::SetWindowTitle(const base::string16& title) { void NativeWidgetMus::SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { - // NOTIMPLEMENTED(); + const char* const kWindowAppIcon_Property = + mus::mojom::WindowManager::kWindowAppIcon_Property; + + if (!app_icon.isNull()) { + // Send the app icon 1x bitmap to the window manager. + // TODO(jamescook): Support other scale factors. + window_->SetSharedProperty<SkBitmap>( + kWindowAppIcon_Property, app_icon.GetRepresentation(1.f).sk_bitmap()); + } else if (window_->HasSharedProperty(kWindowAppIcon_Property)) { + // Remove the existing icon. + window_->ClearSharedProperty(kWindowAppIcon_Property); + } } void NativeWidgetMus::InitModalType(ui::ModalType modal_type) { diff --git a/ui/views/mus/native_widget_mus_unittest.cc b/ui/views/mus/native_widget_mus_unittest.cc new file mode 100644 index 0000000..a2ff4b0 --- /dev/null +++ b/ui/views/mus/native_widget_mus_unittest.cc @@ -0,0 +1,128 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/views/mus/native_widget_mus.h" + +#include "base/macros.h" +#include "components/mus/public/cpp/property_type_converters.h" +#include "components/mus/public/cpp/window.h" +#include "components/mus/public/cpp/window_property.h" +#include "components/mus/public/interfaces/window_manager.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace views { +namespace { + +// Returns a small colored bitmap. +SkBitmap MakeBitmap(SkColor color) { + SkBitmap bitmap; + bitmap.allocN32Pixels(8, 8); + bitmap.eraseColor(color); + return bitmap; +} + +// A WidgetDelegate that supplies an app icon. +class TestWidgetDelegate : public WidgetDelegateView { + public: + explicit TestWidgetDelegate(const SkBitmap& icon) + : app_icon_(gfx::ImageSkia::CreateFrom1xBitmap(icon)) {} + + ~TestWidgetDelegate() override {} + + void SetIcon(const SkBitmap& icon) { + app_icon_ = gfx::ImageSkia::CreateFrom1xBitmap(icon); + } + + // views::WidgetDelegate: + gfx::ImageSkia GetWindowAppIcon() override { return app_icon_; } + + private: + gfx::ImageSkia app_icon_; + + DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); +}; + +class NativeWidgetMusTest : public ViewsTestBase { + public: + NativeWidgetMusTest() {} + ~NativeWidgetMusTest() override {} + + // Creates a test widget. Takes ownership of |delegate|. + Widget* CreateWidget(TestWidgetDelegate* delegate) { + Widget* widget = new Widget(); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.delegate = delegate; + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(10, 20, 100, 200); + widget->Init(params); + return widget; + } + + private: + DISALLOW_COPY_AND_ASSIGN(NativeWidgetMusTest); +}; + +// Tests that a window with an icon sets the mus::Window icon property. +TEST_F(NativeWidgetMusTest, AppIcon) { + // Create a Widget with a bitmap as the icon. + SkBitmap source_bitmap = MakeBitmap(SK_ColorRED); + scoped_ptr<Widget> widget( + CreateWidget(new TestWidgetDelegate(source_bitmap))); + + // The mus::Window has the icon property. + mus::Window* window = + static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); + EXPECT_TRUE(window->HasSharedProperty( + mus::mojom::WindowManager::kWindowAppIcon_Property)); + + // The icon is the expected icon. + SkBitmap icon = window->GetSharedProperty<SkBitmap>( + mus::mojom::WindowManager::kWindowAppIcon_Property); + EXPECT_TRUE(gfx::BitmapsAreEqual(source_bitmap, icon)); +} + +// Tests that a window without an icon does not set the mus::Window icon +// property. +TEST_F(NativeWidgetMusTest, NoAppIcon) { + // Create a Widget without a special icon. + scoped_ptr<Widget> widget(CreateWidget(nullptr)); + + // The mus::Window does not have an icon property. + mus::Window* window = + static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); + EXPECT_FALSE(window->HasSharedProperty( + mus::mojom::WindowManager::kWindowAppIcon_Property)); +} + +// Tests that changing the icon on a Widget updates the mus::Window icon +// property. +TEST_F(NativeWidgetMusTest, ChangeAppIcon) { + // Create a Widget with an icon. + SkBitmap bitmap1 = MakeBitmap(SK_ColorRED); + TestWidgetDelegate* delegate = new TestWidgetDelegate(bitmap1); + scoped_ptr<Widget> widget(CreateWidget(delegate)); + + // Update the icon to a new image. + SkBitmap bitmap2 = MakeBitmap(SK_ColorGREEN); + delegate->SetIcon(bitmap2); + widget->UpdateWindowIcon(); + + // The window has the updated icon. + mus::Window* window = + static_cast<NativeWidgetMus*>(widget->native_widget_private())->window(); + SkBitmap icon = window->GetSharedProperty<SkBitmap>( + mus::mojom::WindowManager::kWindowAppIcon_Property); + EXPECT_TRUE(gfx::BitmapsAreEqual(bitmap2, icon)); +} + +} // namespace +} // namespace views |