VBForm.cpp 15 KB

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