#include "window.hpp" #include "gdkmm/pixbuf.h" #include "gtkmm/cssprovider.h" #include "gtkmm/image.h" #include "sigc++/adaptors/bind.h" #include "sigc++/functors/mem_fun.h" #include //} // 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(); 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 &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(); imgFlagBomb->set(m_pixbufFlagBomb); buttons.at(i)->set_child(*imgFlagBomb); } else { auto imgBomb = std::make_shared(); 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(); // 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; iget_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(); 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(argc, argv); }