Update MineSweeper app

This commit is contained in:
daleclack 2024-02-29 22:42:21 +08:00
parent 8bdcbeb9d3
commit 33196352f7
9 changed files with 496 additions and 8 deletions

View File

@ -30,7 +30,8 @@ set(SOURCES src/core/main.cpp src/core/MainWin.cpp src/core/MyStack.cpp
src/calc_app/CalcApp.cpp src/run_app/RunApp.cpp src/draw_app/DrawApp.cpp
src/game24_app/Game24.cpp src/game24_app/Game24App.cpp src/text_app/TextEditor.cpp
src/text_app/MyInfoBar.cpp src/image_app/ImageApp.cpp src/image_app/MyImage.cpp
src/mine_app/MineSweeper.cpp src/mine_app/MineCell.cpp)
src/mine_app/MineSweeper.cpp src/mine_app/MineCell.cpp src/mine_app/InputBox.cpp
src/mine_app/ScoresItem.cpp src/mine_app/ScoresWin.cpp)
#Compile resources with GCR_CMake

View File

@ -0,0 +1,152 @@
#include <vector>
#include <string>
#include <fstream>
#include "InputBox.h"
#include "ScoresWin.h"
#include "jsonfile.h"
struct _InputBox
{
GtkWindow parent_instance;
GtkWidget *main_box, *btn_box;
GtkWidget *entry_name;
GtkWidget *scores_check;
GtkWidget *btn_ok, *btn_cancel;
int game_time;
// Scores Window
ScoresWin *scores_win;
};
G_DEFINE_TYPE(InputBox, input_box, GTK_TYPE_WINDOW)
static std::vector<std::string> names;
static std::vector<int> times;
static json data;
static void btnok_clicked(GtkButton *btn, InputBox *self)
{
// Save score to json file
// Open a file to save json data
std::fstream outfile;
outfile.open("scores.json", std::ios_base::out);
if (outfile.is_open())
{
// Insert data to json
const char *c_name = gtk_editable_get_text(GTK_EDITABLE(self->entry_name));
std::string name = std::string(c_name);
names.push_back(name);
times.push_back(self->game_time);
data["name"] = names;
data["time"] = times;
// Output data
outfile << data;
}
outfile.close();
// Show Scores window
if (gtk_check_button_get_active(GTK_CHECK_BUTTON(self->scores_check)))
{
scores_win_update_and_show(self->scores_win);
}
gtk_window_close(GTK_WINDOW(self));
}
static gboolean input_box_closed(GtkWidget *win, InputBox *self)
{
// Hide the window
gtk_widget_set_visible(win, FALSE);
return TRUE;
}
static void input_box_init(InputBox *self)
{
// Initalize window
gtk_window_set_default_size(GTK_WINDOW(self), 300, 150);
gtk_window_set_icon_name(GTK_WINDOW(self), "mines_app");
// Create widgets
self->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
self->btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
self->entry_name = gtk_entry_new();
self->scores_check = gtk_check_button_new_with_label("Show scores window");
self->btn_ok = gtk_button_new_with_label("OK");
self->btn_cancel = gtk_button_new_with_label("Cancel");
// Create Scores Window
GtkWindow *window = gtk_window_get_transient_for(GTK_WINDOW(self));
self->scores_win = scores_win_new(window);
// Link signals
g_signal_connect(self->btn_ok, "clicked", G_CALLBACK(btnok_clicked), self);
g_signal_connect(self, "close-request", G_CALLBACK(input_box_closed), self);
g_signal_connect_swapped(self->btn_cancel, "clicked", G_CALLBACK(gtk_window_close), self);
// Pack widgets
gtk_box_append(GTK_BOX(self->main_box), self->entry_name);
gtk_box_append(GTK_BOX(self->main_box), self->scores_check);
gtk_box_append(GTK_BOX(self->btn_box), self->btn_ok);
gtk_box_append(GTK_BOX(self->btn_box), self->btn_cancel);
gtk_box_append(GTK_BOX(self->main_box), self->btn_box);
gtk_widget_set_margin_bottom(self->main_box, 10);
gtk_widget_set_margin_end(self->main_box, 10);
gtk_widget_set_margin_start(self->main_box, 10);
gtk_widget_set_margin_top(self->main_box, 10);
gtk_widget_set_halign(self->btn_box, GTK_ALIGN_END);
// gtk_widget_set_halign(self->main_box, GTK_ALIGN_CENTER);
gtk_widget_set_valign(self->main_box, GTK_ALIGN_CENTER);
gtk_window_set_child(GTK_WINDOW(self), self->main_box);
}
static void input_box_class_init(InputBoxClass *klass)
{
}
InputBox *input_box_new(GtkWindow *parent)
{
return Input_Box(g_object_new(input_box_get_type(),
"transient-for", parent, NULL));
}
void input_box_present(InputBox *self)
{
// Show the input box
gtk_window_present(GTK_WINDOW(self));
}
void input_box_set_game_time(InputBox *self, int time)
{
// Try to open json file
std::fstream jsonfile;
jsonfile.open("scores.json", std::ios_base::in);
// If json file opened, read the data
if (jsonfile.is_open())
{
data = json::parse(jsonfile);
std::vector<std::string> names1 = data["name"];
std::vector<int> times1 = data["time"];
names = names1;
times = times1;
}
else
{
// Otherwist, create json data
data = json::parse(R"(
{
"name":[" "],
"time":[0]
}
)");
}
jsonfile.close();
// Set game time for input box
self->game_time = time;
}
void input_box_show_scores(InputBox *self)
{
scores_win_update_and_show(self->scores_win);
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(InputBox, input_box, Input, Box, GtkWindow)
InputBox *input_box_new(GtkWindow *parent);
void input_box_set_game_time(InputBox *self, int time);
void input_box_present(InputBox *self);
void input_box_show_scores(InputBox *self);

View File

@ -1,5 +1,6 @@
#include "MineSweeper.h"
#include "MineCell.h"
#include "InputBox.h"
#include <cstdlib>
#include <string>
@ -16,6 +17,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;
@ -28,6 +33,9 @@ struct _MineSweeper
gboolean started;
int mines_clear, mine_count;
GameStatus game_status;
// InputBox for game win
InputBox *input_box;
};
G_DEFINE_TYPE(MineSweeper, mine_sweeper, GTK_TYPE_APPLICATION_WINDOW)
@ -170,9 +178,9 @@ static void mine_sweeper_check_mines(MineSweeper *self, int pos_x, int pos_y)
self->game_status = GameStatus::Winned;
self->started = FALSE;
// // Save the time of game
// input_dialog->set_game_time(timer_count);
// input_dialog->show();
// Save the time of game
input_box_set_game_time(self->input_box, self->time_count);
input_box_present(self->input_box);
}
}
@ -244,9 +252,13 @@ static gboolean time_func(gpointer data)
{
MineSweeper *mine_app = MINE_SWEEPER(data);
char tmp[50];
(mine_app->time_count)++;
sprintf(tmp, "Time:%d", mine_app->time_count);
gtk_label_set_label(GTK_LABEL(mine_app->time_label), tmp);
// Update time when game is running
if (mine_app->game_status == GameStatus::Running)
{
(mine_app->time_count)++;
sprintf(tmp, "Time:%d", mine_app->time_count);
gtk_label_set_label(GTK_LABEL(mine_app->time_label), tmp);
}
return mine_app->started;
}
@ -284,16 +296,67 @@ 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)
{
MineSweeper *app = MINE_SWEEPER(data);
input_box_show_scores(app->input_box);
}
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");
gtk_window_set_icon_name(GTK_WINDOW(self), "mine_app");
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 Input Box
self->input_box = input_box_new(GTK_WINDOW(self));
// Create widgets
self->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
self->mine_grid = gtk_grid_new();
self->time_label = gtk_label_new("");
self->status_label = gtk_label_new("Game not started");
self->status_label = gtk_label_new("");
self->btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
self->btn_start = gtk_button_new_with_label("Start/Reset");
self->btn_show = gtk_button_new_with_label("Show All");

