VBForm.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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 <LibDraw/PNGLoader.h>
  10. #include <LibGUI/GAction.h>
  11. #include <LibGUI/GBoxLayout.h>
  12. #include <LibGUI/GMenu.h>
  13. #include <LibGUI/GMessageBox.h>
  14. #include <LibGUI/GPainter.h>
  15. static VBForm* s_current;
  16. VBForm* VBForm::current()
  17. {
  18. return s_current;
  19. }
  20. VBForm::VBForm(const String& name, GWidget* parent)
  21. : GWidget(parent)
  22. , m_name(name)
  23. {
  24. s_current = this;
  25. set_fill_with_background_color(true);
  26. set_greedy_for_hits(true);
  27. m_context_menu = GMenu::construct();
  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", load_png("/res/icons/16x16/layout-horizontally.png"), [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", load_png("/res/icons/16x16/layout-vertically.png"), [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. bool in_layout = widget.is_in_layout();
  90. auto grabber_rect = widget.grabber_rect(direction);
  91. painter.fill_rect(grabber_rect, in_layout ? Color::White : Color::Black);
  92. if (in_layout)
  93. painter.draw_rect(grabber_rect, Color::Black);
  94. });
  95. }
  96. }
  97. }
  98. bool VBForm::is_selected(const VBWidget& widget) const
  99. {
  100. // FIXME: Fix HashTable and remove this const_cast.
  101. return m_selected_widgets.contains(const_cast<VBWidget*>(&widget));
  102. }
  103. VBWidget* VBForm::widget_at(const Point& position)
  104. {
  105. auto result = hit_test(position, GWidget::ShouldRespectGreediness::No);
  106. if (!result.widget)
  107. return nullptr;
  108. auto* gwidget = result.widget;
  109. while (gwidget) {
  110. if (auto* widget = m_gwidget_map.get(gwidget).value_or(nullptr))
  111. return widget;
  112. gwidget = gwidget->parent_widget();
  113. }
  114. return nullptr;
  115. }
  116. void VBForm::grabber_mousedown_event(GMouseEvent& event, Direction grabber)
  117. {
  118. m_transform_event_origin = event.position();
  119. for_each_selected_widget([](auto& widget) { widget.capture_transform_origin_rect(); });
  120. m_resize_direction = grabber;
  121. }
  122. void VBForm::keydown_event(GKeyEvent& event)
  123. {
  124. if (event.key() == KeyCode::Key_Delete) {
  125. delete_selected_widgets();
  126. return;
  127. }
  128. if (event.key() == KeyCode::Key_Tab) {
  129. if (m_widgets.is_empty())
  130. return;
  131. if (m_selected_widgets.is_empty()) {
  132. set_single_selected_widget(&m_widgets.first());
  133. update();
  134. return;
  135. }
  136. int selected_widget_index = 0;
  137. for (; selected_widget_index < m_widgets.size(); ++selected_widget_index) {
  138. if (&m_widgets[selected_widget_index] == *m_selected_widgets.begin())
  139. break;
  140. }
  141. ++selected_widget_index;
  142. if (selected_widget_index == m_widgets.size())
  143. selected_widget_index = 0;
  144. set_single_selected_widget(&m_widgets[selected_widget_index]);
  145. update();
  146. return;
  147. }
  148. if (!m_selected_widgets.is_empty()) {
  149. switch (event.key()) {
  150. case KeyCode::Key_Up:
  151. update();
  152. for_each_selected_widget([this](auto& widget) {
  153. if (widget.is_in_layout())
  154. return;
  155. widget.gwidget()->move_by(0, -m_grid_size);
  156. });
  157. break;
  158. case KeyCode::Key_Down:
  159. update();
  160. for_each_selected_widget([this](auto& widget) {
  161. if (widget.is_in_layout())
  162. return;
  163. widget.gwidget()->move_by(0, m_grid_size);
  164. });
  165. break;
  166. case KeyCode::Key_Left:
  167. update();
  168. for_each_selected_widget([this](auto& widget) {
  169. if (widget.is_in_layout())
  170. return;
  171. widget.gwidget()->move_by(-m_grid_size, 0);
  172. });
  173. break;
  174. case KeyCode::Key_Right:
  175. update();
  176. for_each_selected_widget([this](auto& widget) {
  177. if (widget.is_in_layout())
  178. return;
  179. widget.gwidget()->move_by(m_grid_size, 0);
  180. });
  181. break;
  182. }
  183. return;
  184. }
  185. }
  186. void VBForm::set_single_selected_widget(VBWidget* widget)
  187. {
  188. if (!widget) {
  189. if (!m_selected_widgets.is_empty()) {
  190. m_selected_widgets.clear();
  191. on_widget_selected(nullptr);
  192. update();
  193. }
  194. return;
  195. }
  196. m_selected_widgets.clear();
  197. m_selected_widgets.set(widget);
  198. on_widget_selected(m_selected_widgets.size() == 1 ? widget : nullptr);
  199. update();
  200. }
  201. void VBForm::add_to_selection(VBWidget& widget)
  202. {
  203. m_selected_widgets.set(&widget);
  204. update();
  205. }
  206. void VBForm::remove_from_selection(VBWidget& widget)
  207. {
  208. m_selected_widgets.remove(&widget);
  209. update();
  210. }
  211. void VBForm::mousedown_event(GMouseEvent& event)
  212. {
  213. if (m_resize_direction == Direction::None) {
  214. bool hit_grabber = false;
  215. for_each_selected_widget([&](auto& widget) {
  216. if (widget.is_in_layout())
  217. return;
  218. auto grabber = widget.grabber_at(event.position());
  219. if (grabber != Direction::None) {
  220. hit_grabber = true;
  221. return grabber_mousedown_event(event, grabber);
  222. }
  223. });
  224. if (hit_grabber)
  225. return;
  226. }
  227. auto* widget = widget_at(event.position());
  228. if (!widget) {
  229. set_single_selected_widget(nullptr);
  230. return;
  231. }
  232. if (event.button() == GMouseButton::Left || event.button() == GMouseButton::Right) {
  233. m_transform_event_origin = event.position();
  234. if (event.modifiers() == Mod_Ctrl)
  235. remove_from_selection(*widget);
  236. else if (event.modifiers() == Mod_Shift)
  237. add_to_selection(*widget);
  238. else if (!m_selected_widgets.contains(widget))
  239. set_single_selected_widget(widget);
  240. for_each_selected_widget([](auto& widget) { widget.capture_transform_origin_rect(); });
  241. on_widget_selected(single_selected_widget());
  242. }
  243. }
  244. void VBForm::mousemove_event(GMouseEvent& event)
  245. {
  246. if (event.buttons() & GMouseButton::Left) {
  247. if (m_resize_direction == Direction::None) {
  248. update();
  249. auto delta = event.position() - m_transform_event_origin;
  250. for_each_selected_widget([&](auto& widget) {
  251. if (widget.is_in_layout())
  252. return;
  253. auto new_rect = widget.transform_origin_rect().translated(delta);
  254. new_rect.set_x(new_rect.x() - (new_rect.x() % m_grid_size));
  255. new_rect.set_y(new_rect.y() - (new_rect.y() % m_grid_size));
  256. widget.set_rect(new_rect);
  257. });
  258. return;
  259. }
  260. int diff_x = event.x() - m_transform_event_origin.x();
  261. int diff_y = event.y() - m_transform_event_origin.y();
  262. int change_x = 0;
  263. int change_y = 0;
  264. int change_w = 0;
  265. int change_h = 0;
  266. switch (m_resize_direction) {
  267. case Direction::DownRight:
  268. change_w = diff_x;
  269. change_h = diff_y;
  270. break;
  271. case Direction::Right:
  272. change_w = diff_x;
  273. break;
  274. case Direction::UpRight:
  275. change_w = diff_x;
  276. change_y = diff_y;
  277. change_h = -diff_y;
  278. break;
  279. case Direction::Up:
  280. change_y = diff_y;
  281. change_h = -diff_y;
  282. break;
  283. case Direction::UpLeft:
  284. change_x = diff_x;
  285. change_w = -diff_x;
  286. change_y = diff_y;
  287. change_h = -diff_y;
  288. break;
  289. case Direction::Left:
  290. change_x = diff_x;
  291. change_w = -diff_x;
  292. break;
  293. case Direction::DownLeft:
  294. change_x = diff_x;
  295. change_w = -diff_x;
  296. change_h = diff_y;
  297. break;
  298. case Direction::Down:
  299. change_h = diff_y;
  300. break;
  301. default:
  302. ASSERT_NOT_REACHED();
  303. }
  304. update();
  305. for_each_selected_widget([&](auto& widget) {
  306. if (widget.is_in_layout())
  307. return;
  308. auto new_rect = widget.transform_origin_rect();
  309. Size minimum_size { 5, 5 };
  310. new_rect.set_x(new_rect.x() + change_x);
  311. new_rect.set_y(new_rect.y() + change_y);
  312. new_rect.set_width(max(minimum_size.width(), new_rect.width() + change_w));
  313. new_rect.set_height(max(minimum_size.height(), new_rect.height() + change_h));
  314. new_rect.set_x(new_rect.x() - (new_rect.x() % m_grid_size));
  315. new_rect.set_y(new_rect.y() - (new_rect.y() % m_grid_size));
  316. new_rect.set_width(new_rect.width() - (new_rect.width() % m_grid_size) + 1);
  317. new_rect.set_height(new_rect.height() - (new_rect.height() % m_grid_size) + 1);
  318. widget.set_rect(new_rect);
  319. });
  320. set_cursor_type_from_grabber(m_resize_direction);
  321. } else {
  322. for (auto& widget : m_selected_widgets) {
  323. if (widget->is_in_layout())
  324. continue;
  325. auto grabber_at = widget->grabber_at(event.position());
  326. set_cursor_type_from_grabber(grabber_at);
  327. if (grabber_at != Direction::None)
  328. break;
  329. }
  330. }
  331. }
  332. void VBForm::load_from_file(const String& path)
  333. {
  334. auto file = CFile::construct(path);
  335. if (!file->open(CIODevice::ReadOnly)) {
  336. GMessageBox::show(String::format("Could not open '%s' for reading", path.characters()), "Error", GMessageBox::Type::Error, GMessageBox::InputType::OK, window());
  337. return;
  338. }
  339. auto file_contents = file->read_all();
  340. auto form_json = JsonValue::from_string(file_contents);
  341. if (!form_json.is_object()) {
  342. GMessageBox::show(String::format("Could not parse '%s'", path.characters()), "Error", GMessageBox::Type::Error, GMessageBox::InputType::OK, window());
  343. return;
  344. }
  345. m_name = form_json.as_object().get("name").to_string();
  346. auto widgets = form_json.as_object().get("widgets").as_array();
  347. widgets.for_each([&](const JsonValue& widget_value) {
  348. auto& widget_object = widget_value.as_object();
  349. auto widget_class = widget_object.get("class").as_string();
  350. auto widget_type = widget_type_from_class_name(widget_class);
  351. // FIXME: Construct VBWidget within the right parent..
  352. auto vbwidget = VBWidget::create(widget_type, *this, nullptr);
  353. widget_object.for_each_member([&](auto& property_name, const JsonValue& property_value) {
  354. (void)property_name;
  355. (void)property_value;
  356. VBProperty& property = vbwidget->property(property_name);
  357. dbgprintf("Set property %s.%s to '%s'\n", widget_class.characters(), property_name.characters(), property_value.to_string().characters());
  358. property.set_value(property_value);
  359. });
  360. m_widgets.append(vbwidget);
  361. });
  362. }
  363. void VBForm::write_to_file(const String& path)
  364. {
  365. auto file = CFile::construct(path);
  366. if (!file->open(CIODevice::WriteOnly)) {
  367. GMessageBox::show(String::format("Could not open '%s' for writing", path.characters()), "Error", GMessageBox::Type::Error, GMessageBox::InputType::OK, window());
  368. return;
  369. }
  370. JsonObject form_object;
  371. form_object.set("name", m_name);
  372. JsonArray widget_array;
  373. for (auto& widget : m_widgets) {
  374. JsonObject widget_object;
  375. widget.for_each_property([&](auto& property) {
  376. if (property.value().is_bool())
  377. widget_object.set(property.name(), property.value().to_bool());
  378. else if (property.value().is_int())
  379. widget_object.set(property.name(), property.value().to_int());
  380. else
  381. widget_object.set(property.name(), property.value().to_string());
  382. });
  383. widget_array.append(widget_object);
  384. }
  385. form_object.set("widgets", widget_array);
  386. file->write(form_object.to_string());
  387. }
  388. void VBForm::dump()
  389. {
  390. dbgprintf("[Form]\n");
  391. dbgprintf("Name=%s\n", m_name.characters());
  392. dbgprintf("\n");
  393. int i = 0;
  394. for (auto& widget : m_widgets) {
  395. dbgprintf("[Widget %d]\n", i++);
  396. widget.for_each_property([](auto& property) {
  397. dbgprintf("%s=%s\n", property.name().characters(), property.value().to_string().characters());
  398. });
  399. dbgprintf("\n");
  400. }
  401. }
  402. void VBForm::mouseup_event(GMouseEvent& event)
  403. {
  404. if (event.button() == GMouseButton::Left) {
  405. m_transform_event_origin = {};
  406. m_resize_direction = Direction::None;
  407. }
  408. }
  409. void VBForm::delete_selected_widgets()
  410. {
  411. Vector<VBWidget*> to_delete;
  412. for_each_selected_widget([&](auto& widget) {
  413. to_delete.append(&widget);
  414. });
  415. if (to_delete.is_empty())
  416. return;
  417. for (auto& widget : to_delete)
  418. m_widgets.remove_first_matching([&widget](auto& entry) { return entry == widget; });
  419. on_widget_selected(single_selected_widget());
  420. update();
  421. }
  422. template<typename Callback>
  423. void VBForm::for_each_selected_widget(Callback callback)
  424. {
  425. for (auto& widget : m_selected_widgets)
  426. callback(*widget);
  427. }
  428. void VBForm::set_cursor_type_from_grabber(Direction grabber)
  429. {
  430. if (grabber == m_mouse_direction_type)
  431. return;
  432. switch (grabber) {
  433. case Direction::Up:
  434. case Direction::Down:
  435. window()->set_override_cursor(GStandardCursor::ResizeVertical);
  436. break;
  437. case Direction::Left:
  438. case Direction::Right:
  439. window()->set_override_cursor(GStandardCursor::ResizeHorizontal);
  440. break;
  441. case Direction::UpLeft:
  442. case Direction::DownRight:
  443. window()->set_override_cursor(GStandardCursor::ResizeDiagonalTLBR);
  444. break;
  445. case Direction::UpRight:
  446. case Direction::DownLeft:
  447. window()->set_override_cursor(GStandardCursor::ResizeDiagonalBLTR);
  448. break;
  449. case Direction::None:
  450. window()->set_override_cursor(GStandardCursor::None);
  451. break;
  452. }
  453. m_mouse_direction_type = grabber;
  454. }
  455. VBWidget* VBForm::single_selected_widget()
  456. {
  457. if (m_selected_widgets.size() != 1)
  458. return nullptr;
  459. return *m_selected_widgets.begin();
  460. }