// Copyright (c) 2012 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 "ash/drag_drop/drag_image_view.h" #include "grit/ui_resources.h" #include "skia/ext/image_operations.h" #include "ui/aura/window.h" #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/dip_util.h" #include "ui/gfx/canvas.h" #include "ui/gfx/size_conversions.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/shadow_types.h" namespace ash { namespace { using views::Widget; Widget* CreateDragWidget(gfx::NativeView context) { Widget* drag_widget = new Widget; Widget::InitParams params; params.type = Widget::InitParams::TYPE_TOOLTIP; params.keep_on_top = true; params.context = context; params.accept_events = false; params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW; drag_widget->Init(params); drag_widget->SetOpacity(0xFF); drag_widget->GetNativeWindow()->set_owned_by_parent(false); drag_widget->GetNativeWindow()->SetName("DragWidget"); SetShadowType(drag_widget->GetNativeView(), wm::SHADOW_TYPE_NONE); return drag_widget; } } DragImageView::DragImageView(gfx::NativeView context, ui::DragDropTypes::DragEventSource event_source) : views::ImageView(), drag_event_source_(event_source), touch_drag_operation_(ui::DragDropTypes::DRAG_NONE) { widget_.reset(CreateDragWidget(context)); widget_->SetContentsView(this); widget_->SetAlwaysOnTop(true); // We are owned by the DragDropController. set_owned_by_client(); } DragImageView::~DragImageView() { widget_->Hide(); } void DragImageView::SetBoundsInScreen(const gfx::Rect& bounds) { widget_->SetBounds(bounds); widget_size_ = bounds.size(); } void DragImageView::SetScreenPosition(const gfx::Point& position) { widget_->SetBounds(gfx::Rect(position, widget_size_)); } gfx::Rect DragImageView::GetBoundsInScreen() const { return widget_->GetWindowBoundsInScreen(); } void DragImageView::SetWidgetVisible(bool visible) { if (visible != widget_->IsVisible()) { if (visible) widget_->Show(); else widget_->Hide(); } } void DragImageView::SetTouchDragOperationHintOff() { // Simply set the drag type to non-touch so that no hint is drawn. drag_event_source_ = ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE; SchedulePaint(); } void DragImageView::SetTouchDragOperation(int operation) { if (touch_drag_operation_ == operation) return; touch_drag_operation_ = operation; SchedulePaint(); } void DragImageView::SetTouchDragOperationHintPosition( const gfx::Point& position) { if (touch_drag_operation_indicator_position_ == position) return; touch_drag_operation_indicator_position_ = position; SchedulePaint(); } void DragImageView::SetOpacity(float visibility) { DCHECK_GE(visibility, 0.0f); DCHECK_LE(visibility, 1.0f); widget_->SetOpacity(static_cast<int>(0xff * visibility)); } void DragImageView::OnPaint(gfx::Canvas* canvas) { if (GetImage().isNull()) return; // |widget_size_| is in DIP. ImageSkia::size() also returns the size in DIP. if (GetImage().size() == widget_size_) { canvas->DrawImageInt(GetImage(), 0, 0); } else { float device_scale = 1; if (widget_->GetNativeView() && widget_->GetNativeView()->layer()) { device_scale = ui::GetDeviceScaleFactor( widget_->GetNativeView()->layer()); } // The drag image already has device scale factor applied. But // |widget_size_| is in DIP units. gfx::Size scaled_widget_size = gfx::ToRoundedSize( gfx::ScaleSize(widget_size_, device_scale)); gfx::ImageSkiaRep image_rep = GetImage().GetRepresentation(device_scale); if (image_rep.is_null()) return; SkBitmap scaled = skia::ImageOperations::Resize( image_rep.sk_bitmap(), skia::ImageOperations::RESIZE_LANCZOS3, scaled_widget_size.width(), scaled_widget_size.height()); gfx::ImageSkia image_skia(gfx::ImageSkiaRep(scaled, device_scale)); canvas->DrawImageInt(image_skia, 0, 0); } if (drag_event_source_ != ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) return; // Select appropriate drag hint. gfx::Image* drag_hint = &ui::ResourceBundle::GetSharedInstance().GetImageNamed( IDR_TOUCH_DRAG_TIP_NODROP); if (touch_drag_operation_ & ui::DragDropTypes::DRAG_COPY) { drag_hint = &ui::ResourceBundle::GetSharedInstance().GetImageNamed( IDR_TOUCH_DRAG_TIP_COPY); } else if (touch_drag_operation_ & ui::DragDropTypes::DRAG_MOVE) { drag_hint = &ui::ResourceBundle::GetSharedInstance().GetImageNamed( IDR_TOUCH_DRAG_TIP_MOVE); } else if (touch_drag_operation_ & ui::DragDropTypes::DRAG_LINK) { drag_hint = &ui::ResourceBundle::GetSharedInstance().GetImageNamed( IDR_TOUCH_DRAG_TIP_LINK); } if (!drag_hint->IsEmpty()) { gfx::Size drag_hint_size = drag_hint->Size(); // Enlarge widget if required to fit the drag hint image. if (drag_hint_size.width() > widget_size_.width() || drag_hint_size.height() > widget_size_.height()) { gfx::Size new_widget_size = widget_size_; new_widget_size.SetToMax(drag_hint_size); widget_->SetSize(new_widget_size); } // Make sure drag hint image is positioned within the widget. gfx::Point drag_hint_position = touch_drag_operation_indicator_position_; drag_hint_position.Offset(-drag_hint_size.width() / 2, 0); gfx::Rect drag_hint_bounds(drag_hint_position, drag_hint_size); drag_hint_bounds.AdjustToFit(gfx::Rect(widget_size_)); // Draw image. canvas->DrawImageInt(*(drag_hint->ToImageSkia()), drag_hint_bounds.x(), drag_hint_bounds.y()); } } } // namespace ash