VBForm.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. #include "VBForm.h"
  2. #include "VBProperty.h"
  3. #include "VBWidget.h"
  4. #include "VBWidgetRegistry.h"
  5. #include <AK/JsonArray.h>
  6. #include <AK/JsonObject.h>
  7. #include <AK/StringBuilder.h>
  8. #include <LibCore/CFile.h>
  9. #include <LibGUI/GAction.h>
  10. #include <LibGUI/GMenu.h>
  11. #include <LibGUI/GMessageBox.h>
  12. #include <LibGUI/GPainter.h>
  13. static VBForm* s_current;
  14. VBForm* VBForm::current()
  15. {
  16. return s_current;
  17. }
  18. VBForm::VBForm(const String& name, GWidget* parent)
  19. : GWidget(parent)
  20. , m_name(name)
  21. {
  22. s_current = this;
  23. set_fill_with_background_color(true);
  24. set_background_color(Color::WarmGray);
  25. set_greedy_for_hits(true);
  26. m_context_menu = make<GMenu>();
  27. m_context_menu->add_action(GAction::create("Move to front", [this](auto&) {
  28. if (auto* widget = single_selected_widget())
  29. widget->gwidget()->move_to_front();
  30. }));
  31. m_context_menu->add_action(GAction::create("Move to back", [this](auto&) {
  32. if (auto* widget = single_selected_widget())
  33. widget->gwidget()->move_to_back();
  34. }));
  35. m_context_menu->add_action(GAction::create("Delete", [this](auto&) {
  36. delete_selected_widgets();
  37. }));
  38. }
  39. void VBForm::context_menu_event(GContextMenuEvent& event)
  40. {
  41. m_context_menu->popup(event.screen_position());
  42. }
  43. void VBForm::insert_widget(VBWidgetType type)
  44. {
  45. auto widget = VBWidget::create(type, *this);
  46. widget->set_rect({ m_next_insertion_position, { m_grid_size * 10 + 1, m_grid_size * 5 + 1 } });
  47. m_next_insertion_position.move_by(m_grid_size, m_grid_size);
  48. m_widgets.append(move(widget));
  49. }
  50. VBForm::~VBForm()
  51. {
  52. }
  53. void VBForm::paint_event(GPaintEvent& event)
  54. {
  55. GPainter painter(*this);
  56. painter.add_clip_rect(event.rect());
  57. for (int y = 0; y < height(); y += m_grid_size) {
  58. for (int x = 0; x < width(); x += m_grid_size) {
  59. painter.set_pixel({ x, y }, Color::from_rgb(0x404040));
  60. }
  61. }
  62. }
  63. void VBForm::second_paint_event(GPaintEvent& event)
  64. {
  65. GPainter painter(*this);
  66. painter.add_clip_rect(event.rect());
  67. for (auto& widget : m_widgets) {
  68. if (widget.is_selected()) {
  69. for_each_direction([&](auto direction) {
  70. painter.fill_rect(widget.grabber_rect(direction), Color::Black);
  71. });
  72. }
  73. }
  74. }
  75. bool VBForm::is_selected(const VBWidget& widget) const
  76. {
  77. // FIXME: Fix HashTable and remove this const_cast.
  78. return m_selected_widgets.contains(const_cast<VBWidget*>(&widget));
  79. }
  80. VBWidget* VBForm::widget_at(const Point& position)
  81. {
  82. auto* gwidget = child_at(position);
  83. if (!gwidget)
  84. return nullptr;
  85. return m_gwidget_map.get(gwidget).value_or(nullptr);
  86. }
  87. void VBForm::grabber_mousedown_event(GMouseEvent& event, Direction grabber)
  88. {
  89. m_transform_event_origin = event.position();
  90. for_each_selected_widget([](auto& widget) { widget.capture_transform_origin_rect(); });
  91. m_resize_direction = grabber;
  92. }
  93. void VBForm::keydown_event(GKeyEvent& event)
  94. {
  95. if (event.key() == KeyCode::Key_Delete) {
  96. delete_selected_widgets();
  97. return;
  98. }
  99. if (event.key() == KeyCode::Key_Tab) {
  100. if (m_widgets.is_empty())
  101. return;
  102. if (m_selected_widgets.is_empty()) {
  103. set_single_selected_widget(&m_widgets.first());
  104. update();
  105. return;
  106. }
  107. int selected_widget_index = 0;
  108. for (; selected_widget_index < m_widgets.size(); ++selected_widget_index) {
  109. if (&m_widgets[selected_widget_index] == *m_selected_widgets.begin())
  110. break;
  111. }
  112. ++selected_widget_index;
  113. if (selected_widget_index == m_widgets.size())
  114. selected_widget_index = 0;
  115. set_single_selected_widget(&m_widgets[selected_widget_index]);
  116. update();
  117. return;
  118. }
  119. if (!m_selected_widgets.is_empty()) {
  120. switch (event.key()) {
  121. case KeyCode::Key_Up:
  122. update();
  123. for_each_selected_widget([this](auto& widget) { widget.gwidget()->move_by(0, -m_grid_size); });
  124. break;
  125. case KeyCode::Key_Down:
  126. update();
  127. for_each_selected_widget([this](auto& widget) { widget.gwidget()->move_by(0, m_grid_size); });
  128. break;
  129. case KeyCode::Key_Left:
  130. update();
  131. for_each_selected_widget([this](auto& widget) { widget.gwidget()->move_by(-m_grid_size, 0); });
  132. break;
  133. case KeyCode::Key_Right:
  134. update();
  135. for_each_selected_widget([this](auto& widget) { widget.gwidget()->move_by(m_grid_size, 0); });
  136. break;
  137. }
  138. return;
  139. }
  140. }
  141. void VBForm::set_single_selected_widget(VBWidget* widget)
  142. {
  143. if (!widget) {
  144. if (!m_selected_widgets.is_empty()) {
  145. m_selected_widgets.clear();
  146. on_widget_selected(nullptr);
  147. update();
  148. }
  149. return;
  150. }
  151. m_selected_widgets.clear();
  152. m_selected_widgets.set(widget);
  153. on_widget_selected(m_selected_widgets.size() == 1 ? widget : nullptr);
  154. update();
  155. }
  156. void VBForm::add_to_selection(VBWidget& widget)
  157. {
  158. m_selected_widgets.set(&widget);
  159. update();
  160. }
  161. void VBForm::remove_from_selection(VBWidget& widget)
  162. {
  163. m_selected_widgets.remove(&widget);
  164. update();
  165. }
  166. void VBForm::mousedown_event(GMouseEvent& event)
  167. {
  168. if (m_resize_direction == Direction::None) {
  169. bool hit_grabber = false;
  170. for_each_selected_widget([&](auto& widget) {
  171. auto grabber = widget.grabber_at(event.position());
  172. if (grabber != Direction::None) {
  173. hit_grabber = true;
  174. return grabber_mousedown_event(event, grabber);
  175. }
  176. });
  177. if (hit_grabber)
  178. return;
  179. }
  180. auto* widget = widget_at(event.position());
  181. if (!widget) {
  182. set_single_selected_widget(nullptr);
  183. return;
  184. }
  185. if (event.button() == GMouseButton::Left || event.button() == GMouseButton::Right) {
  186. m_transform_event_origin = event.position();
  187. if (event.modifiers() == Mod_Ctrl)
  188. remove_from_selection(*widget);
  189. else if (event.modifiers() == Mod_Shift)
  190. add_to_selection(*widget);
  191. else if (!m_selected_widgets.contains(widget))
  192. set_single_selected_widget(widget);
  193. for_each_selected_widget([](auto& widget) { widget.capture_transform_origin_rect(); });
  194. on_widget_selected(single_selected_widget());
  195. }
  196. }
  197. void VBForm::mousemove_event(GMouseEvent& event)
  198. {
  199. if (event.buttons() & GMouseButton::Left) {
  200. if (m_resize_direction == Direction::None) {
  201. update();
  202. auto delta = event.position() - m_transform_event_origin;
  203. for_each_selected_widget([&](auto& widget) {
  204. auto new_rect = widget.transform_origin_rect().translated(delta);
  205. new_rect.set_x(new_rect.x() - (new_rect.x() % m_grid_size));
  206. new_rect.set_y(new_rect.y() - (new_rect.y() % m_grid_size));
  207. widget.set_rect(new_rect);
  208. });
  209. return;
  210. }
  211. int diff_x = event.x() - m_transform_event_origin.x();
  212. int diff_y = event.y() - m_transform_event_origin.y();
  213. int change_x = 0;
  214. int change_y = 0;
  215. int change_w = 0;
  216. int change_h = 0;
  217. switch (m_resize_direction) {
  218. case Direction::DownRight:
  219. change_w = diff_x;
  220. change_h = diff_y;
  221. break;
  222. case Direction::Right:
  223. change_w = diff_x;
  224. break;
  225. case Direction::UpRight:
  226. change_w = diff_x;
  227. change_y = diff_y;
  228. change_h = -diff_y;
  229. break;
  230. case Direction::Up:
  231. change_y = diff_y;
  232. change_h = -diff_y;
  233. break;
  234. case Direction::UpLeft:
  235. change_x = diff_x;
  236. change_w = -diff_x;
  237. change_y = diff_y;
  238. change_h = -diff_y;
  239. break;
  240. case Direction::Left:
  241. change_x = diff_x;
  242. change_w = -diff_x;
  243. break;
  244. case Direction::DownLeft:
  245. change_x = diff_x;
  246. change_w = -diff_x;
  247. change_h = diff_y;
  248. break;
  249. case Direction::Down:
  250. change_h = diff_y;
  251. break;
  252. default:
  253. ASSERT_NOT_REACHED();
  254. }
  255. update();
  256. for_each_selected_widget([&](auto& widget) {
  257. auto new_rect = widget.transform_origin_rect();
  258. Size minimum_size { 5, 5 };
  259. new_rect.set_x(new_rect.x() + change_x);
  260. new_rect.set_y(new_rect.y() + change_y);
  261. new_rect.set_width(max(minimum_size.width(), new_rect.width() + change_w));
  262. new_rect.set_height(max(minimum_size.height(), new_rect.height() + change_h));
  263. new_rect.set_x(new_rect.x() - (new_rect.x() % m_grid_size));
  264. new_rect.set_y(new_rect.y() - (new_rect.y() % m_grid_size));
  265. new_rect.set_width(new_rect.width() - (new_rect.width() % m_grid_size) + 1);
  266. new_rect.set_height(new_rect.height() - (new_rect.height() % m_grid_size) + 1);
  267. widget.set_rect(new_rect);
  268. });
  269. set_cursor_type_from_grabber(m_resize_direction);
  270. } else {
  271. for (auto& widget : m_selected_widgets) {
  272. auto grabber_at = widget->grabber_at(event.position());
  273. set_cursor_type_from_grabber(grabber_at);
  274. if (grabber_at != Direction::None)
  275. break;
  276. }
  277. }
  278. }
  279. void VBForm::load_from_file(const String& path)
  280. {
  281. CFile file(path);
  282. if (!file.open(CIODevice::ReadOnly)) {
  283. GMessageBox::show(String::format("Could not open '%s' for reading", path.characters()), "Error", GMessageBox::Type::Error, GMessageBox::InputType::OK, window());
  284. return;
  285. }
  286. auto file_contents = file.read_all();
  287. auto form_json = JsonValue::from_string(file_contents);
  288. if (!form_json.is_object()) {
  289. GMessageBox::show(String::format("Could not parse '%s'", path.characters()), "Error", GMessageBox::Type::Error, GMessageBox::InputType::OK, window());
  290. return;
  291. }
  292. m_name = form_json.as_object().get("name").to_string();
  293. auto widgets = form_json.as_object().get("widgets").as_array();
  294. widgets.for_each([&](const JsonValue& widget_value) {
  295. auto& widget_object = widget_value.as_object();
  296. auto widget_class = widget_object.get("class").as_string();
  297. auto widget_type = widget_type_from_class_name(widget_class);
  298. auto vbwidget = VBWidget::create(widget_type, *this);
  299. widget_object.for_each_member([&](auto& property_name, const JsonValue& property_value) {
  300. (void)property_name;
  301. (void)property_value;
  302. VBProperty& property = vbwidget->property(property_name);
  303. dbgprintf("Set property %s.%s to '%s'\n", widget_class.characters(), property_name.characters(), property_value.to_string().characters());
  304. property.set_value(property_value);
  305. });
  306. m_widgets.append(vbwidget);
  307. });
  308. }
  309. void VBForm::write_to_file(const String& path)
  310. {
  311. CFile file(path);
  312. if (!file.open(CIODevice::WriteOnly)) {
  313. GMessageBox::show(String::format("Could not open '%s' for writing", path.characters()), "Error", GMessageBox::Type::Error, GMessageBox::InputType::OK, window());
  314. return;
  315. }
  316. JsonObject form_object;
  317. form_object.set("name", m_name);
  318. JsonArray widget_array;
  319. for (auto& widget : m_widgets) {
  320. JsonObject widget_object;
  321. widget.for_each_property([&](auto& property) {
  322. if (property.value().is_bool())
  323. widget_object.set(property.name(), property.value().to_bool());
  324. else if (property.value().is_int())
  325. widget_object.set(property.name(), property.value().to_int());
  326. else
  327. widget_object.set(property.name(), property.value().to_string());
  328. });
  329. widget_array.append(widget_object);
  330. }
  331. form_object.set("widgets", widget_array);
  332. file.write(form_object.to_string());
  333. }
  334. void VBForm::dump()
  335. {
  336. dbgprintf("[Form]\n");
  337. dbgprintf("Name=%s\n", m_name.characters());
  338. dbgprintf("\n");
  339. int i = 0;
  340. for (auto& widget : m_widgets) {
  341. dbgprintf("[Widget %d]\n", i++);
  342. widget.for_each_property([](auto& property) {
  343. dbgprintf("%s=%s\n", property.name().characters(), property.value().to_string().characters());
  344. });
  345. dbgprintf("\n");
  346. }
  347. }
  348. void VBForm::mouseup_event(GMouseEvent& event)
  349. {
  350. if (event.button() == GMouseButton::Left) {
  351. m_transform_event_origin = {};
  352. m_resize_direction = Direction::None;
  353. }
  354. }
  355. void VBForm::delete_selected_widgets()
  356. {
  357. Vector<VBWidget*> to_delete;
  358. for_each_selected_widget([&](auto& widget) {
  359. to_delete.append(&widget);
  360. });
  361. for (auto& widget : to_delete)
  362. m_widgets.remove_first_matching([&widget](auto& entry) { return entry == widget; });
  363. on_widget_selected(single_selected_widget());
  364. }
  365. template<typename Callback>
  366. void VBForm::for_each_selected_widget(Callback callback)
  367. {
  368. for (auto& widget : m_selected_widgets)
  369. callback(*widget);
  370. }
  371. void VBForm::set_cursor_type_from_grabber(Direction grabber)
  372. {
  373. if (grabber == m_mouse_direction_type)
  374. return;
  375. switch (grabber) {
  376. case Direction::Up:
  377. case Direction::Down:
  378. window()->set_override_cursor(GStandardCursor::ResizeVertical);
  379. break;
  380. case Direction::Left:
  381. case Direction::Right:
  382. window()->set_override_cursor(GStandardCursor::ResizeHorizontal);
  383. break;
  384. case Direction::UpLeft:
  385. case Direction::DownRight:
  386. window()->set_override_cursor(GStandardCursor::ResizeDiagonalTLBR);
  387. break;
  388. case Direction::UpRight:
  389. case Direction::DownLeft:
  390. window()->set_override_cursor(GStandardCursor::ResizeDiagonalBLTR);
  391. break;
  392. case Direction::None:
  393. window()->set_override_cursor(GStandardCursor::None);
  394. break;
  395. }
  396. m_mouse_direction_type = grabber;
  397. }
  398. VBWidget* VBForm::single_selected_widget()
  399. {
  400. if (m_selected_widgets.size() != 1)
  401. return nullptr;
  402. return *m_selected_widgets.begin();
  403. }