window.cpp~ 11 KB

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