From d7aea435678eb83740f2bab19d4dc8a5c1621ed9 Mon Sep 17 00:00:00 2001 From: daleclack Date: Mon, 26 Feb 2024 22:16:09 +0800 Subject: [PATCH] Add Menu --- Gtk4/gtk158_minesweeper4/CMakeLists.txt | 2 +- Gtk4/gtk158_minesweeper4/src/InputBox.cpp | 0 Gtk4/gtk158_minesweeper4/src/InputBox.h | 0 Gtk4/gtk158_minesweeper4/src/MineSweeper.cc | 302 ------------------ Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp | 49 +++ Gtk4/gtk158_minesweeper4/src/MineSweeper.hh | 81 ----- .../src/{jsonfile.hh => jsonfile.h} | 0 7 files changed, 50 insertions(+), 384 deletions(-) create mode 100644 Gtk4/gtk158_minesweeper4/src/InputBox.cpp create mode 100644 Gtk4/gtk158_minesweeper4/src/InputBox.h delete mode 100644 Gtk4/gtk158_minesweeper4/src/MineSweeper.cc delete mode 100644 Gtk4/gtk158_minesweeper4/src/MineSweeper.hh rename Gtk4/gtk158_minesweeper4/src/{jsonfile.hh => jsonfile.h} (100%) diff --git a/Gtk4/gtk158_minesweeper4/CMakeLists.txt b/Gtk4/gtk158_minesweeper4/CMakeLists.txt index b883d37..dbfbbbd 100644 --- a/Gtk4/gtk158_minesweeper4/CMakeLists.txt +++ b/Gtk4/gtk158_minesweeper4/CMakeLists.txt @@ -26,7 +26,7 @@ link_directories (${GTK4_LIBRARY_DIRS}) # set(PO_DIR ${CMAKE_BINARY_DIR}/po/zh_CN/LC_MESSAGES) #Source files -set(SOURCE_FILE src/main.cpp src/MineSweeper.cpp src/MineCell.cpp) +set(SOURCE_FILE src/main.cpp src/MineSweeper.cpp src/MineCell.cpp src/InputBox.cpp) #Compile Resource diff --git a/Gtk4/gtk158_minesweeper4/src/InputBox.cpp b/Gtk4/gtk158_minesweeper4/src/InputBox.cpp new file mode 100644 index 0000000..e69de29 diff --git a/Gtk4/gtk158_minesweeper4/src/InputBox.h b/Gtk4/gtk158_minesweeper4/src/InputBox.h new file mode 100644 index 0000000..e69de29 diff --git a/Gtk4/gtk158_minesweeper4/src/MineSweeper.cc b/Gtk4/gtk158_minesweeper4/src/MineSweeper.cc deleted file mode 100644 index 321d121..0000000 --- a/Gtk4/gtk158_minesweeper4/src/MineSweeper.cc +++ /dev/null @@ -1,302 +0,0 @@ -#include "MineSweeper.hh" -#include -#include - -MineSweeper::MineSweeper() - : main_box(Gtk::Orientation::VERTICAL, 5), - btn_box(Gtk::Orientation::HORIZONTAL, 5), - cell(nullptr) -{ - // Initalize Window - set_title("MineSweeper"); - set_titlebar(header); - header.set_show_title_buttons(); - header.set_decoration_layout("close,minimize,maximize:menu"); - header.pack_end(menu_btn); - set_icon_name("org.gtk.daleclack"); - - // Initalize Menu - menu_builder = Gtk::Builder::create_from_resource("/org/gtk/daleclack/mine_menu.xml"); - auto object = menu_builder->get_object("mine_menu"); - popover1.set_menu_model(object); - // popover1.show(); - menu_btn.set_popover(popover1); - - // Add Actions - add_action("new_game", sigc::mem_fun(*this, &MineSweeper::new_game)); - add_action("scores", sigc::mem_fun(*this, &MineSweeper::show_scores)); - add_action("show_mines", sigc::mem_fun(*this, &MineSweeper::show_mines)); - add_action("quit", sigc::mem_fun(*this, &MineSweeper::hide)); - - // Default setting - reset_game(); - - // Buttons - btnstart.set_label("Start/Reset"); - btnpause.set_label("Pause"); - btnshow.set_label("Show All"); - btnexit.set_label("Exit"); - btn_box.append(btnstart); - btn_box.append(btnpause); - btn_box.append(btnshow); - btn_box.append(btnexit); - btnstart.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::new_game)); - btnshow.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::show_mines)); - btnexit.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::hide)); - - // Pack widgets - status_label.set_halign(Gtk::Align::CENTER); - btn_box.set_halign(Gtk::Align::CENTER); - mine_grid.set_halign(Gtk::Align::CENTER); - main_box.append(status_label); - main_box.append(mine_grid); - main_box.append(btn_box); - - // Create a dialog - input_dialog = InputBox::create(); - - // Create Scores Window - scores_win = ScoresWin::create(); - - // Bind windows - input_dialog->set_transient_for(*this); - scores_win->set_transient_for(*this); - input_dialog->set_scores_window(scores_win); - - // Show everything - set_child(main_box); - //show_all_children(); -} - -MineSweeper::~MineSweeper(){ - // Delete all resources - delete input_dialog; - delete scores_win; - if(cell != nullptr){ - delete[] cell; - } -} - -void MineSweeper::new_game(){ - // New game = reset game - reset_game(); -} - -void MineSweeper::reset_game(int width, int height, int mines) -{ - // Clear the cells - if(cell != nullptr){ - delete[] cell; - } - - cell = new MineCell[width * height]; - // Reset timer - mytimer.disconnect(); - timer_count = 0; - mytimer = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MineSweeper::timer_func), 1000); - - mine_count = 0; - mines_clear = 0; - mine_grid.set_sensitive(); - // Reset all data - for (int i = 0; i < width; i++) - { - for (int j = 0; j < height; j++) - { - // Initalize cell - cell[i * 7 + j].set_has_frame(); - cell[i * 7 + j].set_image_from_icon_name("", Gtk::IconSize::LARGE); - // cell[i * 7 + j].set_always_show_image(); - cell[i * 7 + j].set_size_request(40, 40); - cell[i * 7 + j].mines_around = 0; - cell[i * 7 + j].has_mine = false; - cell[i * 7 + j].cleared = false; - } - } - - // Reset mines - while (mine_count < mines) - { - int index = g_random_int_range(0, width * height); - if (!(cell[index].has_mine)) - { - cell[index].has_mine = true; - // cell[index].set_label("x"); - mine_count++; - } - } - // std::cout << mine_count << std::endl; - // game_ended = false; - // winned = true; - game_status = GameStatus::Running; - status_label.set_label(" "); - calc_mines(); - - // Append buttons to grid - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { - // cell[i * 7 + j].set_label("?"); - cell[i * width + j].signal_clicked().connect(sigc::bind( - sigc::mem_fun(*this, &MineSweeper::cell_clicked), &cell[i * width + j])); - mine_grid.attach(cell[i * width + j], j, i); - cell[i * width + j].set_has_frame(); - cell[i * width + j].x = j; - cell[i * width + j].y = i; - cell[i * width + j].cleared = false; - } - } - - mine_grid.show(); -} - -void MineSweeper::pause_game(){ - -} - -void MineSweeper::calc_mines() -{ - // Calculate the mines around a cell - for (int i = 0; i < 7; i++) - { - for (int j = 0; j < 7; j++) - { - int index1, index2; - // The Search cell should not over the grids - for (index1 = MAX(0, i - 1); index1 < MIN(i + 1, 6) + 1; index1++) - { - for (index2 = MAX(0, j - 1); index2 < MIN(j + 1, 6) + 1; index2++) - { - if ((cell[index1 * 7 + index2].has_mine)) - { - cell[i * 7 + j].mines_around++; - } - } - } - } - } -} - -void MineSweeper::show_mines() -{ - // Show all cell with a mine - for (int i = 0; i < 49; i++) - { - if (cell[i].has_mine) - { - cell[i].set_image_from_icon_name("mine", Gtk::IconSize::LARGE); - } - } -} - -void MineSweeper::show_scores(){ - // Show Scores Window - input_dialog->read_scores(); -} - -void MineSweeper::game_lost(int explode_index){ - // When a cell with mine is clicked, show other mines - for (int i = 0; i < 49; i++) - { - if (cell[i].has_mine && i != explode_index) - { - cell[i].set_image_from_icon_name("mine", Gtk::IconSize::LARGE); - } - } -} - -bool MineSweeper::timer_func() -{ - // Set timer - char tmp[50]; - timer_count++; - sprintf(tmp, "Time:%d", timer_count); - status_label.set_label(tmp); - return true; -} - -void MineSweeper::cell_clicked(MineCell *cell1) -{ - cell1->set_has_frame(false); - if (game_status == GameStatus::Running && !cell1->cleared) - { - // - // If get mine, the game will end now - if (cell1->has_mine) - { - // Set game to stop - // winned = false; - game_status = GameStatus::Ended; - cell1->cleared = true; - cell1->set_image_from_icon_name("exploded", Gtk::IconSize::LARGE); - - // End the game - game_lost(cell1->y * 7 + cell1->x); - status_label.set_label("You lost!"); - // game_ended = true; - mytimer.disconnect(); - mine_grid.set_sensitive(false); - } - else - { - // If no mines, check the cell around - check_mines(cell1->x, cell1->y); - } - } -} - -void MineSweeper::check_mines(int pos_x, int pos_y) -{ - if (pos_x >= 0 && pos_x <= 6 && - pos_y >= 0 && pos_y <= 6) - { - if (!cell[pos_y * 7 + pos_x].has_mine && - !cell[pos_y * 7 + pos_x].cleared) - { - mines_clear++; - // Show the cell has no mines around - if (cell[pos_y * 7 + pos_x].mines_around == 0) - { - cell[pos_y * 7 + pos_x].set_image_from_icon_name("", Gtk::IconSize::LARGE); - } - else - { - // Show the numbers of mines around a cell - char *label = g_strdup_printf("%dmines", cell[pos_y * 7 + pos_x].mines_around); - cell[pos_y * 7 + pos_x].set_image_from_icon_name(label, Gtk::IconSize::LARGE); - g_free(label); - } - - // make the cell without mines cleared - cell[pos_y * 7 + pos_x].set_has_frame(false); - cell[pos_y * 7 + pos_x].cleared = true; - - // Check the cells around a cell that has no mines - if (cell[pos_y * 7 + pos_x].mines_around == 0) - { - check_mines((pos_x - 1), (pos_y - 1)); - check_mines((pos_x + 1), (pos_y + 1)); - check_mines((pos_x - 1), (pos_y + 1)); - check_mines((pos_x + 1), (pos_y - 1)); - check_mines(pos_x, (pos_y - 1)); - check_mines(pos_x, (pos_y + 1)); - check_mines((pos_x + 1), pos_y); - check_mines((pos_x - 1), pos_y); - } - } - } - - // If all the mines has cleared, you has winned - if (mines_clear == 40) - { - // Stop the game - status_label.set_label("You winned!"); - game_status = GameStatus::Winned; - mytimer.disconnect(); - - // Save the time of game - input_dialog->set_game_time(timer_count); - input_dialog->show(); - } -} diff --git a/Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp b/Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp index b613b51..d25b1a7 100644 --- a/Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp +++ b/Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp @@ -16,6 +16,10 @@ struct _MineSweeper { GtkApplicationWindow parent_instance; + // Header widgets + GtkWidget *header, *menu_btn; + GtkBuilder *menu_builder; + // Child widgets GtkWidget *main_box, *btn_box; GtkWidget *mine_grid; @@ -284,10 +288,55 @@ static void btnshow_clicked(GtkButton *btn, MineSweeper *self) } } +// Signal Handler for menus +static void newgame_activated(GSimpleAction *action, GVariant *parmeter, gpointer data) +{ + btnstart_clicked(NULL, MINE_SWEEPER(data)); +} + +static void scores_activated(GSimpleAction *action, GVariant *parmeter, gpointer data) +{ +} + +static void showmines_activated(GSimpleAction *action, GVariant *parmeter, gpointer data) +{ + btnshow_clicked(NULL, MINE_SWEEPER(data)); +} + +static void quit_activated(GSimpleAction *action, GVariant *parmeter, gpointer data) +{ + gtk_window_destroy(GTK_WINDOW(data)); +} + static void mine_sweeper_init(MineSweeper *self) { // Initalize window gtk_window_set_title(GTK_WINDOW(self), "MineSweeper"); + self->header = gtk_header_bar_new(); + gtk_window_set_titlebar(GTK_WINDOW(self), self->header); + + // Add action for menu + GActionEntry entries[] = + { + {"new_game", newgame_activated, NULL, NULL, NULL}, + {"scores", scores_activated, NULL, NULL, NULL}, + {"show_mines", showmines_activated, NULL, NULL, NULL}, + {"quit", quit_activated, NULL, NULL, NULL}}; + g_action_map_add_action_entries(G_ACTION_MAP(self), entries, + G_N_ELEMENTS(entries), self); + + // Create Menu and button + self->menu_btn = gtk_menu_button_new(); + gtk_menu_button_set_icon_name(GTK_MENU_BUTTON(self->menu_btn), "open-menu"); + gtk_header_bar_pack_end(GTK_HEADER_BAR(self->header), self->menu_btn); + + // Create Menu + self->menu_builder = gtk_builder_new_from_resource("/org/gtk/daleclack/mine_menu.xml"); + GMenuModel *model = G_MENU_MODEL(gtk_builder_get_object(self->menu_builder, "mine_menu")); + gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(self->menu_btn), model); + GtkPopover *popover = gtk_menu_button_get_popover(GTK_MENU_BUTTON(self->menu_btn)); + gtk_popover_set_has_arrow(popover, FALSE); + gtk_widget_set_halign(GTK_WIDGET(popover), GTK_ALIGN_END); // Create widgets self->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); diff --git a/Gtk4/gtk158_minesweeper4/src/MineSweeper.hh b/Gtk4/gtk158_minesweeper4/src/MineSweeper.hh deleted file mode 100644 index 5a25fa9..0000000 --- a/Gtk4/gtk158_minesweeper4/src/MineSweeper.hh +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include -#include "InputBox.hh" -#include "ScoresWin.hh" - -// The status of the minesweeper game -enum class GameStatus -{ - Running, - Winned, - Ended, - Paused -}; - -// The cell of the minesweeper game, -// and the mines is under the cell -class MineCell : public Gtk::Button -{ -public: - bool has_mine = false; // Whether the grid has mine - bool cleared = false; // Whether the mine is cleared - int mines_around; // The number near the grid - int x, y; // The Position of the grid - MineCell() - { - // Set button style - mines_around = 0; - } -}; - -// Main class -class MineSweeper : public Gtk::ApplicationWindow -{ -public: - MineSweeper(); - ~MineSweeper(); - -private: - // HeaderBar - Gtk::HeaderBar header; - Gtk::MenuButton menu_btn; - Gtk::PopoverMenu popover1; - - // Child widgets - Gtk::Grid mine_grid; - Gtk::Label status_label; - Gtk::Box main_box, btn_box; - Gtk::Button btnstart, btnpause, btnshow, btnexit; - - // The cell to place mines - MineCell *cell; - GameStatus game_status; // Use enum class for the status of game - // bool winned, game_ended; // The status of game(win/end) - int mines_clear, mine_count; // Whether the mine is cleared - - // Menu - Glib::RefPtr menu_builder; - - // Timer - int timer_count; - sigc::connection mytimer; - - // Input dialog - InputBox *input_dialog; - - // Scores Window - ScoresWin *scores_win; - - // Signal Handlers - void new_game(); // "New Game" handler - void reset_game(int width = 7, int height = 7, int mines = 9); // Reset all mines - void pause_game(); // Pause or continue the game - void calc_mines(); // Get the mines around - void show_mines(); // Show all mines - void show_scores(); // Show all scores - void game_lost(int explode_index); // You lost the game - void cell_clicked(MineCell *cell1); // Open a cell - bool timer_func(); // Timer - void check_mines(int pos_x, int pos_y); // Check if there is a mine -}; diff --git a/Gtk4/gtk158_minesweeper4/src/jsonfile.hh b/Gtk4/gtk158_minesweeper4/src/jsonfile.h similarity index 100% rename from Gtk4/gtk158_minesweeper4/src/jsonfile.hh rename to Gtk4/gtk158_minesweeper4/src/jsonfile.h