VBForm.cpp 14 KB

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