abstract_canvas.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /* abstract_canvas.cpp - generic canvas which can be edited with tools
  2. * Copyright (C) 2017-2018 caryoscelus
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <algorithm>
  18. #include <QMouseEvent>
  19. #include <QGraphicsPixmapItem>
  20. #include <QDebug>
  21. #include <util/strings.h>
  22. #include "abstract_canvas.h"
  23. #include "registry.h"
  24. namespace rainynite::studio {
  25. AbstractCanvas::AbstractCanvas(QWidget* parent) :
  26. QGraphicsView(parent),
  27. the_scene(make_unique<QGraphicsScene>()),
  28. image(make_unique<QGraphicsPixmapItem>())
  29. {
  30. setScene(the_scene.get());
  31. scene()->addItem(image.get());
  32. }
  33. AbstractCanvas::~AbstractCanvas() = default;
  34. void AbstractCanvas::set_background_image(QPixmap const& pixmap) {
  35. image->setPixmap(pixmap);
  36. }
  37. void AbstractCanvas::set_bg_transform(QTransform const& transform) {
  38. image->setTransform(transform);
  39. }
  40. void AbstractCanvas::load_registered_tools() {
  41. for (auto factory : get_canvas_tools_by_type(typeid(*this)))
  42. add_tool((*factory)());
  43. }
  44. void AbstractCanvas::add_tool(unique_ptr<CanvasTool> tool) {
  45. named_tools.emplace(tool->name(), tool.get());
  46. tools.push_back(std::move(tool));
  47. }
  48. void AbstractCanvas::add_editor(shared_ptr<AbstractCanvasEditor> editor) {
  49. editors.push_back(editor);
  50. editor->set_canvas(this);
  51. }
  52. void AbstractCanvas::remove_editor(shared_ptr<AbstractCanvasEditor> editor) {
  53. editors.erase(std::remove(editors.begin(), editors.end(), editor), editors.end());
  54. }
  55. void AbstractCanvas::clear_editors() {
  56. editors.clear();
  57. }
  58. shared_ptr<AbstractCanvasEditor> AbstractCanvas::latest_editor() const {
  59. if (editors.size() > 0)
  60. return editors.back();
  61. return nullptr;
  62. }
  63. vector<observer_ptr<CanvasTool>> AbstractCanvas::list_tools() const {
  64. vector<observer_ptr<CanvasTool>> result;
  65. std::transform(
  66. tools.begin(),
  67. tools.end(),
  68. std::back_inserter(result),
  69. [](auto const& e) {
  70. return make_observer(e.get());
  71. }
  72. );
  73. return result;
  74. }
  75. void AbstractCanvas::use_tool(string const& name) {
  76. auto it = named_tools.find(name);
  77. if (it == named_tools.end()) {
  78. qWarning() << "Cannot find tool" << util::str(name);
  79. return;
  80. }
  81. if (use_tool(it->second))
  82. Q_EMIT tool_changed(name);
  83. }
  84. bool AbstractCanvas::use_tool(observer_ptr<CanvasTool> tool) {
  85. if (current_tool == tool)
  86. return false;
  87. if (current_tool != nullptr) {
  88. removeEventFilter(current_tool.get());
  89. current_tool->set_canvas(nullptr);
  90. }
  91. tool->set_canvas(this);
  92. installEventFilter(tool.get());
  93. current_tool = tool;
  94. return true;
  95. }
  96. void AbstractCanvas::zoom_at(QPoint point, double factor) {
  97. auto old_pos = mapToScene(point);
  98. scale(factor, factor);
  99. auto new_pos = mapToScene(point);
  100. auto delta = new_pos-old_pos;
  101. translate(delta.x(), delta.y());
  102. Q_EMIT zoomed();
  103. }
  104. void AbstractCanvas::set_zoom(double level) {
  105. zoom_at({width()/2, height()/2}, level/zoom_level());
  106. }
  107. void AbstractCanvas::zoom_to_rect(QRectF rect) {
  108. fitInView(rect, Qt::KeepAspectRatio);
  109. Q_EMIT zoomed();
  110. }
  111. double AbstractCanvas::zoom_level() {
  112. return std::abs(transform().m11());
  113. }
  114. void AbstractCanvas::mirror_horizontally(bool value) {
  115. if (value != is_mirrored_horizontally())
  116. scale(-1.0, 1.0);
  117. }
  118. bool AbstractCanvas::is_mirrored_horizontally() const {
  119. return transform().m11() < 0;
  120. }
  121. void AbstractCanvas::scroll_by(QPointF delta) {
  122. delta /= transform().m11();
  123. translate(delta.x(), delta.y());
  124. }
  125. // Unfortunately, mouse events are not sent to filter..
  126. #define EVENT_HANDLER(method, Event) \
  127. void AbstractCanvas::method(Event* event) { \
  128. if (current_tool != nullptr) { \
  129. if (current_tool->eventFilter(this, event)) \
  130. return; \
  131. } \
  132. if (auto editor = latest_editor()) { \
  133. if (editor->canvas_event(event)) \
  134. return; \
  135. } \
  136. QGraphicsView::method(event); \
  137. }
  138. #define MOUSE_HANDLER(method) EVENT_HANDLER(method, QMouseEvent)
  139. MOUSE_HANDLER(mouseDoubleClickEvent)
  140. MOUSE_HANDLER(mouseMoveEvent)
  141. MOUSE_HANDLER(mousePressEvent)
  142. MOUSE_HANDLER(mouseReleaseEvent)
  143. EVENT_HANDLER(wheelEvent, QWheelEvent)
  144. #undef MOUSE_HANDLER
  145. #undef EVENT_HANDLER
  146. void AbstractCanvas::set_context(shared_ptr<EditorContext> context) {
  147. ContextListener::set_context(context);
  148. for (auto const& editor : editors) {
  149. if (auto cl = dynamic_cast<ContextListener*>(editor.get())) {
  150. cl->set_context(context);
  151. }
  152. }
  153. }
  154. } // namespace rainynite::studio