AbstractZoomPanWidget.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * Copyright (c) 2022, Mustafa Quraish <mustafa@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "AbstractZoomPanWidget.h"
  7. namespace GUI {
  8. constexpr float wheel_zoom_factor = 8.0f;
  9. void AbstractZoomPanWidget::set_scale(float new_scale)
  10. {
  11. if (m_original_rect.is_null())
  12. return;
  13. m_scale = clamp(new_scale, m_min_scale, m_max_scale);
  14. Gfx::IntSize new_size;
  15. new_size.set_width(m_original_rect.width() * m_scale);
  16. new_size.set_height(m_original_rect.height() * m_scale);
  17. m_content_rect.set_size(new_size);
  18. if (on_scale_change)
  19. on_scale_change(m_scale);
  20. relayout();
  21. }
  22. void AbstractZoomPanWidget::scale_by(float delta)
  23. {
  24. float new_scale = m_scale * AK::exp2(delta);
  25. set_scale(new_scale);
  26. }
  27. void AbstractZoomPanWidget::scale_centered(float new_scale, Gfx::IntPoint const& center)
  28. {
  29. if (m_original_rect.is_null())
  30. return;
  31. new_scale = clamp(new_scale, m_min_scale, m_max_scale);
  32. if (new_scale == m_scale)
  33. return;
  34. Gfx::FloatPoint focus_point {
  35. center.x() - width() / 2.0f,
  36. center.y() - height() / 2.0f
  37. };
  38. m_origin = (m_origin + focus_point) * (new_scale / m_scale) - focus_point;
  39. set_scale(new_scale);
  40. }
  41. void AbstractZoomPanWidget::start_panning(Gfx::IntPoint const& position)
  42. {
  43. m_saved_cursor = override_cursor();
  44. set_override_cursor(Gfx::StandardCursor::Drag);
  45. m_pan_start = m_origin;
  46. m_pan_mouse_pos = position;
  47. m_is_panning = true;
  48. }
  49. void AbstractZoomPanWidget::stop_panning()
  50. {
  51. m_is_panning = false;
  52. set_override_cursor(m_saved_cursor);
  53. }
  54. void AbstractZoomPanWidget::pan_to(Gfx::IntPoint const& position)
  55. {
  56. // NOTE: `position` here (and `m_pan_mouse_pos`) are both in frame coordinates, not
  57. // content coordinates, by design. The derived class should not have to keep track of
  58. // the (zoomed) content coordinates itself, but just pass along the mouse position.
  59. auto delta = position - m_pan_mouse_pos;
  60. m_origin = m_pan_start.translated(-delta.x(), -delta.y());
  61. relayout();
  62. }
  63. Gfx::FloatPoint AbstractZoomPanWidget::frame_to_content_position(Gfx::IntPoint const& frame_position) const
  64. {
  65. Gfx::FloatPoint content_position;
  66. content_position.set_x(((float)frame_position.x() - (float)m_content_rect.x()) / m_scale);
  67. content_position.set_y(((float)frame_position.y() - (float)m_content_rect.y()) / m_scale);
  68. return content_position;
  69. }
  70. Gfx::FloatRect AbstractZoomPanWidget::frame_to_content_rect(Gfx::IntRect const& frame_rect) const
  71. {
  72. Gfx::FloatRect content_rect;
  73. content_rect.set_location(frame_to_content_position(frame_rect.location()));
  74. content_rect.set_width((float)frame_rect.width() / m_scale);
  75. content_rect.set_height((float)frame_rect.height() / m_scale);
  76. return content_rect;
  77. }
  78. Gfx::FloatPoint AbstractZoomPanWidget::content_to_frame_position(Gfx::IntPoint const& content_position) const
  79. {
  80. Gfx::FloatPoint frame_position;
  81. frame_position.set_x(m_content_rect.x() + ((float)content_position.x() * m_scale));
  82. frame_position.set_y(m_content_rect.y() + ((float)content_position.y() * m_scale));
  83. return frame_position;
  84. }
  85. Gfx::FloatRect AbstractZoomPanWidget::content_to_frame_rect(Gfx::IntRect const& content_rect) const
  86. {
  87. Gfx::FloatRect frame_rect;
  88. frame_rect.set_location(content_to_frame_position(content_rect.location()));
  89. frame_rect.set_width((float)content_rect.width() * m_scale);
  90. frame_rect.set_height((float)content_rect.height() * m_scale);
  91. return frame_rect;
  92. }
  93. void AbstractZoomPanWidget::mousewheel_event(GUI::MouseEvent& event)
  94. {
  95. float new_scale = scale() / AK::exp2(event.wheel_delta() / wheel_zoom_factor);
  96. scale_centered(new_scale, event.position());
  97. }
  98. void AbstractZoomPanWidget::mousedown_event(GUI::MouseEvent& event)
  99. {
  100. if (!m_is_panning && event.button() == GUI::MouseButton::Middle) {
  101. start_panning(event.position());
  102. event.accept();
  103. return;
  104. }
  105. }
  106. void AbstractZoomPanWidget::resize_event(GUI::ResizeEvent& event)
  107. {
  108. relayout();
  109. GUI::Widget::resize_event(event);
  110. }
  111. void AbstractZoomPanWidget::mousemove_event(GUI::MouseEvent& event)
  112. {
  113. if (!m_is_panning)
  114. return;
  115. pan_to(event.position());
  116. event.accept();
  117. }
  118. void AbstractZoomPanWidget::mouseup_event(GUI::MouseEvent& event)
  119. {
  120. if (m_is_panning && event.button() == GUI::MouseButton::Middle) {
  121. stop_panning();
  122. event.accept();
  123. return;
  124. }
  125. }
  126. void AbstractZoomPanWidget::relayout()
  127. {
  128. if (m_original_rect.is_null())
  129. return;
  130. Gfx::IntSize new_size = m_content_rect.size();
  131. Gfx::IntPoint new_location;
  132. new_location.set_x((width() / 2) - (new_size.width() / 2) - m_origin.x());
  133. new_location.set_y((height() / 2) - (new_size.height() / 2) - m_origin.y());
  134. m_content_rect.set_location(new_location);
  135. handle_relayout(m_content_rect);
  136. }
  137. void AbstractZoomPanWidget::reset_view()
  138. {
  139. m_origin = { 0, 0 };
  140. set_scale(1.0f);
  141. }
  142. void AbstractZoomPanWidget::set_content_rect(Gfx::IntRect const& content_rect)
  143. {
  144. m_content_rect = enclosing_int_rect(content_to_frame_rect(content_rect));
  145. update();
  146. }
  147. void AbstractZoomPanWidget::set_scale_bounds(float min_scale, float max_scale)
  148. {
  149. m_min_scale = min_scale;
  150. m_max_scale = max_scale;
  151. }
  152. }