Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • SuborbitalPigeon/rugby
  • webpigeon/rugby
2 results
Show changes
Showing with 872 additions and 1006 deletions
# SPDX-FileCopyrightText: 2016-2024 Bruce Cowan <bruce@bcowan.me.uk>
#
# SPDX-License-Identifier: GPL-3.0-or-later
project('rugby', 'c',
version: '0.4.alpha',
license:'GPLv3+',
meson_version: '>= 1.1.0',
default_options: [
'buildtype=debugoptimized',
'c_std=gnu11',
'warning_level=3'
],
)
datadir = get_option('datadir')
desktopdir = datadir / 'applications'
gnome = import('gnome')
gio_dep = dependency('gio-2.0', version: '>= 2.76')
gtk_dep = dependency('gtk4', version: '>= 4.12')
libadwaita_dep = dependency('libadwaita-1', version: '>=1.5.beta')
conf = configuration_data()
profile = get_option('profile')
conf.set_quoted('PROFILE', profile)
if profile == 'development'
conf.set_quoted('VERSION', '@0@-@VCS_TAG@'.format(meson.project_version()))
else
conf.set_quoted('VERSION', meson.project_version())
endif
cflags = ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_70',
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_70',
'-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_4_12',
'-DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_4_12',
'-Wno-overlength-strings',
]
cc = meson.get_compiler('c')
add_project_arguments(cc.get_supported_arguments(cflags),
language: 'c')
subdir('data')
subdir('src')
gnome.post_install(
glib_compile_schemas: true,
update_desktop_database: true,
)
# SPDX-FileCopyrightText: 2022 Bruce Cowan <bruce@bcowan.me.uk>
#
# SPDX-License-Identifier: CC0-1.0
option(
'profile',
type: 'combo',
choices: ['default', 'development'],
value: 'default',
description: 'What sort of build is this?'
)
#include "rugby-application.h"
#include "rugby-cell-renderer-score.h"
#include "rugby-score-store.h"
#include "rugby-scoring.h"
struct _RugbyApplicationPrivate
{
GtkWidget *enablefilter;
GtkWidget *filterscale;
RugbyScoreStore *store;
GtkTreeModel *fmodel;
};
G_DEFINE_TYPE (RugbyApplication, rugby_application, GTK_TYPE_APPLICATION)
static void
scorespin_changed_cb (GtkSpinButton *spin,
RugbyApplication *app)
{
gint score;
gint max;
score = gtk_spin_button_get_value_as_int (spin);
rugby_score_store_set_score (app->priv->store, score);
/* I'd rather not have to do this */
max = MAX (rugby_scoring_get_max_tries (score), 1.0);
gtk_range_set_range (GTK_RANGE (app->priv->filterscale), 0.0, max);
}
static void
enablefilter_toggled_cb (GtkToggleButton *toggle,
RugbyApplication *app)
{
if (gtk_toggle_button_get_active (toggle))
gtk_widget_set_sensitive (app->priv->filterscale, TRUE);
else
gtk_widget_set_sensitive (app->priv->filterscale, FALSE);
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (app->priv->fmodel));
}
static void
filterscale_changed_cb (GtkRange *range,
RugbyApplication *app)
{
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (app->priv->fmodel));
}
static void
store_finished_cb (RugbyScoreStore *store,
gint possibilities,
GtkStatusbar *bar)
{
gchar *status;
guint context_id;
status = g_strdup_printf ("%d possibilities", possibilities);
context_id = gtk_statusbar_get_context_id (bar, "possibilities count");
gtk_statusbar_pop (bar, context_id);
gtk_statusbar_push (bar, context_id, status);
g_free (status);
}
static gboolean
filter_func (GtkTreeModel *model,
GtkTreeIter *iter,
RugbyApplication *app)
{
gint tries, utries;
gint current;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (app->priv->enablefilter)))
{
current = (gint) gtk_range_get_value (GTK_RANGE (app->priv->filterscale));
gtk_tree_model_get (model, iter,
RUGBY_SCORE_STORE_TRIES, &tries,
RUGBY_SCORE_STORE_UTRIES, &utries, -1);
if (tries + utries == current)
return TRUE;
else
return FALSE;
}
else
return TRUE;
}
static void
app_about (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkApplication *application = GTK_APPLICATION (user_data);
GList *windows;
GtkWindow *window;
windows = gtk_application_get_windows (application);
window = windows->data;
/* TODO show proper about dialogue */
gtk_show_about_dialog (window,
"program-name", "Rugby",
"license-type", GTK_LICENSE_MIT_X11,
NULL);
}
static void
app_quit (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GApplication *application = G_APPLICATION (user_data);
g_application_quit (application);
}
static GActionEntry app_entries[] =
{
{ "about", app_about, NULL, NULL, NULL },
{ "quit", app_quit, NULL, NULL, NULL }
};
static void
rugby_application_startup (GApplication *application)
{
GtkBuilder *builder;
GError *err = NULL;
G_APPLICATION_CLASS (rugby_application_parent_class)->startup (application);
g_action_map_add_action_entries (G_ACTION_MAP (application), app_entries, G_N_ELEMENTS (app_entries), application);
builder = gtk_builder_new ();
if (!gtk_builder_add_from_resource (builder, "/uk/me/bcowan/rugby/interface.ui", &err))
{
g_error ("Error: %s", err->message);
}
gtk_application_set_app_menu (GTK_APPLICATION (application), G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
g_object_unref (builder);
}
static void
rugby_application_activate (GApplication *app)
{
RugbyApplicationPrivate *priv = RUGBY_APPLICATION (app)->priv;
GtkBuilder *builder;
GError *err = NULL;
GtkWidget *score;
GtkWidget *tree;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkWidget *statusbar;
GtkWidget *window;
GtkCssProvider *provider;
builder = gtk_builder_new ();
if (!gtk_builder_add_from_resource (builder, "/uk/me/bcowan/rugby/interface.ui", &err))
{
g_error ("Error: %s", err->message);
}
score = GTK_WIDGET (gtk_builder_get_object (builder, "scorespin"));
g_signal_connect (score, "value-changed",
G_CALLBACK (scorespin_changed_cb), app);
priv->enablefilter = GTK_WIDGET (gtk_builder_get_object (builder, "enablefilter"));
g_signal_connect (priv->enablefilter, "toggled",
G_CALLBACK (enablefilter_toggled_cb), app);
priv->filterscale = GTK_WIDGET (gtk_builder_get_object (builder, "filterscale"));
g_signal_connect (priv->filterscale, "value-changed",
G_CALLBACK (filterscale_changed_cb), app);
tree = GTK_WIDGET (gtk_builder_get_object (builder, "treeview"));
/* Would be nice to use GtkBuilder */
renderer = rugby_cell_renderer_score_new ();
column = gtk_tree_view_column_new_with_attributes ("Score", renderer,
"tries", RUGBY_SCORE_STORE_TRIES,
"utries", RUGBY_SCORE_STORE_UTRIES,
"pens", RUGBY_SCORE_STORE_PENS,
NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
/* Create a TreeModelFilter */
priv->store = rugby_score_store_new ();
priv->fmodel = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->store), NULL);
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->fmodel),
(GtkTreeModelFilterVisibleFunc) filter_func, app, NULL);
gtk_tree_view_set_model (GTK_TREE_VIEW (tree), priv->fmodel);
statusbar = GTK_WIDGET (gtk_builder_get_object (builder, "status"));
g_signal_connect (priv->store, "finished",
G_CALLBACK (store_finished_cb), statusbar);
/* init CSS */
provider = gtk_css_provider_new ();
/* TODO load this from resource */
gtk_css_provider_load_from_path (provider, "rugby.css", NULL);
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_window_set_application (GTK_WINDOW (window), GTK_APPLICATION (app));
gtk_widget_show_all (window);
g_object_unref (builder);
}
static void
rugby_application_class_init (RugbyApplicationClass *klass)
{
GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
app_class->startup = rugby_application_startup;
app_class->activate = rugby_application_activate;
g_type_class_add_private (klass, sizeof (RugbyApplicationPrivate));
}
static void
rugby_application_init (RugbyApplication *app)
{
app->priv = G_TYPE_INSTANCE_GET_PRIVATE (app, RUGBY_TYPE_APPLICATION, RugbyApplicationPrivate);
}
/**
* rugby_application_new:
*
* Creates a #RugbyApplication. This is the main UI of the program
*
* Returns: a new #RugbyApplication
*/
RugbyApplication *
rugby_application_new (void)
{
g_type_init ();
g_set_application_name ("Rugby");
return g_object_new (RUGBY_TYPE_APPLICATION,
"application-id", "uk.me.bcowan.Rugby",
NULL);
}
#ifndef __RUGBY_APPLICATION_H__
#define __RUGBY_APPLICATION_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define RUGBY_TYPE_APPLICATION (rugby_application_get_type ())
#define RUGBY_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RUGBY_TYPE_APPLICATION, RugbyApplication))
#define RUGBY_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RUGBY_TYPE_APPLICATION, RugbyApplicationClass))
#define RUGBY_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RUGBY_TYPE_APPLICATION))
#define RUGBY_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RUGBY_TYPE_APPLICATION))
#define RUGBY_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RUGBY_TYPE_APPLICATION, RugbyApplicationClass))
typedef struct _RugbyApplication RugbyApplication;
typedef struct _RugbyApplicationClass RugbyApplicationClass;
typedef struct _RugbyApplicationPrivate RugbyApplicationPrivate;
struct _RugbyApplication
{
GtkApplication parent;
/*< private >*/
RugbyApplicationPrivate *priv;
};
struct _RugbyApplicationClass
{
GtkApplicationClass parent_class;
};
GType rugby_application_get_type (void) G_GNUC_CONST;
RugbyApplication * rugby_application_new (void);
G_END_DECLS
#endif /* __RUGBY_APPLICATION_H__ */
#include "rugby-cell-renderer-score.h"
#include "rugby-scoring.h"
enum
{
PROP_0,
PROP_TRIES,
PROP_UTRIES,
PROP_PENS
};
struct _RugbyCellRendererScorePrivate
{
gint tries;
gint utries;
gint pens;
};
/* Style classes for the different types of score */
#define RUGBY_STYLE_CLASS_TRY "try"
#define RUGBY_STYLE_CLASS_UTRY "utry"
#define RUGBY_STYLE_CLASS_PEN "pen"
G_DEFINE_TYPE (RugbyCellRendererScore, rugby_cell_renderer_score, GTK_TYPE_CELL_RENDERER)
static void
rugby_cell_renderer_score_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
RugbyCellRendererScorePrivate *priv = RUGBY_CELL_RENDERER_SCORE (object)->priv;
switch (prop_id)
{
case PROP_TRIES:
priv->tries = g_value_get_int (value);
break;
case PROP_UTRIES:
priv->utries = g_value_get_int (value);
break;
case PROP_PENS:
priv->pens = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
rugby_cell_renderer_score_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
RugbyCellRendererScorePrivate *priv = RUGBY_CELL_RENDERER_SCORE (object)->priv;
switch (prop_id)
{
case PROP_TRIES:
g_value_set_int (value, priv->tries);
break;
case PROP_UTRIES:
g_value_set_int (value, priv->utries);
break;
case PROP_PENS:
g_value_set_int (value, priv->pens);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
#define FIXED_WIDTH 400
#define FIXED_HEIGHT 20
static void
rugby_cell_renderer_score_get_preferred_width (GtkCellRenderer *cell,
GtkWidget *widget,
gint *minimum_width,
gint *natural_width)
{
gint xpad;
gint width;
gtk_cell_renderer_get_padding (cell, &xpad, NULL);
width = xpad * 2 + FIXED_WIDTH;
if (minimum_width)
*minimum_width = width;
if (natural_width)
*natural_width = width;
}
static void
rugby_cell_renderer_score_get_preferred_height (GtkCellRenderer *cell,
GtkWidget *widget,
gint *minimum_height,
gint *natural_height)
{
gint ypad;
gint height;
gtk_cell_renderer_get_padding (cell, NULL, &ypad);
height = ypad * 2 + FIXED_HEIGHT;
if (minimum_height)
*minimum_height = height;
if (natural_height)
*natural_height = height;
}
static void
render_bar (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height,
RugbyScoreType type)
{
gtk_style_context_save (context);
switch (type)
{
case RUGBY_SCORE_TYPE_TRY:
gtk_style_context_add_class (context, RUGBY_STYLE_CLASS_TRY);
break;
case RUGBY_SCORE_TYPE_UTRY:
gtk_style_context_add_class (context, RUGBY_STYLE_CLASS_UTRY);
break;
case RUGBY_SCORE_TYPE_PEN:
gtk_style_context_add_class (context, RUGBY_STYLE_CLASS_PEN);
break;
}
gtk_render_activity (context, cr, x, y, width, height);
gtk_style_context_restore (context);
}
static void
rugby_cell_renderer_score_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
RugbyCellRendererScorePrivate *priv = RUGBY_CELL_RENDERER_SCORE (cell)->priv;
GtkStyleContext *context;
gint xpad, ypad;
gdouble x, y, total_w, w, h;
GtkBorder padding, border;
gint tries, utries, pens;
gint total;
gint i;
context = gtk_widget_get_style_context (widget);
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
x = cell_area->x + xpad;
y = cell_area->y + ypad;
total_w = cell_area->width - xpad * 2;
h = cell_area->height - ypad * 2;
gtk_style_context_save (context);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
gtk_render_background (context, cr, x, y, total_w, h);
gtk_render_frame (context, cr, x, y, total_w, h);
gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border);
gtk_style_context_get_padding (context, GTK_STATE_FLAG_NORMAL, &padding);
x += border.left + padding.left;
y += border.top + padding.top;
total_w -= border.left + border.right + padding.left + padding.right;
h -= border.top + border.bottom + padding.top + padding.bottom;
gtk_style_context_restore (context);
tries = priv->tries;
utries = priv->utries;
pens = priv->pens;
total = tries * TRY_POINTS + utries * UTRY_POINTS + pens * PEN_POINTS;
gtk_style_context_save (context);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
w = total_w / (total / 7.0);
for (i = 0; i < tries; i++)
{
render_bar (context, cr, x, y, w, h, RUGBY_SCORE_TYPE_TRY);
x += w;
}
w = total_w / (total / 5.0);
for (i = 0; i < utries; i++)
{
render_bar (context, cr, x, y, w, h, RUGBY_SCORE_TYPE_UTRY);
x += w;
}
w = total_w / (total / 3.0);
for (i = 0; i < pens; i++)
{
render_bar (context, cr, x, y, w, h, RUGBY_SCORE_TYPE_PEN);
x += w;
}
gtk_style_context_restore (context);
}
static void
rugby_cell_renderer_score_class_init (RugbyCellRendererScoreClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
obj_class->get_property = rugby_cell_renderer_score_get_property;
obj_class->set_property = rugby_cell_renderer_score_set_property;
cell_class->get_preferred_width = rugby_cell_renderer_score_get_preferred_width;
cell_class->get_preferred_height = rugby_cell_renderer_score_get_preferred_height;
cell_class->render = rugby_cell_renderer_score_render;
g_object_class_install_property (obj_class, PROP_TRIES,
g_param_spec_int ("tries", "Tries", "Number of tries",
0, 50, 0,
G_PARAM_READWRITE));
g_object_class_install_property (obj_class, PROP_UTRIES,
g_param_spec_int ("utries", "Unconverted Tries", "Number of Unconverted Tries",
0, 50, 0,
G_PARAM_READWRITE));
g_object_class_install_property (obj_class, PROP_PENS,
g_param_spec_int ("pens", "Penalties", "Number of penalties",
0, 50, 0,
G_PARAM_READWRITE));
g_type_class_add_private (klass, sizeof (RugbyCellRendererScorePrivate));
}
static void
rugby_cell_renderer_score_init (RugbyCellRendererScore *cellscore)
{
RugbyCellRendererScorePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE ((cellscore), RUGBY_TYPE_CELL_RENDERER_SCORE, RugbyCellRendererScorePrivate);
priv->tries = 0;
priv->utries = 0;
priv->pens = 0;
gtk_cell_renderer_set_padding (GTK_CELL_RENDERER (cellscore), 2, 2);
cellscore->priv = priv;
}
GtkCellRenderer *
rugby_cell_renderer_score_new (void)
{
return g_object_new (RUGBY_TYPE_CELL_RENDERER_SCORE, NULL);
}
#ifndef __RUGBY_CELL_RENDERER_SCORE__
#define __RUGBY_CELL_RENDERER_SCORE__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define RUGBY_TYPE_CELL_RENDERER_SCORE (rugby_cell_renderer_score_get_type ())
#define RUGBY_CELL_RENDERER_SCORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RUGBY_TYPE_CELL_RENDERER_SCORE, RugbyCellRendererScore))
#define RUGBY_CELL_RENDERER_SCORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RUGBY_TYPE_CELL_RENDERER_SCORE, RugbyCellRendererScoreClass))
#define RUGBY_IS_CELL_RENDERER_SCORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RUGBY_TYPE_CELL_RENDERER_SCORE))
#define RUGBY_IS_CELL_RENDERER_SCORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RUGBY_TYPE_CELL_RENDERER_SCORE))
#define RUGBY_CELL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RUGBY_TYPE_CELL_RENDERER_SCORE, RugbyCellRendererScoreClass))
typedef struct _RugbyCellRendererScore RugbyCellRendererScore;
typedef struct _RugbyCellRendererScoreClass RugbyCellRendererScoreClass;
typedef struct _RugbyCellRendererScorePrivate RugbyCellRendererScorePrivate;
struct _RugbyCellRendererScore
{
GtkCellRenderer parent;
/*< private >*/
RugbyCellRendererScorePrivate *priv;
};
struct _RugbyCellRendererScoreClass
{
GtkCellRendererClass parent_class;
};
GType rugby_cell_renderer_score_get_type (void) G_GNUC_CONST;
GtkCellRenderer * rugby_cell_renderer_score_new (void);
G_END_DECLS
#endif /* __RUGBY_CELL_RENDERER_SCORE__ */
#include "rugby-score-store.h"
#include "rugby-scoring.h"
enum
{
FINISHED,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_SCORE,
PROP_LAST
};
static GParamSpec *pspecs[PROP_LAST];
struct _RugbyScoreStorePrivate
{
gint score;
};
G_DEFINE_TYPE (RugbyScoreStore, rugby_score_store, GTK_TYPE_LIST_STORE)
static guint signals[LAST_SIGNAL] = { 0, };
static void
rugby_score_store_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
RugbyScoreStore *store = RUGBY_SCORE_STORE (object);
switch (prop_id)
{
case PROP_SCORE:
rugby_score_store_set_score (store, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
rugby_score_store_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
RugbyScoreStore *store = RUGBY_SCORE_STORE (object);
switch (prop_id)
{
case PROP_SCORE:
g_value_set_int (value, rugby_score_store_get_score (store));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
rugby_score_store_class_init (RugbyScoreStoreClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS (klass);
obj_class->get_property = rugby_score_store_get_property;
obj_class->set_property = rugby_score_store_set_property;
pspecs[PROP_SCORE] = g_param_spec_int ("score", "Score", "The score",
0, 150, 0,
G_PARAM_READWRITE);
g_object_class_install_property (obj_class, PROP_SCORE, pspecs[PROP_SCORE]);
/**
* RugbyScoreStore::finished:
* @store: the object which received the signal
*
* Emitted when possibilities checking has finished.
*/
signals[FINISHED] =
g_signal_new ("finished",
G_TYPE_FROM_CLASS (obj_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (RugbyScoreStoreClass, finished),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_INT);
g_type_class_add_private (klass, sizeof (RugbyScoreStorePrivate));
}
static void
populate_store (GObject *object,
GParamSpec *pspec)
{
GtkListStore *store = GTK_LIST_STORE (object);
RugbyScoreStorePrivate *priv = RUGBY_SCORE_STORE (object)->priv;
GVariant *possibilities;
GVariant *possibility;
GVariantIter iter;
gsize total;
/* Clear the store */
gtk_list_store_clear (store);
possibilities = rugby_scoring_get_possibilities (priv->score);
if (possibilities == NULL)
return;
total = g_variant_iter_init (&iter, possibilities);
while ((possibility = g_variant_iter_next_value (&iter)))
{
gint tries, utries, pens;
GString *tooltip;
GtkTreeIter iter;
g_variant_get (possibility, "(iii)", &tries, &utries, &pens);
g_variant_unref (possibility);
tooltip = g_string_new_len (NULL, 20);
if (utries > 0 || tries > 0)
{
g_string_append_printf (tooltip, "%d tries, %d converted",
tries + utries, tries);
if (pens > 0)
g_string_append_printf (tooltip, ", %d kicks",
pens);
}
else if (utries == 0 && tries == 0)
g_string_append_printf (tooltip, "%d kicks",
pens);
/* Put result in list store */
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
RUGBY_SCORE_STORE_TRIES, tries,
RUGBY_SCORE_STORE_UTRIES, utries,
RUGBY_SCORE_STORE_PENS, pens,
RUGBY_SCORE_STORE_TOOLTIP, tooltip->str,
-1);
g_string_free (tooltip, TRUE);
}
g_signal_emit (store, signals[FINISHED], 0, (gint) total);
}
static void
rugby_score_store_init (RugbyScoreStore *store)
{
RugbyScoreStorePrivate *priv;
GType types[] = { G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING };
priv = G_TYPE_INSTANCE_GET_PRIVATE (store, RUGBY_TYPE_SCORE_STORE, RugbyScoreStorePrivate);
gtk_list_store_set_column_types (GTK_LIST_STORE (store), RUGBY_SCORE_STORE_COLUMNS, types);
priv->score = 0;
g_signal_connect (store, "notify::score",
G_CALLBACK (populate_store), NULL);
store->priv = priv;
}
/**
* rugby_score_store_new:
*
* Creates a #RugbyScoreStore. This can be used to update a #GtkTreeView
* with possibilities.
*
* Returns: a new #RugbyScoreStore
*/
RugbyScoreStore *
rugby_score_store_new (void)
{
return g_object_new (RUGBY_TYPE_SCORE_STORE, NULL);
}
/**
* rugby_score_store_get_score:
* @scores: a #RugbyScoreStore
*
* Gets the current score.
*
* Returns: the current score
*/
gint
rugby_score_store_get_score (RugbyScoreStore *store)
{
g_return_val_if_fail (RUGBY_IS_SCORE_STORE (store), 0);
return store->priv->score;
}
/**
* rugby_score_store_set_score:
* @scores: a #RugbyScoreStore
* @score: the new score
*
* Sets the current score.
*/
void
rugby_score_store_set_score (RugbyScoreStore *store,
gint score)
{
g_return_if_fail (RUGBY_IS_SCORE_STORE (store));
g_return_if_fail (score >= 0 && score <= 150);
store->priv->score = score;
g_object_notify_by_pspec (G_OBJECT (store), pspecs[PROP_SCORE]);
}
#ifndef __RUGBY_SCORE_STORE_H__
#define __RUGBY_SCORE_STORE_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
enum
{
RUGBY_SCORE_STORE_TRIES,
RUGBY_SCORE_STORE_UTRIES,
RUGBY_SCORE_STORE_PENS,
RUGBY_SCORE_STORE_TOOLTIP,
RUGBY_SCORE_STORE_COLUMNS
};
#define RUGBY_TYPE_SCORE_STORE (rugby_score_store_get_type ())
#define RUGBY_SCORE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RUGBY_TYPE_SCORE_STORE, RugbyScoreStore))
#define RUGBY_SCORE_SCORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RUGBY_TYPE_SCORE_STORE, RugbyScoreStoreClass))
#define RUGBY_IS_SCORE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RUGBY_TYPE_SCORE_STORE))
#define RUGBY_IS_SCORE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RUGBY_TYPE_SCORE_STORE))
#define RUGBY_SCORE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RUGBY_TYPE_SCORE_STORE, RugbyScoreStoreClass))
typedef struct _RugbyScoreStore RugbyScoreStore;
typedef struct _RugbyScoreStoreClass RugbyScoreStoreClass;
typedef struct _RugbyScoreStorePrivate RugbyScoreStorePrivate;
struct _RugbyScoreStore
{
GtkListStore parent;
/*< private >*/
RugbyScoreStorePrivate *priv;
};
struct _RugbyScoreStoreClass
{
GtkListStoreClass parent_class;
void (* finished) (RugbyScoreStore *store,
int possibilities);
};
GType rugby_score_store_get_type (void) G_GNUC_CONST;
RugbyScoreStore * rugby_score_store_new (void);
gint rugby_score_store_get_score (RugbyScoreStore *store);
void rugby_score_store_set_score (RugbyScoreStore *store,
gint score);
G_END_DECLS
#endif /* __RUGBY_SCORE_STORE_H__ */
#include "rugby-scoring.h"
/**
* rugby_scoring_get_possibilities:
* @score: the score of a team
*
* Gets the possibile ways of scoring @score points, as a %GVariant of the
* type "a(iii)", in the order "tries, utries, pens".
*
* Returns: the possibilities, or %NULL in the case there are none
*/
GVariant *
rugby_scoring_get_possibilities (gint score)
{
GVariantBuilder builder;
gint possibilities = 0;
gint max_tries, max_utries;
gint tries, utries;
g_return_val_if_fail (score > 0, NULL);
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
max_tries = (gint) (score / TRY_POINTS);
max_utries = (gint) (score / UTRY_POINTS);
for (tries = 0; tries <= max_tries; tries++)
{
for (utries = 0; utries <= max_utries; utries++)
{
gint left;
/* Calculate left over score for penalties */
left = score - (tries * TRY_POINTS) - (utries * UTRY_POINTS);
if (left < 0)
continue;
/* If the score after pens is 0, it's a possibility */
if (left % PEN_POINTS == 0)
{
gint pens;
pens = left / PEN_POINTS;
/* Add result to variant */
g_variant_builder_add (&builder, "(iii)", tries, utries, pens);
possibilities++;
}
}
}
if (possibilities == 0)
return NULL;
else
return g_variant_builder_end (&builder);
}
/**
* rugby_scoring_get_max_tries:
* @score: the score of a team
*
* Gets the maximum number of tries possible for a given score. Not related to
* Max Evans.
*
* Returns: the maximum number of tries possible
*/
gint
rugby_scoring_get_max_tries (gint score)
{
return (score % 5 == 1) ? score / UTRY_POINTS - 1 : score / UTRY_POINTS;
}
#ifndef __RUGBY_SCORING_H__
#define __RUGBY_SCORING_H__
#include <glib.h>
G_BEGIN_DECLS
#define TRY_POINTS 7
#define UTRY_POINTS 5
#define PEN_POINTS 3
typedef enum
{
RUGBY_SCORE_TYPE_TRY,
RUGBY_SCORE_TYPE_UTRY,
RUGBY_SCORE_TYPE_PEN
} RugbyScoreType;
GVariant * rugby_scoring_get_possibilities (gint score);
gint rugby_scoring_get_max_tries (gint score);
G_END_DECLS
#endif /*__RUGBY_SCORING_H__ */
#include "rugby-application.h"
int
main (int argc,
char **argv)
{
RugbyApplication *app;
gint retval;
app = rugby_application_new ();
retval = g_application_run (G_APPLICATION (app), argc, argv);
return retval;
}
.try
{
/* Scarlet red */
background-image: -gtk-gradient (linear,
left top, left bottom,
from (#ef2929),
to (#a40000));
}
.utry
{
/* Chameleon */
background-image: -gtk-gradient (linear,
left top, left bottom,
from (#8ae234),
to (#4e9a06));
}
.kick
{
/* Butter */
background-image: -gtk-gradient (linear,
left top, left bottom,
from (#fce94f),
to (#c4a000));
}
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/uk/me/bcowan/rugby">
<file preprocess="xml-stripblanks">interface.ui</file>
<file compressed="true">rugby.css</file>
</gresource>
</gresources>
/*
* SPDX-FileCopyrightText: 2012-2024 Bruce Cowan <bruce@bcowan.me.uk>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include "rugby-app-window.h"
#include "rugby-pref-dialog.h"
static void
on_activate ( GApplication *app,
G_GNUC_UNUSED void *user_data)
{
g_assert (GTK_IS_APPLICATION (app));
GtkWindow *window = gtk_application_get_active_window (GTK_APPLICATION (app));
if (!window)
window = GTK_WINDOW (rugby_app_window_new (GTK_APPLICATION (app)));
if (g_strcmp0 (PROFILE, "development") == 0)
gtk_widget_add_css_class (GTK_WIDGET (window), "devel");
gtk_window_present (GTK_WINDOW (window));
}
static void
about_activated (G_GNUC_UNUSED GSimpleAction *simple,
G_GNUC_UNUSED GVariant *parameter,
void *user_data)
{
GtkApplication *app = GTK_APPLICATION (user_data);
GtkWindow *window = gtk_application_get_active_window (app);
adw_show_about_dialog_from_appdata (GTK_WIDGET (window),
"uk/me/bcowan/Rugby/uk.me.bcowan.Rugby.metainfo.xml",
NULL,
"application-icon", "football-american",
"version", VERSION,
NULL);
}
static void
preferences_activated (G_GNUC_UNUSED GSimpleAction *simple,
G_GNUC_UNUSED GVariant *parameter,
void *user_data)
{
GtkApplication *app = GTK_APPLICATION (user_data);
GtkWindow *window = gtk_application_get_active_window (app);
RugbyPrefDialog *pref_window = rugby_pref_dialog_new ();
adw_dialog_present (ADW_DIALOG (pref_window), GTK_WIDGET (window));
}
static void
on_startup ( GApplication *app,
G_GNUC_UNUSED void *user_data)
{
const GActionEntry app_entries[] =
{
{ .name = "about", .activate = about_activated },
{ .name = "prefs", .activate = preferences_activated },
};
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries,
G_N_ELEMENTS (app_entries),
app);
gtk_application_set_accels_for_action (GTK_APPLICATION (app),
"app.prefs",
(const char*[]) { "<Ctrl>comma", NULL });
}
int
main (int argc,
char **argv)
{
g_autoptr (AdwApplication) app = adw_application_new ("uk.me.bcowan.Rugby",
G_APPLICATION_NON_UNIQUE);
g_signal_connect (app, "startup",
G_CALLBACK (on_startup), NULL);
g_signal_connect (app, "activate",
G_CALLBACK (on_activate), NULL);
return g_application_run (G_APPLICATION (app), argc, argv);
}
# SPDX-FileCopyrightText: 2016-2024 Bruce Cowan <bruce@bcowan.me.uk>
#
# SPDX-License-Identifier: GPL-3.0-or-later
sources = [
'main.c',
'rugby-app-window.c',
'rugby-list-store.c',
'rugby-possibility.c',
'rugby-possibility-widget.c',
'rugby-pref-dialog.c',
]
config_h = vcs_tag(
input: configure_file(
output: 'config.h.in',
configuration: conf,
),
output: 'config.h',
command: ['git', 'rev-parse', '--short', 'HEAD'],
fallback: (profile == 'development') ? 'devel' : '',
)
executable('rugby',
sources, config_h, resources,
dependencies: [gio_dep, gtk_dep, libadwaita_dep],
install_dir: get_option('bindir'),
install: true)
/*
* SPDX-FileCopyrightText: 2017-2024 Bruce Cowan <bruce@bcowan.me.uk>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include "rugby-app-window.h"
#include "rugby-list-store.h"
#include "rugby-possibility.h"
#include "rugby-possibility-widget.h"
#include <glib/gi18n.h>
struct _RugbyAppWindow
{
AdwApplicationWindow parent;
GtkWidget *scorespin;
GtkWidget *stack;
GSettings *win_settings;
};
G_DEFINE_FINAL_TYPE (RugbyAppWindow, rugby_app_window, ADW_TYPE_APPLICATION_WINDOW)
static void
list_store_items_changed_cb ( GListModel *model,
G_GNUC_UNUSED unsigned position,
G_GNUC_UNUSED unsigned removed,
G_GNUC_UNUSED unsigned added,
RugbyAppWindow *self)
{
unsigned n_items = g_list_model_get_n_items (model);
if (n_items == 0)
gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "empty_page");
else
gtk_stack_set_visible_child_name (GTK_STACK (self->stack), "list_page");
}
static char *
item_tooltip_cb (GtkListItem *item)
{
RugbyPossibility *possibility = gtk_list_item_get_item (item);
if (!possibility)
return NULL;
GString *tooltip = g_string_new (NULL);
int tries = rugby_possibility_get_tries (possibility);
int utries = rugby_possibility_get_utries (possibility);
int kicks = rugby_possibility_get_kicks (possibility);
if (tries > 0)
{
g_string_printf (tooltip, ngettext ("%d converted try",
"%d converted tries",
tries),
tries);
}
if (utries > 0)
{
if (tooltip->len > 0)
g_string_append (tooltip, ", ");
g_string_append_printf (tooltip, ngettext ("%d unconverted try",
"%d unconverted tries",
utries),
utries);
}
if (kicks > 0)
{
if (tooltip->len > 0)
g_string_append (tooltip, ", ");
g_string_append_printf (tooltip, ngettext ("%d kick",
"%d kicks",
kicks),
kicks);
}
return g_string_free (tooltip, FALSE);
}
static char *
header_label_cb (GtkListItem *item)
{
RugbyPossibility *possibility = gtk_list_header_get_item (GTK_LIST_HEADER (item));
if (!possibility)
return NULL;
int total_tries = rugby_possibility_total_tries (possibility);
return g_strdup_printf (ngettext ("%d try", "%d tries", total_tries), total_tries);
}
static void
activate_score_changed (G_GNUC_UNUSED GSimpleAction *action,
GVariant *parameter,
void *user_data)
{
RugbyAppWindow *self = RUGBY_APP_WINDOW (user_data);
double current_value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->scorespin));
const char *direction = g_variant_get_string (parameter, NULL);
if (g_strcmp0 (direction, "up") == 0)
gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->scorespin), current_value + 1.0);
else if (g_strcmp0 (direction, "down") == 0)
gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->scorespin), current_value - 1.0);
else
g_assert_not_reached ();
}
static void
rugby_app_window_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), RUGBY_TYPE_APP_WINDOW);
g_clear_object (&RUGBY_APP_WINDOW (object)->win_settings);
G_OBJECT_CLASS (rugby_app_window_parent_class)->dispose (object);
}
static void
rugby_app_window_init (RugbyAppWindow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
const GActionEntry win_entries[] = {
{ .name = "score-changed", .activate = activate_score_changed, .parameter_type = "s" },
};
g_action_map_add_action_entries (G_ACTION_MAP (self),
win_entries,
G_N_ELEMENTS (win_entries),
self);
self->win_settings = g_settings_new ("uk.me.bcowan.Rugby.window");
g_settings_bind (self->win_settings, "height",
self, "default-height",
G_SETTINGS_BIND_DEFAULT);
g_settings_bind (self->win_settings, "width",
self, "default-width",
G_SETTINGS_BIND_DEFAULT);
}
static void
rugby_app_window_class_init (RugbyAppWindowClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
g_type_ensure (RUGBY_TYPE_POSSIBILITY_WIDGET);
g_type_ensure (RUGBY_TYPE_LIST_STORE);
object_class->dispose = rugby_app_window_dispose;
gtk_widget_class_set_template_from_resource (widget_class,
"/uk/me/bcowan/Rugby/gtk/window.ui");
gtk_widget_class_bind_template_child (widget_class, RugbyAppWindow, scorespin);
gtk_widget_class_bind_template_child (widget_class, RugbyAppWindow, stack);
gtk_widget_class_bind_template_callback (widget_class, header_label_cb);
gtk_widget_class_bind_template_callback (widget_class, item_tooltip_cb);
gtk_widget_class_bind_template_callback (widget_class, list_store_items_changed_cb);
}
RugbyAppWindow *
rugby_app_window_new (GtkApplication *app)
{
return g_object_new (RUGBY_TYPE_APP_WINDOW,
"application", app,
NULL);
}
/*
* SPDX-FileCopyrightText: 2017-2022 Bruce Cowan <bruce@bcowan.me.uk>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <adwaita.h>
G_BEGIN_DECLS
#define RUGBY_TYPE_APP_WINDOW (rugby_app_window_get_type())
G_DECLARE_FINAL_TYPE (RugbyAppWindow, rugby_app_window, RUGBY, APP_WINDOW, AdwApplicationWindow)
RugbyAppWindow *rugby_app_window_new (GtkApplication *app);
G_END_DECLS
/*
* SPDX-FileCopyrightText: 2018-2024 Bruce Cowan <bruce@bcowan.me.uk>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "rugby-list-store.h"
#include "rugby-possibility.h"
#include <gio/gio.h>
#include <gtk/gtk.h>
struct _RugbyListStore
{
GObject parent_instance;
int score;
GListStore *items;
GSettings *settings;
};
static void rugby_list_store_list_model_iface_init (GListModelInterface *iface);
static void rugby_list_store_section_model_iface_init (GtkSectionModelInterface *iface);
G_DEFINE_FINAL_TYPE_WITH_CODE (RugbyListStore, rugby_list_store, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
rugby_list_store_list_model_iface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_SECTION_MODEL,
rugby_list_store_section_model_iface_init))
enum
{
PROP_SCORE = 1,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
// Helper functions
static int
sort_func ( gconstpointer a,
gconstpointer b,
G_GNUC_UNUSED void *user_data)
{
int atries = rugby_possibility_get_tries ((void *) a);
int autries = rugby_possibility_get_utries ((void *) a);
int btries = rugby_possibility_get_tries ((void *) b);
int butries = rugby_possibility_get_utries ((void *) b);
int trydiff = (btries + butries) - (atries + autries);
// Sort by total tries first, then converted
if (trydiff != 0)
return trydiff;
else
return btries - atries;
}
static void
process_data (RugbyListStore *self)
{
int try_points = g_settings_get_int (self->settings, "try-points");
int utry_points = g_settings_get_int (self->settings, "utry-points");
int kick_points = g_settings_get_int (self->settings, "kick-points");
unsigned old_length = g_list_model_get_n_items (G_LIST_MODEL (self->items));
g_list_store_remove_all (self->items);
if (self->score == 0)
{
g_list_model_items_changed (G_LIST_MODEL (self), 0, old_length, 0);
return;
}
int max_tries = self->score / try_points;
int max_utries = self->score / utry_points;
for (int tries = 0; tries <= max_tries; tries++)
{
for (int utries = 0; utries <= max_utries; utries++)
{
int left = self->score - (tries * try_points) - (utries * utry_points);
if (left < 0)
break;
if (left % kick_points == 0)
{
int kicks = left / kick_points;
RugbyPossibility *possibility = rugby_possibility_new (tries,
utries,
kicks);
g_list_store_append (self->items, possibility);
g_object_unref (possibility);
}
}
}
g_list_store_sort (self->items, sort_func, NULL);
g_list_model_items_changed (G_LIST_MODEL (self), 0, old_length,
g_list_model_get_n_items (G_LIST_MODEL (self->items)));
}
// GListModel implementation
static GType
rugby_list_store_get_item_type (G_GNUC_UNUSED GListModel *list)
{
return RUGBY_TYPE_POSSIBILITY;
}
static unsigned
rugby_list_store_get_n_items (GListModel *list)
{
RugbyListStore *self = RUGBY_LIST_STORE (list);
return g_list_model_get_n_items (G_LIST_MODEL (self->items));
}
static void *
rugby_list_store_get_item (GListModel *list,
unsigned position)
{
RugbyListStore *self = RUGBY_LIST_STORE (list);
return g_list_model_get_item (G_LIST_MODEL (self->items), position);
}
static void
rugby_list_store_list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = rugby_list_store_get_item_type;
iface->get_n_items = rugby_list_store_get_n_items;
iface->get_item = rugby_list_store_get_item;
}
// SectionModel implementation
static void
rugby_list_store_get_section (GtkSectionModel *model,
unsigned position,
unsigned *out_start,
unsigned *out_end)
{
RugbyListStore *self = RUGBY_LIST_STORE (model);
unsigned n_items = g_list_model_get_n_items (G_LIST_MODEL (self->items));
RugbyPossibility *possibility = g_list_model_get_item (G_LIST_MODEL (self->items),
position);
if (!possibility)
{
*out_start = n_items;
*out_end = G_MAXUINT;
return;
}
int target_tries = rugby_possibility_total_tries (possibility);
// Find start
for (unsigned i = 0; i < n_items; i++)
{
possibility = g_list_model_get_item (G_LIST_MODEL (self->items), i);
int total = rugby_possibility_total_tries (possibility);
if (total == target_tries)
{
*out_start = i;
break;
}
}
// Find end
for (unsigned i = *out_start + 1; i < n_items; i++)
{
possibility = g_list_model_get_item (G_LIST_MODEL (self->items), i);
int total = rugby_possibility_total_tries (possibility);
if (total != target_tries)
{
*out_end = i;
return;
}
}
*out_end = n_items;
}
static void
rugby_list_store_section_model_iface_init (GtkSectionModelInterface *iface)
{
iface->get_section = rugby_list_store_get_section;
}
// Class functions
static void
rugby_list_store_dispose (GObject *object)
{
RugbyListStore *self = RUGBY_LIST_STORE (object);
g_clear_object (&self->settings);
g_clear_object (&self->items);
G_OBJECT_CLASS (rugby_list_store_parent_class)->dispose (object);
}
static void
rugby_list_store_get_property (GObject *object,
unsigned prop_id,
GValue *value,
GParamSpec *pspec)
{
RugbyListStore *self = RUGBY_LIST_STORE (object);
switch (prop_id)
{
case PROP_SCORE:
g_value_set_int (value, self->score);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
rugby_list_store_set_property (GObject *object,
unsigned prop_id,
const GValue *value,
GParamSpec *pspec)
{
RugbyListStore *self = RUGBY_LIST_STORE (object);
switch (prop_id)
{
int score;
case PROP_SCORE:
score = g_value_get_int (value);
if (score != self->score)
{
self->score = score;
process_data (self);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SCORE]);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
rugby_list_store_class_init (RugbyListStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = rugby_list_store_dispose;
object_class->get_property = rugby_list_store_get_property;
object_class->set_property = rugby_list_store_set_property;
properties[PROP_SCORE] = g_param_spec_int ("score", "", "",
0, 200, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class,
N_PROPS,
properties);
}
static void
rugby_list_store_init (RugbyListStore *self)
{
self->settings = g_settings_new ("uk.me.bcowan.Rugby");
g_signal_connect_swapped (self->settings, "changed::try-points",
G_CALLBACK (process_data), self);
g_signal_connect_swapped (self->settings, "changed::utry-points",
G_CALLBACK (process_data), self);
g_signal_connect_swapped (self->settings, "changed::kick-points",
G_CALLBACK (process_data), self);
self->score = 0;
self->items = g_list_store_new (RUGBY_TYPE_POSSIBILITY);
}
/*
* SPDX-FileCopyrightText: 2018-2022 Bruce Cowan <bruce@bcowan.me.uk>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS
#define RUGBY_TYPE_LIST_STORE (rugby_list_store_get_type())
G_DECLARE_FINAL_TYPE (RugbyListStore, rugby_list_store, RUGBY, LIST_STORE, GObject)
G_END_DECLS
/*
* SPDX-FileCopyrightText: 2018-2024 Bruce Cowan <bruce@bcowan.me.uk>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include "rugby-possibility-widget.h"
#include "rugby-possibility.h"
#include <adwaita.h>
struct _RugbyPossibilityWidget
{
GtkWidget parent_instance;
GSettings *settings;
RugbyPossibility *possibility;
};
G_DEFINE_FINAL_TYPE (RugbyPossibilityWidget, rugby_possibility_widget, GTK_TYPE_WIDGET)
enum
{
PROP_POSSIBILITY = 1,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static void
rugby_possibility_widget_dispose (GObject *object)
{
RugbyPossibilityWidget *self = RUGBY_POSSIBILITY_WIDGET (object);
g_clear_object (&self->settings);
g_clear_object (&self->possibility);
G_OBJECT_CLASS (rugby_possibility_widget_parent_class)->dispose (object);
}
static void
rugby_possibility_widget_get_property (GObject *object,
unsigned prop_id,
GValue *value,
GParamSpec *pspec)
{
RugbyPossibilityWidget *self = RUGBY_POSSIBILITY_WIDGET (object);
switch (prop_id)
{
case PROP_POSSIBILITY:
g_value_set_object (value, self->possibility);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
rugby_possibility_widget_set_property (GObject *object,
unsigned prop_id,
const GValue *value,
GParamSpec *pspec)
{
RugbyPossibilityWidget *self = RUGBY_POSSIBILITY_WIDGET (object);
switch (prop_id)
{
case PROP_POSSIBILITY:
self->possibility = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
render_bar (GtkSnapshot *snapshot,
float x,
float w,
float h,
const GdkRGBA fill,
const GdkRGBA border)
{
graphene_rect_t area = GRAPHENE_RECT_INIT (x, 0.0, w, h);
GskRoundedRect rounded;
gsk_rounded_rect_init_from_rect (&rounded,
&area,
h / 2.0);
gtk_snapshot_push_rounded_clip (snapshot, &rounded);
gtk_snapshot_append_color (snapshot,
&fill,
&area);
gtk_snapshot_pop (snapshot);
gtk_snapshot_append_border (snapshot,
&rounded,
(float[]) { 2.0, 2.0, 2.0, 2.0 },
(GdkRGBA[]) { border,
border,
border,
border });
}
static void
rugby_possibility_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
RugbyPossibilityWidget *self = RUGBY_POSSIBILITY_WIDGET (widget);
int try_points = g_settings_get_int (self->settings, "try-points");
int utry_points = g_settings_get_int (self->settings, "utry-points");
int kick_points = g_settings_get_int (self->settings, "kick-points");
int width = gtk_widget_get_width (widget);
int height = gtk_widget_get_height (widget);
float x = 0.0;
int tries = rugby_possibility_get_tries (self->possibility);
int utries = rugby_possibility_get_utries (self->possibility);
int kicks = rugby_possibility_get_kicks (self->possibility);
int score = tries * try_points + utries * utry_points + kicks * kick_points;
GdkRGBA fill;
GdkRGBA border;
AdwStyleManager *manager = adw_style_manager_get_default ();
if (adw_style_manager_get_dark (manager))
gdk_rgba_parse (&border, "white");
else
gdk_rgba_parse (&border, "black");
// Tries
float w = width / (score / (float) try_points);
gdk_rgba_parse (&fill, "#33d17a"); // Green 3
for (int i = 0; i < tries; i++)
{
render_bar (snapshot, x, w, height, fill, border);
x += w;
}
// Unconverted tries
w = width / (score / (float) utry_points);
gdk_rgba_parse (&fill, "#f6d32d"); // Yellow 3
for (int i = 0; i < utries; i++)
{
render_bar (snapshot, x, w, height, fill, border);
x += w;
}
// Kicks
w = width / (score / (float) kick_points);
gdk_rgba_parse (&fill, "#e01b24"); // Red 3
for (int i = 0; i < kicks; i++)
{
render_bar (snapshot, x, w, height, fill, border);
x += w;
}
}
static void
rugby_possibility_widget_class_init (RugbyPossibilityWidgetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = rugby_possibility_widget_dispose;
object_class->get_property = rugby_possibility_widget_get_property;
object_class->set_property = rugby_possibility_widget_set_property;
widget_class->snapshot = rugby_possibility_widget_snapshot;
properties[PROP_POSSIBILITY] =
g_param_spec_object ("possibility", "", "",
RUGBY_TYPE_POSSIBILITY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class,
N_PROPS,
properties);
}
static void
rugby_possibility_widget_init (RugbyPossibilityWidget *self)
{
self->settings = g_settings_new ("uk.me.bcowan.Rugby");
}