|
|
@@ -0,0 +1,333 @@
|
|
|
+#include "window.hpp"
|
|
|
+
|
|
|
+
|
|
|
+//}
|
|
|
+// void MainWindow::ApplyStyles() {
|
|
|
+// // Load and apply the CSS file
|
|
|
+// auto css_provider = Gtk::CssProvider::create();
|
|
|
+// css_provider->load_from_path("style.css");
|
|
|
+// Gtk::StyleContext::add_provider_for_display(Gdk::Display::get_default(), css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
|
+// }
|
|
|
+
|
|
|
+void MainWindow::OnCellRightClick(int n_press, double n_x, double n_y, int index) {
|
|
|
+ (void)n_press, (void)n_x, (void)n_y;
|
|
|
+ int x = index % field.getCols();
|
|
|
+ int y = index / field.getRows();
|
|
|
+
|
|
|
+ if(field.isCleared(x, y) == false) {
|
|
|
+ field.toggleFlag(x, y);
|
|
|
+ if(field.isFlagged(x, y)) {
|
|
|
+ auto imgflag = Gtk::make_managed<Gtk::Image>();
|
|
|
+ imgflag->set(m_pixbufFlag);
|
|
|
+ buttons.at(x + y * field.getRows())->set_child(*imgflag);
|
|
|
+ buttons.at(x + y * field.getRows())->set_active(true);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ buttons.at(x + y * field.getRows())->unset_child();
|
|
|
+ buttons.at(x+ y * field.getRows())->queue_draw();
|
|
|
+ buttons.at(x + y * field.getRows())->set_active(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Glib::ustring msg = Glib::ustring::compose("Remaining flags: %1", field.getRemainingFlags());
|
|
|
+ // flagLabel.set_label(msg);
|
|
|
+}
|
|
|
+
|
|
|
+void MainWindow::updateFlagsLabel(int flags) {
|
|
|
+ Glib::ustring msg = Glib::ustring::compose("Remaining flags: %1", flags);
|
|
|
+ flagLabel.set_label(msg);
|
|
|
+}
|
|
|
+// void MainWindow::OnNewButtonClick() {
|
|
|
+// newGame = true;
|
|
|
+// gameOver = false;
|
|
|
+
|
|
|
+// for (auto &button : buttons) {
|
|
|
+// button->set_active(false);
|
|
|
+// button->set_sensitive(true);
|
|
|
+// button->set_label("");
|
|
|
+// }
|
|
|
+
|
|
|
+// field->remainingFlags = MINES;
|
|
|
+// Glib::ustring msg = Glib::ustring::compose("Remaining flags: %1", field->remainingFlags);
|
|
|
+// flagLabel.set_label(msg);
|
|
|
+
|
|
|
+// if (clockConn.connected()) clockConn.disconnect();
|
|
|
+// elapsedTime = 0;
|
|
|
+// clockConn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MainWindow::UpdateClockLabel), 100);
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+// void MainWindow::OpenNearCells(int index, std::set<int> &visited) {
|
|
|
+// int cols = field->Cols();
|
|
|
+// int x = index % cols;
|
|
|
+// int y = index / cols;
|
|
|
+
|
|
|
+// if (visited.count(index)) return;
|
|
|
+
|
|
|
+// Cell* cell = field->GetCell(x, y);
|
|
|
+// if (!cell || cell->bombsNearby > 0 || cell->type == CellType::Bomb) return;
|
|
|
+
|
|
|
+// visited.insert(index);
|
|
|
+// buttons[index]->set_active(true);
|
|
|
+
|
|
|
+// for (int i = -1; i <= 1; i++) {
|
|
|
+// for (int j = -1; j <= 1; j++) {
|
|
|
+// if (i == 0 && j == 0) continue; // Skip the current cell
|
|
|
+
|
|
|
+// int nx = x + i;
|
|
|
+// int ny = y + j;
|
|
|
+// int newIndex = ny * cols + nx;
|
|
|
+// Cell* neighborCell = field->GetCell(nx, ny);
|
|
|
+
|
|
|
+// // Bounds check before recursive call
|
|
|
+// if (nx >= 0 && nx < cols && ny >= 0 && ny < cols) {
|
|
|
+// if (visited.count(newIndex) == 0) {
|
|
|
+// OpenNearCells(newIndex, visited);
|
|
|
+// }
|
|
|
+// if (neighborCell && !buttons[newIndex]->get_active() && !neighborCell->isFlag) {
|
|
|
+// OpenNearCells(newIndex, visited);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void MainWindow::OnCellClick(int x, int y) {
|
|
|
+ if (newGame) {
|
|
|
+ field.initBombs(x, y);
|
|
|
+ newGame = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(field.isFlagged(x, y)) {
|
|
|
+ buttons.at(x + y * field.getRows())->set_active(true);
|
|
|
+ }
|
|
|
+ else if(field.isBomb(x, y)) {
|
|
|
+ openBombs();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ field.clearCell(x, y);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void MainWindow::openBombs() {
|
|
|
+ for(int i=0; i < field.getCols() * field.getRows(); i++) {
|
|
|
+ int x = i % field.getCols();
|
|
|
+ int y = i / field.getRows();
|
|
|
+
|
|
|
+ buttons.at(i)->set_sensitive(false);
|
|
|
+
|
|
|
+ if(field.isBomb(x, y)) {
|
|
|
+ if(field.isFlagged(x, y)) {
|
|
|
+ auto imgFlagBomb = std::make_shared<Gtk::Image>();
|
|
|
+ imgFlagBomb->set(m_pixbufFlagBomb);
|
|
|
+ buttons.at(i)->set_child(*imgFlagBomb);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ auto imgBomb = std::make_shared<Gtk::Image>();
|
|
|
+ imgBomb->set(m_pixbufBomb);
|
|
|
+ buttons.at(i)->set_child(*imgBomb);
|
|
|
+ }
|
|
|
+ buttons.at(i)->set_active(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void MainWindow::updateCell(int x, int y) {
|
|
|
+ if(field.isCleared(x, y)) {
|
|
|
+ if (field.bombsNearby(x, y) > 0) {
|
|
|
+ switch(field.bombsNearby(x, y)) {
|
|
|
+ case 1:
|
|
|
+ buttons.at(x + y * field.getRows())->get_style_context()->add_class("label-1");
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ buttons.at(x + y * field.getRows())->get_style_context()->add_class("label-2");
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ buttons.at(x + y * field.getRows())->get_style_context()->add_class("label-3");
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ buttons.at(x + y * field.getRows())->get_style_context()->add_class("label-4");
|
|
|
+ break;
|
|
|
+ case 5:
|
|
|
+ buttons.at(x + y * field.getRows())->get_style_context()->add_class("label-5");
|
|
|
+ break;
|
|
|
+ case 6:
|
|
|
+ buttons.at(x + y * field.getRows())->get_style_context()->add_class("label-6");
|
|
|
+ break;
|
|
|
+ case 7:
|
|
|
+ buttons.at(x + y * field.getRows())->get_style_context()->add_class("label-7");
|
|
|
+ break;
|
|
|
+ case 8:
|
|
|
+ buttons.at(x + y * field.getRows())->get_style_context()->add_class("label-8");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ buttons.at(x + y * field.getRows())->set_label(Glib::ustring::format(field.bombsNearby(x, y)));
|
|
|
+ }
|
|
|
+ buttons.at(x + y * field.getRows())->set_active(true);
|
|
|
+ buttons.at(x + y * field.getRows())->set_sensitive(false);
|
|
|
+ }
|
|
|
+}
|
|
|
+// void MainWindow::ShowGameWonAnimation() {
|
|
|
+// // Limit the number of confetti images to 10
|
|
|
+// int confettiCount = 10;
|
|
|
+// for (int i = 0; i < confettiCount; ++i) {
|
|
|
+// Glib::signal_timeout().connect_once([this]() {
|
|
|
+// auto confetti = Gtk::make_managed<Gtk::Image>();
|
|
|
+// confetti->set_from_resource("/mineSweeper/confetti");
|
|
|
+// // Randomize position on the grid or overlay.
|
|
|
+// grid->attach(*confetti, rand() % COLS, rand() % COLS);
|
|
|
+// grid->queue_draw();
|
|
|
+// }, i * 100); // Add confetti with a delay of 100ms each
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+// bool MainWindow::AllCellsOpened()
|
|
|
+// {
|
|
|
+// for(int i=0; i<COLS * COLS; i++) {
|
|
|
+// if (!buttons[i]->get_active())
|
|
|
+// return false;
|
|
|
+// }
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+// bool MainWindow::UpdateClockLabel()
|
|
|
+// {
|
|
|
+// if(gameOver) return false;
|
|
|
+
|
|
|
+// elapsedTime++;
|
|
|
+
|
|
|
+// int deciseconds = elapsedTime % 10;
|
|
|
+// int seconds = (elapsedTime / 10) % 60;
|
|
|
+// int minutes = (elapsedTime /600) % 60;
|
|
|
+
|
|
|
+// Glib::ustring msg = Glib::ustring::compose("Elapsed time: %1:%2.%3", \
|
|
|
+// Glib::ustring::format(std::setfill(L'0'), std::setw(2), minutes), \
|
|
|
+// Glib::ustring::format(std::setfill(L'0'), std::setw(2), seconds), \
|
|
|
+// Glib::ustring::format(std::setfill(L'0'), std::setw(1), deciseconds));
|
|
|
+// clockLabel.set_label(msg);
|
|
|
+
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+
|
|
|
+MainWindow::MainWindow()
|
|
|
+{
|
|
|
+ // ApplyStyles(); // Load the CSS file
|
|
|
+ elapsedTime = 0;
|
|
|
+ newGame = true;
|
|
|
+ set_title("MineSweeper");
|
|
|
+ set_default_size(400, 400);
|
|
|
+ set_resizable(false);
|
|
|
+
|
|
|
+ boxV = Gtk::Box(Gtk::Orientation::VERTICAL);
|
|
|
+ boxH = Gtk::Box(Gtk::Orientation::HORIZONTAL);
|
|
|
+
|
|
|
+ boxH.set_hexpand(true);
|
|
|
+
|
|
|
+ boxV.append(boxH);
|
|
|
+ boxH.set_expand(true);
|
|
|
+
|
|
|
+ Gtk::Label labelMines;
|
|
|
+ labelMines.set_margin_top(12);
|
|
|
+ labelMines.set_margin_start(12);
|
|
|
+ labelMines.set_label(Glib::ustring::compose("Total mines: %1", field.getTotalMines()));
|
|
|
+ //labelMines.set_hexpand(true);
|
|
|
+
|
|
|
+ Glib::ustring msg = Glib::ustring::compose("Remaining flags: %1", field.getRemainingFlags());
|
|
|
+ flagLabel = Gtk::Label(msg);
|
|
|
+ flagLabel.set_margin_top(12);
|
|
|
+ flagLabel.set_margin_start(12);
|
|
|
+ flagLabel.set_margin_end(12);
|
|
|
+ //flagLabel.set_hexpand(true);
|
|
|
+
|
|
|
+ clockLabel.set_margin_top(12);
|
|
|
+ clockLabel.set_margin_start(12);
|
|
|
+ clockLabel.set_margin_end(12);
|
|
|
+ clockLabel.set_hexpand(true);
|
|
|
+ Glib::ustring clockmsg = Glib::ustring::compose("Elapsed time: 00:00.0");
|
|
|
+ clockLabel.set_label(clockmsg);
|
|
|
+
|
|
|
+ boxH.append(labelMines);
|
|
|
+ boxH.append(clockLabel);
|
|
|
+ boxH.append(flagLabel);
|
|
|
+
|
|
|
+
|
|
|
+ //TODO check if it's okay to mix std::shared_ptr with Gdk::ptr
|
|
|
+ m_pixbufBomb = Gdk::Pixbuf::create_from_resource("/minesweeper/bomb-solid");
|
|
|
+ m_pixbufFlag = Gdk::Pixbuf::create_from_resource("/minesweeper/flag-solid");
|
|
|
+ m_pixbufFlagBomb = Gdk::Pixbuf::create_from_resource("/minesweeper/flag-bomb");
|
|
|
+
|
|
|
+ // bombPix.set_from_resource("/minesweeper/bomb-solid");
|
|
|
+
|
|
|
+ auto css_provider = Gtk::CssProvider::create();
|
|
|
+ css_provider->load_from_data(
|
|
|
+ ".label-1 { font-weight: bold; font-size: 1.5em; color: Blue; }\
|
|
|
+ .label-2 { font-weight: bold; font-size: 1.5em; color: Green; }\
|
|
|
+ .label-3 { font-weight: bold; font-size: 1.5em; color: Darkorange; }\
|
|
|
+ .label-4 { font-weight: bold; font-size: 1.5em; color: Purple; }\
|
|
|
+ .label-5 { font-weight: bold; font-size: 1.5em; color: Red; }\
|
|
|
+ .label-6 { font-weight: bold; font-size: 1.5em; color: Salmon; }\
|
|
|
+ .label-7 { font-weight: bold; font-size: 1.5em; color: Turquoise; }\
|
|
|
+ .label-8 { font-weight: bold; font-size: 1.5em; color: Magenta; }");
|
|
|
+
|
|
|
+ auto display = Gdk::Display::get_default();
|
|
|
+ Gtk::StyleContext::add_provider_for_display(display, css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
|
+
|
|
|
+ for (int i = 0; i < field.getCols() * field.getRows(); i++) {
|
|
|
+ auto button = std::make_shared<Gtk::ToggleButton>();
|
|
|
+ button->set_size_request(50, 40);
|
|
|
+ button->set_sensitive(true);
|
|
|
+ button->set_active(false);
|
|
|
+ int x = i % field.getCols();
|
|
|
+ int y = i / field.getRows();
|
|
|
+ button->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::OnCellClick), x, y));
|
|
|
+
|
|
|
+ //button->get_style_context()->add_class("fixed-button");
|
|
|
+
|
|
|
+ auto gesture = Gtk::GestureClick::create();
|
|
|
+ gesture->set_button(3);
|
|
|
+ gesture->signal_released().connect(sigc::bind(sigc::mem_fun(*this, \
|
|
|
+ &MainWindow::OnCellRightClick), i));
|
|
|
+ button->add_controller(gesture);
|
|
|
+
|
|
|
+ buttons.push_back(button);
|
|
|
+
|
|
|
+ grid.attach(*button, x, y);
|
|
|
+ }
|
|
|
+
|
|
|
+ field.clearCellSignal.connect(sigc::bind(sigc::mem_fun(*this, &MainWindow::updateCell)));
|
|
|
+ field.remainingFlagsChangedSignal.connect(sigc::bind(sigc::mem_fun(*this, \
|
|
|
+ &MainWindow::updateFlagsLabel)));
|
|
|
+ //newGameButton.set_label("New");
|
|
|
+ //newGameButton.add_css_class("suggested-action");
|
|
|
+ //newGameButton.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::OnNewButtonClick));
|
|
|
+
|
|
|
+ //optionButton.set_icon_name("open-menu");
|
|
|
+
|
|
|
+ //create the minefield
|
|
|
+ //field = new MineField(COLS, MINES);
|
|
|
+
|
|
|
+ //bar.pack_start(newGameButton);
|
|
|
+ //bar.pack_end(optionButton);
|
|
|
+
|
|
|
+ //grid.set_row_homogeneous(false);
|
|
|
+ //grid.set_column_homogeneous(false);
|
|
|
+ grid.set_margin(10);
|
|
|
+ //grid.set_vexpand(true);
|
|
|
+ //grid.set_hexpand(true);
|
|
|
+ // grid.set_fill(false);
|
|
|
+
|
|
|
+ boxV.append(grid);
|
|
|
+
|
|
|
+ this->set_titlebar(bar);
|
|
|
+ this->set_child(boxV);
|
|
|
+}
|
|
|
+
|
|
|
+int main(int argc, char **argv) {
|
|
|
+ auto app = Gtk::Application::create("eu.minesweeper");
|
|
|
+ return app->make_window_and_run<MainWindow>(argc, argv);
|
|
|
+}
|