window.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #include "window.hpp"
  2. #include "gdkmm/texture.h"
  3. #include "sigc++/adaptors/bind.h"
  4. #include "sigc++/functors/mem_fun.h"
  5. //}
  6. // void MainWindow::ApplyStyles() {
  7. // // Load and apply the CSS file
  8. // auto css_provider = Gtk::CssProvider::create();
  9. // css_provider->load_from_path("style.css");
  10. // Gtk::StyleContext::add_provider_for_display(Gdk::Display::get_default(), css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
  11. // }
  12. void MainWindow::OnCellRightClick(int n_press, double n_x, double n_y, int index)
  13. {
  14. (void)n_press, (void)n_x, (void)n_y;
  15. int x = index % field.getCols();
  16. int y = index / field.getCols();
  17. int pos = x + y * field.getRows();
  18. if (field.isOpened(x, y) == false)
  19. {
  20. field.toggleFlag(x, y);
  21. if (field.isFlagged(x, y))
  22. {
  23. auto imgflag = Gtk::make_managed<Gtk::Image>();
  24. imgflag->set(m_textureFlag);
  25. buttons.at(pos)->set_child(*imgflag);
  26. buttons.at(pos)->set_active(true);
  27. }
  28. else
  29. {
  30. buttons.at(pos)->unset_child();
  31. buttons.at(pos)->queue_draw();
  32. buttons.at(pos)->set_active(false);
  33. }
  34. }
  35. }
  36. void MainWindow::updateFlagsLabel(int flags)
  37. {
  38. Glib::ustring msg = Glib::ustring::compose("Remaining flags: %1", flags);
  39. flagLabel.set_label(msg);
  40. }
  41. // void MainWindow::OnNewButtonClick() {
  42. // newGame = true;
  43. // gameOver = false;
  44. // for (auto &button : buttons) {
  45. // button->set_active(false);
  46. // button->set_sensitive(true);
  47. // button->set_label("");
  48. // }
  49. // //field->remainingFlags = MINES;
  50. // Glib::ustring msg = Glib::ustring::compose("Remaining flags: %1", field->remainingFlags);
  51. // flagLabel.set_label(msg);
  52. // if (clockConn.connected()) clockConn.disconnect();
  53. // elapsedTime = 0;
  54. // clockConn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MainWindow::UpdateClockLabel), 100);
  55. // }
  56. void MainWindow::OnCellClick(int x, int y)
  57. {
  58. if (newGame)
  59. {
  60. field.initBombs(x, y);
  61. newGame = false;
  62. }
  63. if (field.isFlagged(x, y))
  64. {
  65. buttons.at(x + y * field.getRows())->set_active(true);
  66. }
  67. else
  68. {
  69. field.openCell(x, y);
  70. if (field.isBomb(x, y))
  71. {
  72. openBombs();
  73. }
  74. }
  75. }
  76. void MainWindow::openBombs()
  77. {
  78. for (int i = 0; i < field.getCols() * field.getRows(); i++)
  79. {
  80. int x = i % field.getCols();
  81. int y = i / field.getCols();
  82. buttons.at(i)->set_sensitive(false);
  83. if (field.isBomb(x, y))
  84. {
  85. if (field.isFlagged(x, y))
  86. {
  87. auto imgFlagBomb = std::make_shared<Gtk::Image>();
  88. imgFlagBomb->set(m_textureFlagBomb);
  89. buttons.at(i)->set_child(*imgFlagBomb);
  90. }
  91. else
  92. {
  93. auto imgBomb = std::make_shared<Gtk::Image>();
  94. imgBomb->set(m_textureBomb);
  95. buttons.at(i)->set_child(*imgBomb);
  96. }
  97. buttons.at(i)->set_active(true);
  98. }
  99. }
  100. }
  101. void MainWindow::updateCell(int x, int y)
  102. {
  103. int pos = x + y * field.getRows();
  104. if (field.isOpened(x, y))
  105. {
  106. if (field.bombsNearby(x, y) > 0)
  107. {
  108. switch (field.bombsNearby(x, y))
  109. {
  110. case 1:
  111. buttons.at(pos)->get_style_context()->add_class("label-1");
  112. break;
  113. case 2:
  114. buttons.at(pos)->get_style_context()->add_class("label-2");
  115. break;
  116. case 3:
  117. buttons.at(pos)->get_style_context()->add_class("label-3");
  118. break;
  119. case 4:
  120. buttons.at(pos)->get_style_context()->add_class("label-4");
  121. break;
  122. case 5:
  123. buttons.at(pos)->get_style_context()->add_class("label-5");
  124. break;
  125. case 6:
  126. buttons.at(pos)->get_style_context()->add_class("label-6");
  127. break;
  128. case 7:
  129. buttons.at(pos)->get_style_context()->add_class("label-7");
  130. break;
  131. case 8:
  132. buttons.at(pos)->get_style_context()->add_class("label-8");
  133. break;
  134. }
  135. buttons.at(pos)->set_label(Glib::ustring::format(field.bombsNearby(x, y)));
  136. }
  137. buttons.at(pos)->set_active(true);
  138. buttons.at(pos)->set_sensitive(false);
  139. }
  140. }
  141. // void MainWindow::ShowGameWonAnimation() {
  142. // // Limit the number of confetti images to 10
  143. // int confettiCount = 10;
  144. // for (int i = 0; i < confettiCount; ++i) {
  145. // Glib::signal_timeout().connect_once([this]() {
  146. // auto confetti = Gtk::make_managed<Gtk::Image>();
  147. // confetti->set_from_resource("/mineSweeper/confetti");
  148. // // Randomize position on the grid or overlay.
  149. // grid->attach(*confetti, rand() % COLS, rand() % COLS);
  150. // grid->queue_draw();
  151. // }, i * 100); // Add confetti with a delay of 100ms each
  152. // }
  153. // }
  154. // bool MainWindow::AllCellsOpened()
  155. // {
  156. // for(int i=0; i<COLS * COLS; i++) {
  157. // if (!buttons[i]->get_active())
  158. // return false;
  159. // }
  160. // return true;
  161. // }
  162. void MainWindow::gameOver()
  163. {
  164. // clockSignalConn.disconnect();
  165. // std::cout << "Signal gameOver emmited\n";
  166. }
  167. void MainWindow::updateClockLabel()
  168. {
  169. size_t time = 100; // field.getCurrentTime();
  170. int deciseconds = (time / 100) % 10;
  171. int seconds = (time / 1000) % 60;
  172. int minutes = (time / 60000) % 60;
  173. Glib::ustring msg = Glib::ustring::compose("Elapsed time: %1:%2.%3",
  174. Glib::ustring::format(std::setfill(L'0'), std::setw(2), minutes),
  175. Glib::ustring::format(std::setfill(L'0'), std::setw(2), seconds),
  176. Glib::ustring::format(std::setfill(L'0'), std::setw(1), deciseconds));
  177. clockLabel.set_label(msg);
  178. }
  179. void MainWindow::handleClockSig(size_t time)
  180. {
  181. (void)time;
  182. m_clockDispatch.emit();
  183. }
  184. MainWindow::MainWindow()
  185. {
  186. // ApplyStyles(); // Load the CSS file
  187. m_elapsedTime = 0;
  188. newGame = true;
  189. set_title("MineSweeper");
  190. set_default_size(400, 400);
  191. set_resizable(false);
  192. boxV = Gtk::Box(Gtk::Orientation::VERTICAL);
  193. boxH = Gtk::Box(Gtk::Orientation::HORIZONTAL);
  194. boxH.set_hexpand(true);
  195. boxV.append(boxH);
  196. boxH.set_expand(true);
  197. Gtk::Label labelMines;
  198. labelMines.set_margin_top(12);
  199. labelMines.set_margin_start(12);
  200. labelMines.set_label(Glib::ustring::compose("Total mines: %1", field.getTotalMines()));
  201. // labelMines.set_hexpand(true);
  202. Glib::ustring msg = Glib::ustring::compose("Remaining flags: %1", field.getRemainingFlags());
  203. flagLabel = Gtk::Label(msg);
  204. flagLabel.set_margin_top(12);
  205. flagLabel.set_margin_start(12);
  206. flagLabel.set_margin_end(12);
  207. // flagLabel.set_hexpand(true);
  208. clockLabel.set_margin_top(12);
  209. // clockLabel.set_margin_start(12);
  210. // Clocklabel.set_margin_end(12);
  211. clockLabel.set_hexpand(true);
  212. Glib::ustring clockmsg = Glib::ustring::compose("Elapsed time: 00:00.0");
  213. clockLabel.set_label(clockmsg);
  214. boxH.append(labelMines);
  215. boxH.append(clockLabel);
  216. boxH.append(flagLabel);
  217. // TODO check if it's okay to mix std::shared_ptr with Gdk::ptr
  218. m_textureBomb = Gdk::Texture::create_from_resource("/minesweeper/bomb-solid");
  219. m_textureFlag = Gdk::Texture::create_from_resource("/minesweeper/flag-solid");
  220. m_textureFlagBomb = Gdk::Texture::create_from_resource("/minesweeper/flag-bomb");
  221. // bombPix.set_from_resource("/minesweeper/bomb-solid");
  222. auto css_provider = Gtk::CssProvider::create();
  223. css_provider->load_from_data(
  224. ".label-1 { font-weight: bold; font-size: 1.5em; color: Blue; }\
  225. .label-2 { font-weight: bold; font-size: 1.5em; color: Green; }\
  226. .label-3 { font-weight: bold; font-size: 1.5em; color: Darkorange; }\
  227. .label-4 { font-weight: bold; font-size: 1.5em; color: Purple; }\
  228. .label-5 { font-weight: bold; font-size: 1.5em; color: Red; }\
  229. .label-6 { font-weight: bold; font-size: 1.5em; color: Salmon; }\
  230. .label-7 { font-weight: bold; font-size: 1.5em; color: Turquoise; }\
  231. .label-8 { font-weight: bold; font-size: 1.5em; color: Magenta; }");
  232. auto display = Gdk::Display::get_default();
  233. Gtk::StyleContext::add_provider_for_display(display, css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
  234. for (int i = 0; i < field.getCols() * field.getRows(); i++)
  235. {
  236. auto button = std::make_shared<Gtk::ToggleButton>();
  237. button->set_size_request(50, 40);
  238. button->set_sensitive(true);
  239. button->set_active(false);
  240. int x = i % field.getCols();
  241. int y = i / field.getRows();
  242. button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::OnCellClick), x, y));
  243. // button->get_style_context()->add_class("fixed-button");
  244. auto gesture = Gtk::GestureClick::create();
  245. gesture->set_button(3);
  246. gesture->signal_released().connect(sigc::bind(sigc::mem_fun(*this,
  247. &MainWindow::OnCellRightClick),
  248. i));
  249. button->add_controller(gesture);
  250. buttons.push_back(button);
  251. grid.attach(*button, x, y);
  252. }
  253. field.openCellSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateCell)));
  254. field.remainingFlagsSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateFlagsLabel)));
  255. field.gameOverSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::gameOver)));
  256. // newGameButton.set_label("New");
  257. // newGameButton.add_css_class("suggested-action");
  258. // newGameButton.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::OnNewButtonClick));
  259. // optionButton.set_icon_name("open-menu");
  260. // field.timerSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::handleClockSig)));
  261. m_clockDispatch.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateClockLabel)));
  262. // field.timerSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateClockLabel)));
  263. // if (clockSignalConn.connected()) clockSignalConn.disconnect();
  264. // elapsedTime = 0;
  265. // clockSignalConn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MainWindow::updateClockLabel), 100);
  266. // }
  267. // create the minefield
  268. // field = new MineField(COLS, MINES);
  269. // bar.pack_start(newGameButton);
  270. // bar.pack_end(optionButton);
  271. // grid.set_row_homogeneous(false);
  272. // grid.set_column_homogeneous(false);
  273. grid.set_margin(10);
  274. // grid.set_vexpand(true);
  275. // grid.set_hexpand(true);
  276. // grid.set_fill(false);
  277. boxV.append(grid);
  278. this->set_titlebar(bar);
  279. this->set_child(boxV);
  280. }
  281. int main(int argc, char **argv)
  282. {
  283. auto app = Gtk::Application::create("eu.bernardomagri.minesweeper");
  284. return app->make_window_and_run<MainWindow>(argc, argv);
  285. }