drag_image_view.cc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/drag_drop/drag_image_view.h"
  5. #include <memory>
  6. #include "ash/public/cpp/shell_window_ids.h"
  7. #include "base/memory/ptr_util.h"
  8. #include "skia/ext/image_operations.h"
  9. #include "ui/aura/window.h"
  10. #include "ui/base/dragdrop/drag_drop_types.h"
  11. #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
  12. #include "ui/base/resource/resource_bundle.h"
  13. #include "ui/display/display.h"
  14. #include "ui/display/screen.h"
  15. #include "ui/gfx/canvas.h"
  16. #include "ui/gfx/image/image_skia_rep.h"
  17. #include "ui/resources/grit/ui_resources.h"
  18. #include "ui/views/widget/widget.h"
  19. namespace ash {
  20. DragImageView::DragImageView(ui::mojom::DragEventSource event_source)
  21. : drag_event_source_(event_source) {}
  22. DragImageView::~DragImageView() = default;
  23. // static
  24. views::UniqueWidgetPtr DragImageView::Create(
  25. aura::Window* root_window,
  26. ui::mojom::DragEventSource event_source) {
  27. views::Widget::InitParams params;
  28. params.type = views::Widget::InitParams::TYPE_TOOLTIP;
  29. params.name = "DragWidget";
  30. params.accept_events = false;
  31. params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
  32. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  33. params.parent =
  34. root_window->GetChildById(kShellWindowId_DragImageAndTooltipContainer);
  35. if (!params.parent)
  36. params.context = root_window; // Happens in tests.
  37. auto drag_widget = views::UniqueWidgetPtr(
  38. std::make_unique<views::Widget>(std::move(params)));
  39. drag_widget->SetOpacity(1.f);
  40. drag_widget->SetContentsView(
  41. base::WrapUnique(new DragImageView(event_source)));
  42. return drag_widget;
  43. }
  44. void DragImageView::SetBoundsInScreen(const gfx::Rect& bounds) {
  45. drag_image_size_ = bounds.size();
  46. GetWidget()->SetBounds(bounds);
  47. }
  48. void DragImageView::SetScreenPosition(const gfx::Point& position) {
  49. GetWidget()->SetBounds(
  50. gfx::Rect(position, GetWidget()->GetWindowBoundsInScreen().size()));
  51. }
  52. gfx::Rect DragImageView::GetBoundsInScreen() const {
  53. return GetWidget()->GetWindowBoundsInScreen();
  54. }
  55. void DragImageView::SetWidgetVisible(bool visible) {
  56. auto* widget = GetWidget();
  57. if (visible != widget->IsVisible()) {
  58. if (visible)
  59. widget->Show();
  60. else
  61. widget->Hide();
  62. }
  63. }
  64. void DragImageView::SetTouchDragOperationHintOff() {
  65. // Simply set the drag type to non-touch so that no hint is drawn.
  66. drag_event_source_ = ui::mojom::DragEventSource::kMouse;
  67. // This disables the drag hint image. This should reduce the widget size if
  68. // the drag image is smaller than the drag hint image, so we set new bounds.
  69. gfx::Rect new_bounds = GetBoundsInScreen();
  70. new_bounds.set_size(drag_image_size_);
  71. SetBoundsInScreen(new_bounds);
  72. SchedulePaint();
  73. }
  74. void DragImageView::SetTouchDragOperation(int operation) {
  75. if (touch_drag_operation_ == operation)
  76. return;
  77. touch_drag_operation_ = operation;
  78. SchedulePaint();
  79. }
  80. void DragImageView::SetTouchDragOperationHintPosition(
  81. const gfx::Point& position) {
  82. if (touch_drag_operation_indicator_position_ == position)
  83. return;
  84. touch_drag_operation_indicator_position_ = position;
  85. SchedulePaint();
  86. }
  87. void DragImageView::SetOpacity(float visibility) {
  88. DCHECK_GE(visibility, 0.0f);
  89. DCHECK_LE(visibility, 1.0f);
  90. GetWidget()->SetOpacity(visibility);
  91. }
  92. void DragImageView::OnPaint(gfx::Canvas* canvas) {
  93. if (GetImage().isNull())
  94. return;
  95. // |drag_image_size_| is in DIP.
  96. // ImageSkia::size() also returns the size in DIP.
  97. if (GetImage().size() == drag_image_size_) {
  98. canvas->DrawImageInt(GetImage(), 0, 0);
  99. } else {
  100. aura::Window* window = GetWidget()->GetNativeWindow();
  101. const float device_scale = display::Screen::GetScreen()
  102. ->GetDisplayNearestWindow(window)
  103. .device_scale_factor();
  104. // The drag image already has device scale factor applied. But
  105. // |drag_image_size_| is in DIP units.
  106. gfx::Size drag_image_size_pixels =
  107. gfx::ScaleToRoundedSize(drag_image_size_, device_scale);
  108. gfx::ImageSkiaRep image_rep = GetImage().GetRepresentation(device_scale);
  109. if (image_rep.is_null())
  110. return;
  111. SkBitmap scaled = skia::ImageOperations::Resize(
  112. image_rep.GetBitmap(), skia::ImageOperations::RESIZE_LANCZOS3,
  113. drag_image_size_pixels.width(), drag_image_size_pixels.height());
  114. gfx::ImageSkia image_skia =
  115. gfx::ImageSkia::CreateFromBitmap(scaled, device_scale);
  116. canvas->DrawImageInt(image_skia, 0, 0);
  117. }
  118. gfx::Image* drag_hint = DragHint();
  119. if (!ShouldDrawDragHint() || drag_hint->IsEmpty())
  120. return;
  121. // Make sure drag hint image is positioned within the widget.
  122. gfx::Size drag_hint_size = drag_hint->Size();
  123. gfx::Point drag_hint_position = touch_drag_operation_indicator_position_;
  124. drag_hint_position.Offset(-drag_hint_size.width() / 2, 0);
  125. gfx::Rect drag_hint_bounds(drag_hint_position, drag_hint_size);
  126. gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
  127. drag_hint_bounds.AdjustToFit(gfx::Rect(widget_size));
  128. // Draw image.
  129. canvas->DrawImageInt(*(drag_hint->ToImageSkia()), drag_hint_bounds.x(),
  130. drag_hint_bounds.y());
  131. }
  132. gfx::Image* DragImageView::DragHint() const {
  133. // Select appropriate drag hint.
  134. gfx::Image* drag_hint =
  135. &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
  136. IDR_TOUCH_DRAG_TIP_NODROP);
  137. if (touch_drag_operation_ & ui::DragDropTypes::DRAG_COPY) {
  138. drag_hint = &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
  139. IDR_TOUCH_DRAG_TIP_COPY);
  140. } else if (touch_drag_operation_ & ui::DragDropTypes::DRAG_MOVE) {
  141. drag_hint = &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
  142. IDR_TOUCH_DRAG_TIP_MOVE);
  143. } else if (touch_drag_operation_ & ui::DragDropTypes::DRAG_LINK) {
  144. drag_hint = &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
  145. IDR_TOUCH_DRAG_TIP_LINK);
  146. }
  147. return drag_hint;
  148. }
  149. bool DragImageView::ShouldDrawDragHint() const {
  150. return drag_event_source_ == ui::mojom::DragEventSource::kTouch;
  151. }
  152. gfx::Size DragImageView::GetMinimumSize() const {
  153. gfx::Size minimum_size = drag_image_size_;
  154. if (ShouldDrawDragHint())
  155. minimum_size.SetToMax(DragHint()->Size());
  156. return minimum_size;
  157. }
  158. void DragImageView::Layout() {
  159. View::Layout();
  160. // Only consider resizing the widget for the drag hint image if we are in a
  161. // touch initiated drag.
  162. gfx::Image* drag_hint = DragHint();
  163. if (!ShouldDrawDragHint() || drag_hint->IsEmpty())
  164. return;
  165. gfx::Size drag_hint_size = drag_hint->Size();
  166. // Enlarge widget if required to fit the drag hint image.
  167. gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
  168. if (drag_hint_size.width() > widget_size.width() ||
  169. drag_hint_size.height() > widget_size.height()) {
  170. widget_size.SetToMax(drag_hint_size);
  171. GetWidget()->SetSize(widget_size);
  172. }
  173. }
  174. } // namespace ash