Add Scores Window

This commit is contained in:
daleclack 2024-02-29 22:32:57 +08:00
parent 311d1e1d21
commit 3a3b3fe59b
10 changed files with 273 additions and 117 deletions

View File

@ -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

View File

@ -2,6 +2,7 @@
#include <string>
#include <fstream>
#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);
}

View File

@ -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);

View File

@ -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)

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

@ -1,80 +0,0 @@
#include "ScoresWin.hh"
ScoresWin::ScoresWin(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &ref_Glade)
: Gtk::Window(cobject),
ref_builder(ref_Glade)
{
// Get Widgets
btnclose = ref_builder->get_widget<Gtk::Button>("btnclose");
tree_view = ref_builder->get_widget<Gtk::TreeView>("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<std::string> name_vec = data["name"];
std::vector<int> 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<ScoresWin>(builder, "scores_win");
return main_win;
}

View File

@ -0,0 +1,178 @@
#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), "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<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

@ -1,35 +0,0 @@
#pragma once
#include <gtkmm.h>
#include "jsonfile.hh"
class ScoresWin : public Gtk::Window{
public:
static ScoresWin *create();
ScoresWin(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &ref_Glade);
void update_and_show();
private:
Glib::RefPtr<Gtk::Builder> 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<std::string> player_name;
Gtk::TreeModelColumn<int> win_time;
};
ModelColumns column1;
Glib::RefPtr<Gtk::ListStore> store;
// Sort function
int sort_func(const Gtk::TreeModel::const_iterator &iter1, const Gtk::TreeModel::const_iterator &iter2);
};