From 3a3b3fe59bcfdebf5dd684f6e69411047fbec47c Mon Sep 17 00:00:00 2001 From: daleclack Date: Thu, 29 Feb 2024 22:32:57 +0800 Subject: [PATCH] Add Scores Window --- Gtk4/gtk158_minesweeper4/CMakeLists.txt | 3 +- Gtk4/gtk158_minesweeper4/src/InputBox.cpp | 19 +- Gtk4/gtk158_minesweeper4/src/InputBox.h | 2 + Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp | 2 + Gtk4/gtk158_minesweeper4/src/ScoresItem.cpp | 47 +++++ Gtk4/gtk158_minesweeper4/src/ScoresItem.h | 15 ++ Gtk4/gtk158_minesweeper4/src/ScoresWin.cc | 80 --------- Gtk4/gtk158_minesweeper4/src/ScoresWin.cpp | 178 +++++++++++++++++++ Gtk4/gtk158_minesweeper4/src/ScoresWin.h | 9 + Gtk4/gtk158_minesweeper4/src/ScoresWin.hh | 35 ---- 10 files changed, 273 insertions(+), 117 deletions(-) create mode 100644 Gtk4/gtk158_minesweeper4/src/ScoresItem.cpp create mode 100644 Gtk4/gtk158_minesweeper4/src/ScoresItem.h delete mode 100644 Gtk4/gtk158_minesweeper4/src/ScoresWin.cc create mode 100644 Gtk4/gtk158_minesweeper4/src/ScoresWin.cpp create mode 100644 Gtk4/gtk158_minesweeper4/src/ScoresWin.h delete mode 100644 Gtk4/gtk158_minesweeper4/src/ScoresWin.hh diff --git a/Gtk4/gtk158_minesweeper4/CMakeLists.txt b/Gtk4/gtk158_minesweeper4/CMakeLists.txt index dbfbbbd..06e098d 100644 --- a/Gtk4/gtk158_minesweeper4/CMakeLists.txt +++ b/Gtk4/gtk158_minesweeper4/CMakeLists.txt @@ -26,7 +26,8 @@ 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 src/InputBox.cpp) +set(SOURCE_FILE src/main.cpp src/MineSweeper.cpp src/MineCell.cpp + src/InputBox.cpp src/ScoresItem.cpp src/ScoresWin.cpp) #Compile Resource diff --git a/Gtk4/gtk158_minesweeper4/src/InputBox.cpp b/Gtk4/gtk158_minesweeper4/src/InputBox.cpp index c581537..f3e1d3d 100644 --- a/Gtk4/gtk158_minesweeper4/src/InputBox.cpp +++ b/Gtk4/gtk158_minesweeper4/src/InputBox.cpp @@ -2,6 +2,7 @@ #include #include #include "InputBox.h" +#include "ScoresWin.h" #include "jsonfile.h" struct _InputBox @@ -12,6 +13,9 @@ struct _InputBox 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) @@ -42,7 +46,10 @@ static void btnok_clicked(GtkButton *btn, InputBox *self) 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)); } @@ -57,6 +64,7 @@ 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), "org.gtk.daleclack"); // Create widgets self->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); @@ -66,6 +74,10 @@ static void input_box_init(InputBox *self) 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); @@ -133,3 +145,8 @@ void input_box_set_game_time(InputBox *self, int time) // 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); +} diff --git a/Gtk4/gtk158_minesweeper4/src/InputBox.h b/Gtk4/gtk158_minesweeper4/src/InputBox.h index 9d3177d..99367ca 100644 --- a/Gtk4/gtk158_minesweeper4/src/InputBox.h +++ b/Gtk4/gtk158_minesweeper4/src/InputBox.h @@ -9,3 +9,5 @@ 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); diff --git a/Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp b/Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp index 379a790..6a0c2a1 100644 --- a/Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp +++ b/Gtk4/gtk158_minesweeper4/src/MineSweeper.cpp @@ -304,6 +304,8 @@ static void newgame_activated(GSimpleAction *action, GVariant *parmeter, gpointe 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) diff --git a/Gtk4/gtk158_minesweeper4/src/ScoresItem.cpp b/Gtk4/gtk158_minesweeper4/src/ScoresItem.cpp new file mode 100644 index 0000000..d82402f --- /dev/null +++ b/Gtk4/gtk158_minesweeper4/src/ScoresItem.cpp @@ -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; +} diff --git a/Gtk4/gtk158_minesweeper4/src/ScoresItem.h b/Gtk4/gtk158_minesweeper4/src/ScoresItem.h new file mode 100644 index 0000000..1a01aff --- /dev/null +++ b/Gtk4/gtk158_minesweeper4/src/ScoresItem.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +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); diff --git a/Gtk4/gtk158_minesweeper4/src/ScoresWin.cc b/Gtk4/gtk158_minesweeper4/src/ScoresWin.cc deleted file mode 100644 index 242d520..0000000 --- a/Gtk4/gtk158_minesweeper4/src/ScoresWin.cc +++ /dev/null @@ -1,80 +0,0 @@ -#include "ScoresWin.hh" - -ScoresWin::ScoresWin(BaseObjectType *cobject, const Glib::RefPtr &ref_Glade) - : Gtk::Window(cobject), - ref_builder(ref_Glade) -{ - // Get Widgets - btnclose = ref_builder->get_widget("btnclose"); - tree_view = ref_builder->get_widget("scores_view"); - btnclose->signal_clicked().connect(sigc::mem_fun(*this, &ScoresWin::hide)); - - // Create the list store - store = Gtk::ListStore::create(column1); - store->set_sort_column(column1.win_time, Gtk::SortType::ASCENDING); - store->set_sort_func(column1.win_time, sigc::mem_fun(*this, &ScoresWin::sort_func)); - // store->set_default_sort_func(sigc::mem_fun(*this, &ScoresWin::sort_func)); - tree_view->set_model(store); - tree_view->append_column("name", column1.player_name); - tree_view->append_column("time", column1.win_time); -} - -void ScoresWin::update_and_show() -{ - 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 name_vec = data["name"]; - std::vector time_vec = data["time"]; - - // Clear the store - store->clear(); - - // Append data to the store - for (int i = 0; i < name_vec.size(); i++) - { - auto row = *(store->append()); - row[column1.player_name] = name_vec[i]; - row[column1.win_time] = time_vec[i]; - } - } - - show(); -} - -int ScoresWin::sort_func(const Gtk::TreeModel::const_iterator &iter1, const Gtk::TreeModel::const_iterator &iter2) -{ - // Sort by the game time - auto row1 = *iter1; - auto row2 = *iter2; - if (row1[column1.win_time] < row2[column1.win_time]) - { - // g_print("test1\n"); - return -1; - } - if (row1[column1.win_time] == row2[column1.win_time]) - { - // g_print("test2\n"); - return 0; - } - else - { - return 1; - } -} - -ScoresWin *ScoresWin::create() -{ - // Create a window - auto builder = Gtk::Builder::create_from_resource("/org/gtk/daleclack/scoreswin.ui"); - ScoresWin *main_win; - - // builder->get_widget_derived("scores_win", main_win); - main_win = Gtk::Builder::get_widget_derived(builder, "scores_win"); - - return main_win; -} diff --git a/Gtk4/gtk158_minesweeper4/src/ScoresWin.cpp b/Gtk4/gtk158_minesweeper4/src/ScoresWin.cpp new file mode 100644 index 0000000..acd2c6a --- /dev/null +++ b/Gtk4/gtk158_minesweeper4/src/ScoresWin.cpp @@ -0,0 +1,178 @@ +#include "jsonfile.h" +#include "ScoresWin.h" +#include "ScoresItem.h" +#include + +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), "org.gtk.daleclack"); + + // 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"); + g_signal_connect(self->btn_close, "clicked", G_CALLBACK(btnclose_clicked), self); + 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); + + // 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 name_vec = data["name"]; + std::vector 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)); +} diff --git a/Gtk4/gtk158_minesweeper4/src/ScoresWin.h b/Gtk4/gtk158_minesweeper4/src/ScoresWin.h new file mode 100644 index 0000000..44d43af --- /dev/null +++ b/Gtk4/gtk158_minesweeper4/src/ScoresWin.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(ScoresWin, scores_win, Scores, Win, GtkWindow) + +ScoresWin *scores_win_new(GtkWindow *parent); + +void scores_win_update_and_show(ScoresWin *self); diff --git a/Gtk4/gtk158_minesweeper4/src/ScoresWin.hh b/Gtk4/gtk158_minesweeper4/src/ScoresWin.hh deleted file mode 100644 index 21e1d0f..0000000 --- a/Gtk4/gtk158_minesweeper4/src/ScoresWin.hh +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include "jsonfile.hh" - -class ScoresWin : public Gtk::Window{ - public: - static ScoresWin *create(); - ScoresWin(BaseObjectType *cobject, const Glib::RefPtr &ref_Glade); - void update_and_show(); - - private: - Glib::RefPtr ref_builder; - - // Child widgets - Gtk::Button *btnclose; - Gtk::TreeView *tree_view; - - // TreeView data - class ModelColumns : public Gtk::TreeModelColumnRecord{ - public: - ModelColumns(){ - add(player_name); - add(win_time); - } - Gtk::TreeModelColumn player_name; - Gtk::TreeModelColumn win_time; - }; - ModelColumns column1; - - Glib::RefPtr store; - - // Sort function - int sort_func(const Gtk::TreeModel::const_iterator &iter1, const Gtk::TreeModel::const_iterator &iter2); -};