| // Copyright (c) 2010 The Chromium OS 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 "window_manager/shadow.h" |
| |
| #include <cmath> |
| #include <utility> |
| |
| #include "unistd.h" |
| |
| #include <gflags/gflags.h> |
| |
| #include "base/singleton.h" |
| |
| DEFINE_string(panel_content_shadow_image_dir, |
| "../assets/images/panel_content_shadow", |
| "Directory containing images for panel content shadows"); |
| DEFINE_string(panel_separator_shadow_image_dir, |
| "../assets/images/panel_separator_shadow", |
| "Directory containing images for shadow cast by panel " |
| "titlebars onto their content windows"); |
| DEFINE_string(panel_titlebar_shadow_image_dir, |
| "../assets/images/panel_titlebar_shadow", |
| "Directory containing images for panel titlebar shadows"); |
| DEFINE_string(rectangular_shadow_image_dir, |
| "../assets/images/rectangular_shadow", |
| "Directory containing images for rectangular shadows"); |
| |
| using std::make_pair; |
| using std::string; |
| using std::tr1::shared_ptr; |
| |
| namespace window_manager { |
| |
| // Names of the different image files that we expect to find in a directory. |
| static const char* kTopFilename = "top.png"; |
| static const char* kBottomFilename = "bottom.png"; |
| static const char* kLeftFilename = "left.png"; |
| static const char* kRightFilename = "right.png"; |
| static const char* kTopLeftFilename = "top_left.png"; |
| static const char* kTopRightFilename = "top_right.png"; |
| static const char* kBottomLeftFilename = "bottom_left.png"; |
| static const char* kBottomRightFilename = "bottom_right.png"; |
| |
| // static |
| Shadow* Shadow::Create(Compositor* compositor, Type type) { |
| return Singleton<Shadow::Factory>::get()->CreateShadow(compositor, type); |
| } |
| |
| void Shadow::Show() { |
| is_shown_ = true; |
| group_->Show(); |
| } |
| |
| void Shadow::Hide() { |
| is_shown_ = false; |
| group_->Hide(); |
| } |
| |
| void Shadow::Move(int x, int y, int anim_ms) { |
| x_ = x; |
| y_ = y; |
| group_->Move(x, y, anim_ms); |
| } |
| |
| void Shadow::MoveX(int x, int anim_ms) { |
| x_ = x; |
| group_->MoveX(x, anim_ms); |
| } |
| |
| void Shadow::MoveY(int y, int anim_ms) { |
| y_ = y; |
| group_->MoveY(y, anim_ms); |
| } |
| |
| void Shadow::Resize(int width, int height, int anim_ms) { |
| width_ = width; |
| height_ = height; |
| |
| // TODO: Figure out what to do for windows that are too small for these |
| // images -- currently, we'll try to scale them to negative values. |
| if (top_actor_.get()) { |
| top_actor_->Move(left_inset_, -top_height_, anim_ms); |
| top_actor_->Scale(width - left_inset_ - right_inset_, 1.0, anim_ms); |
| } |
| if (bottom_actor_.get()) { |
| bottom_actor_->Move(left_inset_, height, anim_ms); |
| bottom_actor_->Scale(width - left_inset_ - right_inset_, 1.0, anim_ms); |
| } |
| if (left_actor_.get()) { |
| left_actor_->Move(-left_width_, top_inset_, anim_ms); |
| left_actor_->Scale(1.0, height - top_inset_ - bottom_inset_, anim_ms); |
| } |
| if (right_actor_.get()) { |
| right_actor_->Move(width, top_inset_, anim_ms); |
| right_actor_->Scale(1.0, height - top_inset_ - bottom_inset_, anim_ms); |
| } |
| |
| if (top_left_actor_.get()) |
| top_left_actor_->Move(-left_width_, -top_height_, anim_ms); |
| if (top_right_actor_.get()) |
| top_right_actor_->Move(width - right_inset_, -top_height_, anim_ms); |
| if (bottom_left_actor_.get()) |
| bottom_left_actor_->Move(-left_width_, height - bottom_inset_, anim_ms); |
| if (bottom_right_actor_.get()) |
| bottom_right_actor_->Move( |
| width - right_inset_, height - bottom_inset_, anim_ms); |
| } |
| |
| void Shadow::SetOpacity(double opacity, int anim_ms) { |
| opacity_ = opacity; |
| group_->SetOpacity(opacity, anim_ms); |
| } |
| |
| |
| Shadow* Shadow::Factory::CreateShadow(Compositor* compositor, Type type) { |
| PrototypeMap::iterator it = prototypes_.find(type); |
| if (it == prototypes_.end()) { |
| string images_dir; |
| switch (type) { |
| case TYPE_RECTANGULAR: |
| images_dir = FLAGS_rectangular_shadow_image_dir; |
| break; |
| case TYPE_PANEL_TITLEBAR: |
| images_dir = FLAGS_panel_titlebar_shadow_image_dir; |
| break; |
| case TYPE_PANEL_CONTENT: |
| images_dir = FLAGS_panel_content_shadow_image_dir; |
| break; |
| case TYPE_PANEL_SEPARATOR: |
| images_dir = FLAGS_panel_separator_shadow_image_dir; |
| break; |
| default: |
| NOTREACHED() << "Unknown shadow type " << type; |
| } |
| shared_ptr<Shadow> prototype(new Shadow(compositor)); |
| prototype->InitAsPrototypeFromDisk(images_dir); |
| it = prototypes_.insert(make_pair(type, prototype)).first; |
| } |
| |
| Shadow* shadow = new Shadow(compositor); |
| shadow->InitFromPrototype(it->second.get()); |
| return shadow; |
| } |
| |
| |
| Shadow::Shadow(Compositor* compositor) |
| : compositor_(compositor), |
| is_shown_(false), |
| opacity_(1.0), |
| x_(0), |
| y_(0), |
| width_(0), |
| height_(0) { |
| } |
| |
| void Shadow::InitAsPrototypeFromDisk(const string& images_dir) { |
| top_actor_.reset(CreateActor(images_dir, kTopFilename)); |
| bottom_actor_.reset(CreateActor(images_dir, kBottomFilename)); |
| left_actor_.reset(CreateActor(images_dir, kLeftFilename)); |
| right_actor_.reset(CreateActor(images_dir, kRightFilename)); |
| top_left_actor_.reset(CreateActor(images_dir, kTopLeftFilename)); |
| top_right_actor_.reset(CreateActor(images_dir, kTopRightFilename)); |
| bottom_left_actor_.reset(CreateActor(images_dir, kBottomLeftFilename)); |
| bottom_right_actor_.reset(CreateActor(images_dir, kBottomRightFilename)); |
| |
| top_height_ = top_actor_.get() ? top_actor_->GetHeight() : 0; |
| bottom_height_ = bottom_actor_.get() ? bottom_actor_->GetHeight() : 0; |
| left_width_ = left_actor_.get() ? left_actor_->GetWidth() : 0; |
| right_width_ = right_actor_.get() ? right_actor_->GetWidth() : 0; |
| |
| // If the scalable actors were supplied, make sure that they're just one |
| // pixel across in the dimension that we'll be scaling them. |
| CHECK(!top_actor_.get() || top_actor_->GetWidth() == 1); |
| CHECK(!bottom_actor_.get() || bottom_actor_->GetWidth() == 1); |
| CHECK(!left_actor_.get() || left_actor_->GetHeight() == 1); |
| CHECK(!right_actor_.get() || right_actor_->GetHeight() == 1); |
| |
| // If any two adjacent corner actors were loaded, make sure that they're |
| // the same size on the side that they share. |
| if (top_left_actor_.get() && top_right_actor_.get()) |
| CHECK(top_left_actor_->GetHeight() == top_right_actor_->GetHeight()); |
| if (bottom_left_actor_.get() && bottom_right_actor_.get()) |
| CHECK(bottom_left_actor_->GetHeight() == bottom_right_actor_->GetHeight()); |
| if (top_left_actor_.get() && bottom_left_actor_.get()) |
| CHECK(top_left_actor_->GetWidth() == bottom_left_actor_->GetWidth()); |
| if (top_right_actor_.get() && bottom_right_actor_.get()) |
| CHECK(top_right_actor_->GetWidth() == bottom_right_actor_->GetWidth()); |
| |
| // Now figure out how many pixels of the corner images overlap with the |
| // window on each side. |
| top_inset_ = bottom_inset_ = left_inset_ = right_inset_ = 0; |
| |
| if (top_actor_.get() && left_actor_.get()) { |
| CHECK(top_left_actor_.get()); |
| top_inset_ = top_left_actor_->GetHeight() - top_height_; |
| left_inset_ = top_left_actor_->GetWidth() - left_width_; |
| } else { |
| CHECK(!top_left_actor_.get()); |
| } |
| |
| if (top_actor_.get() && right_actor_.get()) { |
| CHECK(top_right_actor_.get()); |
| top_inset_ = top_right_actor_->GetHeight() - top_height_; |
| right_inset_ = top_right_actor_->GetWidth() - right_width_; |
| } else { |
| CHECK(!top_right_actor_.get()); |
| } |
| |
| if (bottom_actor_.get() && left_actor_.get()) { |
| CHECK(bottom_left_actor_.get()); |
| bottom_inset_ = bottom_left_actor_->GetHeight() - bottom_height_; |
| left_inset_ = bottom_left_actor_->GetWidth() - left_width_; |
| } else { |
| CHECK(!bottom_left_actor_.get()); |
| } |
| |
| if (bottom_actor_.get() && right_actor_.get()) { |
| CHECK(bottom_right_actor_.get()); |
| bottom_inset_ = bottom_right_actor_->GetHeight() - bottom_height_; |
| right_inset_ = bottom_right_actor_->GetWidth() - right_width_; |
| } else { |
| CHECK(!bottom_right_actor_.get()); |
| } |
| } |
| |
| void Shadow::InitFromPrototype(Shadow* prototype) { |
| DCHECK(prototype); |
| |
| group_.reset(compositor_->CreateGroup()); |
| group_->SetName("shadow group"); |
| |
| if (prototype->top_actor_.get()) { |
| top_actor_.reset(compositor_->CloneActor(prototype->top_actor_.get())); |
| top_actor_->SetName("shadow top"); |
| top_actor_->Show(); |
| group_->AddActor(top_actor_.get()); |
| } |
| if (prototype->bottom_actor_.get()) { |
| bottom_actor_.reset( |
| compositor_->CloneActor(prototype->bottom_actor_.get())); |
| bottom_actor_->SetName("shadow bottom"); |
| bottom_actor_->Show(); |
| group_->AddActor(bottom_actor_.get()); |
| } |
| if (prototype->left_actor_.get()) { |
| left_actor_.reset(compositor_->CloneActor(prototype->left_actor_.get())); |
| left_actor_->SetName("shadow left"); |
| left_actor_->Show(); |
| group_->AddActor(left_actor_.get()); |
| } |
| if (prototype->right_actor_.get()) { |
| right_actor_.reset(compositor_->CloneActor(prototype->right_actor_.get())); |
| right_actor_->SetName("shadow right"); |
| right_actor_->Show(); |
| group_->AddActor(right_actor_.get()); |
| } |
| if (prototype->top_left_actor_.get()) { |
| top_left_actor_.reset( |
| compositor_->CloneActor(prototype->top_left_actor_.get())); |
| top_left_actor_->SetName("shadow top left"); |
| top_left_actor_->Show(); |
| group_->AddActor(top_left_actor_.get()); |
| } |
| if (prototype->top_right_actor_.get()) { |
| top_right_actor_.reset( |
| compositor_->CloneActor(prototype->top_right_actor_.get())); |
| top_right_actor_->SetName("shadow top right"); |
| top_right_actor_->Show(); |
| group_->AddActor(top_right_actor_.get()); |
| } |
| if (prototype->bottom_left_actor_.get()) { |
| bottom_left_actor_.reset( |
| compositor_->CloneActor(prototype->bottom_left_actor_.get())); |
| bottom_left_actor_->SetName("shadow bottom left"); |
| bottom_left_actor_->Show(); |
| group_->AddActor(bottom_left_actor_.get()); |
| } |
| if (prototype->bottom_right_actor_.get()) { |
| bottom_right_actor_.reset( |
| compositor_->CloneActor(prototype->bottom_right_actor_.get())); |
| bottom_right_actor_->SetName("shadow bottom right"); |
| bottom_right_actor_->Show(); |
| group_->AddActor(bottom_right_actor_.get()); |
| } |
| |
| top_height_ = prototype->top_height_; |
| bottom_height_ = prototype->bottom_height_; |
| left_width_ = prototype->left_width_; |
| right_width_ = prototype->right_width_; |
| |
| top_inset_ = prototype->top_inset_; |
| bottom_inset_ = prototype->bottom_inset_; |
| left_inset_ = prototype->left_inset_; |
| right_inset_ = prototype->right_inset_; |
| |
| // Resize the shadow arbitrarily to initialize the positions of the actors. |
| Resize(10, 10, 0); |
| SetOpacity(1.0, 0); |
| Hide(); |
| } |
| |
| Compositor::ImageActor* Shadow::CreateActor(const string& images_dir, |
| const string& filename) { |
| const string path = images_dir + "/" + filename; |
| if (access(path.c_str(), R_OK) != 0) |
| return NULL; |
| return compositor_->CreateImageFromFile(path); |
| } |
| |
| } // namespace window_manager |