View File

@ -0,0 +1,47 @@
#include "ScoresItem.h"
struct _ScoresItem
{
GObject parent_instance;
char name[NAME_MAX];
int time;
};
G_DEFINE_TYPE(ScoresItem, scores_item, G_TYPE_OBJECT)
static void scores_item_init(ScoresItem *self)
{
}
static void scores_item_class_init(ScoresItemClass *klass)
{
}
ScoresItem *scores_item_new(const char *win_name, int win_time)
{
ScoresItem *item = Scores_Item(g_object_new(scores_item_get_type(), NULL));
strncpy(item->name, win_name, NAME_MAX);
item->time = win_time;
return item;
}
const char *scores_item_get_name(ScoresItem *item)
{
return item->name;
}
void scores_item_set_name(ScoresItem *item, const char *win_name)
{
strncpy(item->name, win_name, NAME_MAX);
}
int scores_item_get_time(ScoresItem *item)
{
return item->time;
}
void scores_item_set_time(ScoresItem *item, int win_time)
{
item->time = win_time;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(ScoresItem, scores_item, Scores, Item, GObject)
ScoresItem *scores_item_new(const char *win_name, int win_time);
const char *scores_item_get_name(ScoresItem *item);
void scores_item_set_name(ScoresItem *item, const char *win_name);
int scores_item_get_time(ScoresItem *item);
void scores_item_set_time(ScoresItem *item, int win_time);

View File

@ -0,0 +1,181 @@
#include "jsonfile.h"
#include "ScoresWin.h"
#include "ScoresItem.h"
#include <fstream>
struct _ScoresWin
{
GtkWindow parent;
// Child widgets
GtkWidget *scrolled_win;
GtkWidget *main_box;
GtkWidget *btn_close;
// List widgets
GtkWidget *list_view;
GtkSingleSelection *selection;
GtkListItemFactory *factory_name, *factory_time;
GtkColumnViewColumn *column_name, *column_time;
GListStore *store;
};
G_DEFINE_TYPE(ScoresWin, scores_win, GTK_TYPE_WINDOW)
static gboolean scores_win_closed(GtkWidget *window, ScoresWin *self)
{
gtk_widget_set_visible(window, FALSE);
return TRUE;
}
static void btnclose_clicked(GtkButton *btn, GtkWindow *self)
{
gtk_window_close(self);
}
static void name_factory_setup(GtkSignalListItemFactory *factory,
GtkListItem *item)
{
GtkWidget *label = gtk_label_new("");
gtk_list_item_set_child(item, label);
}
static void name_factory_bind(GtkListItemFactory *factory,
GtkListItem *item)
{
// Get child
GtkWidget *label;
label = gtk_list_item_get_child(item);
// Get Item
ScoresItem *item1 = Scores_Item(gtk_list_item_get_item(item));
gtk_label_set_label(GTK_LABEL(label),
scores_item_get_name(item1));
}
static void time_factory_setup(GtkSignalListItemFactory *factory,
GtkListItem *item)
{
GtkWidget *label = gtk_label_new("");
gtk_list_item_set_child(item, label);
}
static void time_factory_bind(GtkListItemFactory *factory,
GtkListItem *item)
{
// Get child
GtkWidget *label;
label = gtk_list_item_get_child(item);
// Get Item
ScoresItem *item1 = Scores_Item(gtk_list_item_get_item(item));
char *time_str = g_strdup_printf("%d", scores_item_get_time(item1));
gtk_label_set_label(GTK_LABEL(label), time_str);
}
static gint sort_func(gpointer a, gpointer b, gpointer user_data)
{
ScoresItem *item_a = Scores_Item(a);
ScoresItem *item_b = Scores_Item(b);
int time_a = scores_item_get_time(item_a);
int time_b = scores_item_get_time(item_b);
// Return result
if (time_a > time_b)
{
return 1;
}
if (time_a == time_b)
{
return 0;
}
if (time_a < time_b)
{
return -1;
}
return 0;
}
static void scores_win_init(ScoresWin *self)
{
// Initalize window
gtk_window_set_title(GTK_WINDOW(self), "Scores");
gtk_window_set_icon_name(GTK_WINDOW(self), "mines_app");
// Create widgets
self->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
self->scrolled_win = gtk_scrolled_window_new();
self->btn_close = gtk_button_new_with_label("Close");
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(self->scrolled_win),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_set_size_request(self->scrolled_win, 300, 400);
gtk_widget_set_halign(self->btn_close, GTK_ALIGN_CENTER);
// Link signals
g_signal_connect(self->btn_close, "clicked", G_CALLBACK(btnclose_clicked), self);
g_signal_connect(self, "close-request", G_CALLBACK(scores_win_closed), self);
// Create store
self->store = g_list_store_new(scores_item_get_type());
self->selection = gtk_single_selection_new(G_LIST_MODEL(self->store));
// Create Column View
self->list_view = gtk_column_view_new(GTK_SELECTION_MODEL(self->selection));
// Create factory for name
self->factory_name = gtk_signal_list_item_factory_new();
g_signal_connect(self->factory_name, "bind", G_CALLBACK(name_factory_bind), NULL);
g_signal_connect(self->factory_name, "setup", G_CALLBACK(name_factory_setup), NULL);
self->column_name = gtk_column_view_column_new("Name", self->factory_name);
gtk_column_view_append_column(GTK_COLUMN_VIEW(self->list_view), self->column_name);
// Create factory for time
self->factory_time = gtk_signal_list_item_factory_new();
g_signal_connect(self->factory_time, "bind", G_CALLBACK(time_factory_bind), NULL);
g_signal_connect(self->factory_time, "setup", G_CALLBACK(time_factory_setup), NULL);
self->column_time = gtk_column_view_column_new("Time", self->factory_time);
gtk_column_view_append_column(GTK_COLUMN_VIEW(self->list_view), self->column_time);
// Pack widgets
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_win), self->list_view);
gtk_box_append(GTK_BOX(self->main_box), self->scrolled_win);
gtk_box_append(GTK_BOX(self->main_box), self->btn_close);
gtk_window_set_child(GTK_WINDOW(self), self->main_box);
}
static void scores_win_class_init(ScoresWinClass *klass)
{
}
ScoresWin *scores_win_new(GtkWindow *parent)
{
return Scores_Win(g_object_new(scores_win_get_type(),
"transient-for", parent, NULL));
}
void scores_win_update_and_show(ScoresWin *self)
{
std::fstream infile;
infile.open("scores.json", std::ios_base::in);
if (infile.is_open())
{
// Read data from json file
json data = json::parse(infile);
std::vector<std::string> name_vec = data["name"];
std::vector<int> time_vec = data["time"];
// Clear the store
g_list_store_remove_all(self->store);
// Append data to the store
for (int i = 0; i < name_vec.size(); i++)
{
g_list_store_append(self->store,
scores_item_new(name_vec[i].c_str(), time_vec[i]));
}
g_list_store_sort(self->store, (GCompareDataFunc)sort_func, NULL);
}
gtk_window_present(GTK_WINDOW(self));
}

View File

@ -0,0 +1,9 @@
#pragma once
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(ScoresWin, scores_win, Scores, Win, GtkWindow)
ScoresWin *scores_win_new(GtkWindow *parent);
void scores_win_update_and_show(ScoresWin *self);

View File

@ -0,0 +1,7 @@
#pragma once
#include "../json_nlohmann/json.hpp"
#include <fstream>
#include <vector>
using json = nlohmann::json;