GuideTool.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "GuideTool.h"
  7. #include "EditGuideDialog.h"
  8. #include "ImageEditor.h"
  9. #include <LibGUI/Application.h>
  10. #include <LibGUI/BoxLayout.h>
  11. #include <LibGUI/Label.h>
  12. #include <LibGUI/Menu.h>
  13. #include <LibGUI/ValueSlider.h>
  14. #include <LibGUI/Widget.h>
  15. namespace PixelPaint {
  16. GuideTool::GuideTool()
  17. {
  18. }
  19. GuideTool::~GuideTool()
  20. {
  21. }
  22. RefPtr<Guide> GuideTool::closest_guide(const Gfx::IntPoint& point)
  23. {
  24. auto guides = editor()->guides();
  25. Guide* closest_guide = nullptr;
  26. int closest_guide_distance = NumericLimits<int>::max();
  27. for (auto& guide : guides) {
  28. int relevant_position = 0;
  29. if (guide.orientation() == Guide::Orientation::Horizontal)
  30. relevant_position = point.y();
  31. else if (guide.orientation() == Guide::Orientation::Vertical)
  32. relevant_position = point.x();
  33. auto distance = abs(relevant_position - (int)guide.offset());
  34. if (distance < closest_guide_distance) {
  35. closest_guide = &guide;
  36. closest_guide_distance = distance;
  37. }
  38. }
  39. if (closest_guide_distance < 20)
  40. return closest_guide;
  41. return nullptr;
  42. }
  43. void GuideTool::on_mousedown(Layer*, MouseEvent& event)
  44. {
  45. if (!m_editor)
  46. return;
  47. auto& image_event = event.image_event();
  48. if (image_event.button() != GUI::MouseButton::Left)
  49. return;
  50. m_editor->set_guide_visibility(true);
  51. RefPtr<Guide> new_guide;
  52. if (image_event.position().x() < 0 || image_event.position().x() > editor()->image().size().width()) {
  53. new_guide = make_ref_counted<Guide>(Guide::Orientation::Vertical, image_event.position().x());
  54. } else if (image_event.position().y() < 0 || image_event.position().y() > editor()->image().size().height()) {
  55. new_guide = make_ref_counted<Guide>(Guide::Orientation::Horizontal, image_event.position().y());
  56. }
  57. if (new_guide) {
  58. m_selected_guide = new_guide;
  59. m_guide_origin = 0;
  60. editor()->add_guide(new_guide.release_nonnull());
  61. return;
  62. }
  63. m_event_origin = image_event.position();
  64. m_selected_guide = closest_guide(image_event.position());
  65. if (m_selected_guide) {
  66. m_guide_origin = m_selected_guide->offset();
  67. GUI::Application::the()->show_tooltip_immediately(String::formatted("{}", m_guide_origin), GUI::Application::the()->tooltip_source_widget());
  68. }
  69. }
  70. void GuideTool::on_mouseup(Layer*, MouseEvent&)
  71. {
  72. m_guide_origin = 0;
  73. m_event_origin = { 0, 0 };
  74. GUI::Application::the()->hide_tooltip();
  75. if (!m_selected_guide)
  76. return;
  77. if (m_selected_guide->offset() < 0
  78. || (m_selected_guide->orientation() == Guide::Orientation::Horizontal && m_selected_guide->offset() > editor()->image().size().height())
  79. || (m_selected_guide->orientation() == Guide::Orientation::Vertical && m_selected_guide->offset() > editor()->image().size().width())) {
  80. editor()->remove_guide(*m_selected_guide);
  81. editor()->layers_did_change();
  82. }
  83. m_selected_guide = nullptr;
  84. }
  85. void GuideTool::on_mousemove(Layer*, MouseEvent& event)
  86. {
  87. if (!m_selected_guide)
  88. return;
  89. auto& image_event = event.image_event();
  90. auto delta = image_event.position() - m_event_origin;
  91. auto relevant_offset = 0;
  92. if (m_selected_guide->orientation() == Guide::Orientation::Horizontal)
  93. relevant_offset = delta.y();
  94. else if (m_selected_guide->orientation() == Guide::Orientation::Vertical)
  95. relevant_offset = delta.x();
  96. auto new_offset = (float)relevant_offset + m_guide_origin;
  97. if (image_event.shift() && m_snap_size > 0) {
  98. float snap_size_half = m_snap_size / 2.0;
  99. new_offset -= fmodf(new_offset + snap_size_half, m_snap_size) - snap_size_half;
  100. }
  101. m_selected_guide->set_offset(new_offset);
  102. GUI::Application::the()->show_tooltip_immediately(String::formatted("{}", new_offset), GUI::Application::the()->tooltip_source_widget());
  103. editor()->layers_did_change();
  104. }
  105. void GuideTool::on_context_menu(Layer*, GUI::ContextMenuEvent& event)
  106. {
  107. if (!m_editor)
  108. return;
  109. m_editor->set_guide_visibility(true);
  110. if (!m_context_menu) {
  111. m_context_menu = GUI::Menu::construct();
  112. m_context_menu->add_action(GUI::Action::create(
  113. "Set &Offset", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/gear.png"), [this](auto&) {
  114. if (!m_context_menu_guide)
  115. return;
  116. auto dialog = EditGuideDialog::construct(
  117. editor()->window(),
  118. String::formatted("{}", m_context_menu_guide->offset()),
  119. m_context_menu_guide->orientation());
  120. if (dialog->exec() != GUI::Dialog::ExecOK)
  121. return;
  122. auto offset = dialog->offset_as_pixel(*editor());
  123. if (!offset.has_value())
  124. return;
  125. m_context_menu_guide->set_offset(offset.release_value());
  126. m_context_menu_guide->set_orientation(dialog->orientation());
  127. editor()->layers_did_change();
  128. },
  129. editor()));
  130. m_context_menu->add_action(GUI::Action::create(
  131. "&Delete Guide", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/delete.png"), [this](auto&) {
  132. if (!m_context_menu_guide)
  133. return;
  134. editor()->remove_guide(*m_context_menu_guide);
  135. m_selected_guide = nullptr;
  136. m_guide_origin = 0;
  137. editor()->layers_did_change();
  138. },
  139. editor()));
  140. }
  141. auto image_position = editor()->editor_position_to_image_position(event.position());
  142. m_context_menu_guide = closest_guide({ (int)image_position.x(), (int)image_position.y() });
  143. if (m_context_menu_guide)
  144. m_context_menu->popup(event.screen_position());
  145. }
  146. void GuideTool::on_tool_activation()
  147. {
  148. if (m_editor)
  149. m_editor->set_guide_visibility(true);
  150. }
  151. GUI::Widget* GuideTool::get_properties_widget()
  152. {
  153. if (!m_properties_widget) {
  154. m_properties_widget = GUI::Widget::construct();
  155. m_properties_widget->set_layout<GUI::VerticalBoxLayout>();
  156. auto& snapping_container = m_properties_widget->add<GUI::Widget>();
  157. snapping_container.set_fixed_height(20);
  158. snapping_container.set_layout<GUI::HorizontalBoxLayout>();
  159. auto& snapping_label = snapping_container.add<GUI::Label>("Snap offset:");
  160. snapping_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
  161. snapping_label.set_fixed_size(80, 20);
  162. snapping_label.set_tooltip("Press Shift to snap");
  163. auto& snapping_slider = snapping_container.add<GUI::ValueSlider>(Orientation::Horizontal, "px");
  164. snapping_slider.set_range(0, 50);
  165. snapping_slider.set_value(m_snap_size);
  166. snapping_slider.on_change = [&](int value) {
  167. m_snap_size = value;
  168. };
  169. }
  170. return m_properties_widget.ptr();
  171. }
  172. }