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