mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
622 lines
21 KiB
C
622 lines
21 KiB
C
#include <gtk/gtk.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <math.h>
|
|
|
|
#define NUM_UNITS 12
|
|
#define FIRST_LOCKED 1
|
|
|
|
#define NODE_SIZE 88.0
|
|
#define PATH_SPAC 150.0
|
|
#define PATH_CYCLES 2.0
|
|
#define PATH_MARGIN 72.0
|
|
#define PATH_HEIGHT 380.0
|
|
#define PATH_AMP 100.0
|
|
|
|
static GtkStack *main_stack;
|
|
|
|
static double path_x_at(double t) {
|
|
return PATH_MARGIN + (NODE_SIZE / 2.0) + t * PATH_SPAC;
|
|
}
|
|
|
|
static double path_y_at(double t) {
|
|
double mid = PATH_HEIGHT / 2.0;
|
|
double phase = 2.0 * G_PI * PATH_CYCLES / (NUM_UNITS - 1);
|
|
return mid + PATH_AMP * sin(t * phase);
|
|
}
|
|
|
|
typedef struct { double r, g, b; } Rgb;
|
|
|
|
static Rgb color_from_hex(unsigned int hex) {
|
|
Rgb c;
|
|
c.r = (double)((hex >> 16) & 0xFF) / 255.0;
|
|
c.g = (double)((hex >> 8) & 0xFF) / 255.0;
|
|
c.b = (double)(hex & 0xFF) / 255.0;
|
|
return c;
|
|
}
|
|
|
|
static void on_continue_clicked(GtkButton *button, gpointer user_data) {
|
|
gtk_stack_set_visible_child_name(main_stack, "roadmap");
|
|
}
|
|
|
|
static void on_back_clicked(GtkButton *button, gpointer user_data) {
|
|
gtk_stack_set_visible_child_name(main_stack, "welcome");
|
|
}
|
|
|
|
static gboolean on_window_key_pressed(GtkEventControllerKey *controller,
|
|
guint keyval, guint keycode,
|
|
GdkModifierType state,
|
|
gpointer user_data) {
|
|
GtkWindow *window = user_data;
|
|
|
|
(void)controller;
|
|
(void)keycode;
|
|
|
|
if ((keyval == GDK_KEY_q &&
|
|
(state & (GDK_SUPER_MASK | GDK_META_MASK)) != 0) ||
|
|
(keyval == GDK_KEY_F4 &&
|
|
(state & GDK_ALT_MASK) != 0)) {
|
|
GtkApplication *app = gtk_window_get_application(window);
|
|
if (app != NULL)
|
|
g_application_quit(G_APPLICATION(app));
|
|
return GDK_EVENT_STOP;
|
|
}
|
|
|
|
return GDK_EVENT_PROPAGATE;
|
|
}
|
|
|
|
static GtkWidget *build_welcome_page(void) {
|
|
GtkWidget *box;
|
|
GtkWidget *heading;
|
|
GtkWidget *card;
|
|
GtkWidget *button;
|
|
|
|
// Create a vertical box to hold contents
|
|
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
|
|
gtk_widget_set_halign(box, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign(box, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_margin_top(box, 40);
|
|
gtk_widget_set_margin_bottom(box, 40);
|
|
|
|
// Heading
|
|
heading = gtk_label_new(NULL);
|
|
gtk_label_set_markup(GTK_LABEL(heading),
|
|
"Vítejte ve <span color=\"#cba6f7\">Sprechen.C</span>!");
|
|
gtk_widget_set_halign(heading, GTK_ALIGN_CENTER);
|
|
gtk_widget_add_css_class(heading, "heading");
|
|
gtk_box_append(GTK_BOX(box), heading);
|
|
|
|
// Card with the welcome message
|
|
card = gtk_label_new(
|
|
"Sprechen.C je vzdělávací program na procvičování Němčiny, určený "
|
|
"pro studenty středních škol a gymnázií.\n\n"
|
|
"Program je napsaný v Céčku studentama ze SSŠVT!");
|
|
gtk_label_set_justify(GTK_LABEL(card), GTK_JUSTIFY_CENTER);
|
|
gtk_label_set_wrap(GTK_LABEL(card), TRUE);
|
|
gtk_label_set_max_width_chars(GTK_LABEL(card), 48);
|
|
gtk_widget_add_css_class(card, "card");
|
|
gtk_box_append(GTK_BOX(box), card);
|
|
|
|
// Continue button
|
|
button = gtk_button_new_with_label("Pokračuj");
|
|
gtk_widget_set_halign(button, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_margin_top(button, 6);
|
|
gtk_widget_add_css_class(button, "btn-primary");
|
|
gtk_box_append(GTK_BOX(box), button);
|
|
|
|
g_signal_connect(button, "clicked", G_CALLBACK(on_continue_clicked), NULL);
|
|
|
|
return box;
|
|
}
|
|
|
|
static void draw_node_halo(cairo_t *cr, double cx, double cy,
|
|
double radius, double r, double g, double b,
|
|
double alpha) {
|
|
cairo_pattern_t *grad;
|
|
grad = cairo_pattern_create_radial(cx, cy, 6.0, cx, cy, radius);
|
|
cairo_pattern_add_color_stop_rgba(grad, 0.0, r, g, b, alpha);
|
|
cairo_pattern_add_color_stop_rgba(grad, 0.55, r, g, b, alpha * 0.30);
|
|
cairo_pattern_add_color_stop_rgba(grad, 1.0, r, g, b, 0.0);
|
|
cairo_set_source(cr, grad);
|
|
cairo_paint(cr);
|
|
cairo_pattern_destroy(grad);
|
|
}
|
|
|
|
static void draw_finish_flag(cairo_t *cr, double cx, double cy) {
|
|
double base_y = cy - NODE_SIZE / 2.0;
|
|
double top_y = base_y - 84.0;
|
|
double mid_y = top_y + 16.0;
|
|
|
|
cairo_new_path(cr);
|
|
cairo_move_to(cr, cx - 2.0, base_y);
|
|
cairo_line_to(cr, cx + 2.0, base_y);
|
|
cairo_line_to(cr, cx + 2.0, top_y);
|
|
cairo_line_to(cr, cx - 2.0, top_y);
|
|
cairo_close_path(cr);
|
|
cairo_set_source_rgb(cr, 0.804, 0.820, 0.875);
|
|
cairo_fill(cr);
|
|
|
|
cairo_new_path(cr);
|
|
cairo_arc(cr, cx, top_y - 4.0, 5.0, 0.0, 2.0 * G_PI);
|
|
cairo_set_source_rgb(cr, 0.969, 0.698, 0.671);
|
|
cairo_fill(cr);
|
|
|
|
cairo_new_path(cr);
|
|
cairo_move_to(cr, cx, top_y + 2.0);
|
|
cairo_line_to(cr, cx + 42.0, mid_y);
|
|
cairo_line_to(cr, cx, top_y + 30.0);
|
|
cairo_close_path(cr);
|
|
cairo_set_source_rgb(cr, 0.796, 0.651, 0.969);
|
|
cairo_fill(cr);
|
|
}
|
|
|
|
static void draw_rail(GtkDrawingArea *area, cairo_t *cr,
|
|
int width, int height, gpointer user_data) {
|
|
const int segments = (NUM_UNITS - 1) * 60;
|
|
const double t_end = (double)(NUM_UNITS - 1);
|
|
const double t_lit = (double)(FIRST_LOCKED - 1);
|
|
const Rgb mauve = color_from_hex(0xcba6f7);
|
|
const Rgb blue = color_from_hex(0x89b4fa);
|
|
double x0 = path_x_at(0);
|
|
double x1 = path_x_at(NUM_UNITS - 1);
|
|
int k;
|
|
|
|
(void)area;
|
|
(void)width;
|
|
(void)height;
|
|
(void)user_data;
|
|
|
|
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
|
|
cairo_paint(cr);
|
|
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
|
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
|
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
|
|
|
|
draw_node_halo(cr, path_x_at(FIRST_LOCKED - 1),
|
|
path_y_at(FIRST_LOCKED - 1),
|
|
NODE_SIZE * 1.05, mauve.r, mauve.g, mauve.b, 0.18);
|
|
|
|
cairo_new_path(cr);
|
|
cairo_move_to(cr, path_x_at(0), path_y_at(0));
|
|
for (k = 1; k <= segments; k++) {
|
|
double t = t_end * (double)k / (double)segments;
|
|
cairo_line_to(cr, path_x_at(t), path_y_at(t));
|
|
}
|
|
cairo_set_line_width(cr, 16);
|
|
cairo_set_source_rgb(cr, 0.153, 0.157, 0.235);
|
|
cairo_stroke(cr);
|
|
|
|
if (t_lit > 0.0) {
|
|
int lit_segments;
|
|
cairo_pattern_t *grad;
|
|
|
|
lit_segments = (int)(t_lit / t_end * (double)segments + 0.5);
|
|
cairo_new_path(cr);
|
|
cairo_move_to(cr, path_x_at(0), path_y_at(0));
|
|
for (k = 1; k <= lit_segments; k++) {
|
|
double t = t_end * (double)k / (double)segments;
|
|
cairo_line_to(cr, path_x_at(t), path_y_at(t));
|
|
}
|
|
|
|
grad = cairo_pattern_create_linear(x0, 0.0, x1, 0.0);
|
|
cairo_pattern_add_color_stop_rgba(grad, 0.0,
|
|
mauve.r, mauve.g, mauve.b, 0.16);
|
|
cairo_pattern_add_color_stop_rgba(grad, 1.0,
|
|
blue.r, blue.g, blue.b, 0.16);
|
|
cairo_set_source(cr, grad);
|
|
cairo_set_line_width(cr, 34);
|
|
cairo_stroke_preserve(cr);
|
|
cairo_pattern_destroy(grad);
|
|
|
|
grad = cairo_pattern_create_linear(x0, 0.0, x1, 0.0);
|
|
cairo_pattern_add_color_stop_rgba(grad, 0.0,
|
|
mauve.r, mauve.g, mauve.b, 1.0);
|
|
cairo_pattern_add_color_stop_rgba(grad, 1.0,
|
|
blue.r, blue.g, blue.b, 1.0);
|
|
cairo_set_source(cr, grad);
|
|
cairo_set_line_width(cr, 16);
|
|
cairo_stroke_preserve(cr);
|
|
cairo_pattern_destroy(grad);
|
|
}
|
|
|
|
{
|
|
double start_t = t_lit > 0.0 ? t_lit : 0.0;
|
|
int first_seg = (int)(start_t / t_end * (double)segments + 0.5);
|
|
cairo_set_source_rgba(cr, 0.651, 0.678, 0.784, 0.17);
|
|
cairo_set_dash(cr, (double[]){1.0, 26.0}, 2, 0.0);
|
|
cairo_new_path(cr);
|
|
cairo_move_to(cr, path_x_at(start_t), path_y_at(start_t));
|
|
for (k = first_seg; k <= segments; k++) {
|
|
double t = t_end * (double)k / (double)segments;
|
|
cairo_line_to(cr, path_x_at(t), path_y_at(t));
|
|
}
|
|
cairo_stroke(cr);
|
|
cairo_set_dash(cr, NULL, 0, 0.0);
|
|
}
|
|
|
|
draw_finish_flag(cr, path_x_at(NUM_UNITS - 1),
|
|
path_y_at(NUM_UNITS - 1));
|
|
}
|
|
|
|
static void add_path_node(GtkFixed *fixed, int index) {
|
|
int num = index + 1;
|
|
gboolean done = index < FIRST_LOCKED - 1;
|
|
gboolean current = index == FIRST_LOCKED - 1;
|
|
gboolean locked = index >= FIRST_LOCKED;
|
|
double cx = path_x_at(index);
|
|
double cy = path_y_at(index);
|
|
GtkWidget *card;
|
|
GtkWidget *vbox;
|
|
GtkWidget *number;
|
|
char *text;
|
|
|
|
card = gtk_button_new();
|
|
gtk_widget_add_css_class(card, "unit-node");
|
|
gtk_widget_set_sensitive(card, !locked);
|
|
gtk_widget_set_size_request(card, (int)NODE_SIZE, (int)NODE_SIZE);
|
|
if (done)
|
|
gtk_widget_add_css_class(card, "done");
|
|
else if (current)
|
|
gtk_widget_add_css_class(card, "current");
|
|
else
|
|
gtk_widget_add_css_class(card, "locked");
|
|
|
|
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
|
|
gtk_widget_set_halign(vbox, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign(vbox, GTK_ALIGN_CENTER);
|
|
gtk_button_set_child(GTK_BUTTON(card), vbox);
|
|
|
|
text = g_strdup_printf("%d", num);
|
|
number = gtk_label_new(text);
|
|
g_free(text);
|
|
gtk_widget_add_css_class(number, "unit-number");
|
|
gtk_box_append(GTK_BOX(vbox), number);
|
|
|
|
if (done) {
|
|
GtkWidget *icon;
|
|
icon = gtk_image_new_from_icon_name("object-select-symbolic");
|
|
gtk_image_set_pixel_size(GTK_IMAGE(icon), 18);
|
|
gtk_widget_add_css_class(icon, "state-icon");
|
|
gtk_box_append(GTK_BOX(vbox), icon);
|
|
} else if (locked) {
|
|
GtkWidget *lock;
|
|
lock = gtk_image_new_from_icon_name("system-lock-screen-symbolic");
|
|
gtk_image_set_pixel_size(GTK_IMAGE(lock), 16);
|
|
gtk_widget_add_css_class(lock, "lock-icon");
|
|
gtk_box_append(GTK_BOX(vbox), lock);
|
|
}
|
|
|
|
if (done)
|
|
text = g_strdup_printf("Jednotka %d · dokončeno", num);
|
|
else if (current)
|
|
text = g_strdup_printf("Jednotka %d · pokračovat", num);
|
|
else
|
|
text = g_strdup_printf("Jednotka %d · zamčeno", num);
|
|
gtk_widget_set_tooltip_text(card, text);
|
|
g_free(text);
|
|
|
|
gtk_fixed_put(fixed, card,
|
|
(int)(cx - NODE_SIZE / 2.0),
|
|
(int)(cy - NODE_SIZE / 2.0));
|
|
|
|
if (current) {
|
|
GtkWidget *cap;
|
|
cap = gtk_label_new(NULL);
|
|
gtk_label_set_markup(GTK_LABEL(cap),
|
|
"<span color=\"#89b4fa\">▶</span> Pokračuj zde");
|
|
gtk_widget_add_css_class(cap, "node-caption");
|
|
gtk_fixed_put(fixed, cap,
|
|
(int)(cx + NODE_SIZE / 2.0 + 14.0),
|
|
(int)(cy - 30.0));
|
|
}
|
|
}
|
|
|
|
static GtkWidget *build_roadmap_page(void) {
|
|
GtkWidget *page;
|
|
GtkWidget *top;
|
|
GtkWidget *back;
|
|
GtkWidget *center;
|
|
GtkWidget *heading;
|
|
GtkWidget *sub;
|
|
GtkWidget *scroll;
|
|
GtkWidget *wrap;
|
|
GtkWidget *fixed;
|
|
GtkWidget *rail;
|
|
int canvas_w;
|
|
int i;
|
|
|
|
canvas_w = (int)(path_x_at(NUM_UNITS - 1) + PATH_MARGIN);
|
|
|
|
page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_widget_set_hexpand(page, TRUE);
|
|
gtk_widget_set_vexpand(page, TRUE);
|
|
gtk_widget_set_margin_start(page, 32);
|
|
gtk_widget_set_margin_end(page, 32);
|
|
gtk_widget_set_margin_top(page, 24);
|
|
gtk_widget_set_margin_bottom(page, 24);
|
|
|
|
top = gtk_center_box_new();
|
|
gtk_widget_set_margin_top(top, 2);
|
|
gtk_widget_set_margin_bottom(top, 6);
|
|
gtk_box_append(GTK_BOX(page), top);
|
|
|
|
back = gtk_button_new_from_icon_name("go-previous-symbolic");
|
|
gtk_widget_add_css_class(back, "back-btn");
|
|
gtk_widget_set_valign(back, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_tooltip_text(back, "Zpět");
|
|
g_signal_connect(back, "clicked", G_CALLBACK(on_back_clicked), NULL);
|
|
gtk_center_box_set_start_widget(GTK_CENTER_BOX(top), back);
|
|
|
|
center = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
|
|
gtk_widget_set_valign(center, GTK_ALIGN_CENTER);
|
|
gtk_center_box_set_center_widget(GTK_CENTER_BOX(top), center);
|
|
|
|
heading = gtk_label_new("Učební plán");
|
|
gtk_widget_set_halign(heading, GTK_ALIGN_CENTER);
|
|
gtk_widget_add_css_class(heading, "roadmap-title");
|
|
gtk_box_append(GTK_BOX(center), heading);
|
|
|
|
sub = gtk_label_new("Vyberte jednotku na cestě a začněte procvičovat.");
|
|
gtk_widget_set_halign(sub, GTK_ALIGN_CENTER);
|
|
gtk_widget_add_css_class(sub, "roadmap-sub");
|
|
gtk_box_append(GTK_BOX(center), sub);
|
|
|
|
scroll = gtk_scrolled_window_new();
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
|
|
gtk_widget_set_vexpand(scroll, TRUE);
|
|
gtk_widget_set_margin_top(scroll, 14);
|
|
gtk_box_append(GTK_BOX(page), scroll);
|
|
|
|
wrap = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_widget_set_vexpand(wrap, TRUE);
|
|
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), wrap);
|
|
|
|
fixed = gtk_fixed_new();
|
|
gtk_widget_set_halign(fixed, GTK_ALIGN_START);
|
|
gtk_widget_set_valign(fixed, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_size_request(fixed, canvas_w, (int)PATH_HEIGHT);
|
|
gtk_box_append(GTK_BOX(wrap), fixed);
|
|
|
|
rail = gtk_drawing_area_new();
|
|
gtk_widget_set_size_request(rail, canvas_w, (int)PATH_HEIGHT);
|
|
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(rail), draw_rail, NULL, NULL);
|
|
gtk_fixed_put(GTK_FIXED(fixed), rail, 0, 0);
|
|
|
|
for (i = 0; i < NUM_UNITS; i++)
|
|
add_path_node(GTK_FIXED(fixed), i);
|
|
|
|
return page;
|
|
}
|
|
|
|
static void activate(GtkApplication *app, gpointer user_data) {
|
|
GtkWindow *window;
|
|
GtkWidget *headerbar;
|
|
GtkWidget *title_label;
|
|
GtkWidget *welcome_page;
|
|
GtkWidget *roadmap_page;
|
|
GtkEventController *keys;
|
|
GtkCssProvider *provider;
|
|
|
|
// Create a new application window
|
|
window = GTK_WINDOW(gtk_application_window_new(app));
|
|
gtk_window_set_title(window, "Sprechen.c");
|
|
gtk_window_set_default_size(window, 760, 560);
|
|
|
|
// Create a styled headerbar (titlebar)
|
|
headerbar = gtk_header_bar_new();
|
|
gtk_widget_add_css_class(headerbar, "titlebar");
|
|
title_label = gtk_label_new("Sprechen.c");
|
|
gtk_widget_add_css_class(title_label, "app-title");
|
|
gtk_header_bar_set_title_widget(GTK_HEADER_BAR(headerbar), title_label);
|
|
|
|
gtk_window_set_titlebar(window, headerbar);
|
|
|
|
// Stack navigation between welcome and roadmap pages
|
|
main_stack = GTK_STACK(gtk_stack_new());
|
|
gtk_stack_set_transition_type(GTK_STACK(main_stack),
|
|
GTK_STACK_TRANSITION_TYPE_CROSSFADE);
|
|
gtk_window_set_child(window, GTK_WIDGET(main_stack));
|
|
|
|
welcome_page = build_welcome_page();
|
|
roadmap_page = build_roadmap_page();
|
|
|
|
gtk_stack_add_named(main_stack, welcome_page, "welcome");
|
|
gtk_stack_add_named(main_stack, roadmap_page, "roadmap");
|
|
gtk_stack_set_visible_child_name(main_stack, "welcome");
|
|
|
|
keys = gtk_event_controller_key_new();
|
|
gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(keys),
|
|
GTK_PHASE_CAPTURE);
|
|
g_signal_connect(keys, "key-pressed",
|
|
G_CALLBACK(on_window_key_pressed), window);
|
|
gtk_widget_add_controller(GTK_WIDGET(window), GTK_EVENT_CONTROLLER(keys));
|
|
|
|
// Load Catppuccin Mocha CSS
|
|
provider = gtk_css_provider_new();
|
|
gtk_css_provider_load_from_string(provider,
|
|
"window {"
|
|
" background-color: #1e1e2e;"
|
|
" background-image: linear-gradient(160deg, #11111b 0%, #1e1e2e 40%, #313244 100%);"
|
|
"}"
|
|
".titlebar {"
|
|
" background-color: #1e1e2e;"
|
|
" background-image: none;"
|
|
" box-shadow: none;"
|
|
" border: none;"
|
|
"}"
|
|
".app-title {"
|
|
" color: #cdd6f4;"
|
|
" font-weight: 700;"
|
|
" font-size: 14px;"
|
|
"}"
|
|
".heading {"
|
|
" color: #cdd6f4;"
|
|
" font-size: 32px;"
|
|
" font-weight: 800;"
|
|
"}"
|
|
".card {"
|
|
" background-color: rgba(49, 50, 68, 0.92);"
|
|
" border: 1px solid #45475a;"
|
|
" border-radius: 18px;"
|
|
" padding: 30px 34px;"
|
|
" color: #cdd6f4;"
|
|
" font-size: 16px;"
|
|
"}"
|
|
".btn-primary {"
|
|
" background-image: linear-gradient(135deg, #cba6f7 0%, #b4befe 100%);"
|
|
" color: #1e1e2e;"
|
|
" font-size: 16px;"
|
|
" font-weight: 700;"
|
|
" padding: 12px 40px;"
|
|
" border: none;"
|
|
" border-radius: 999px;"
|
|
" box-shadow: 0 6px 18px rgba(203, 166, 247, 0.25);"
|
|
" transition: box-shadow 150ms ease, background-image 150ms ease;"
|
|
"}"
|
|
".btn-primary:hover {"
|
|
" box-shadow: 0 8px 24px rgba(203, 166, 247, 0.45);"
|
|
" background-image: linear-gradient(135deg, #b4befe 0%, #89b4fa 100%);"
|
|
"}"
|
|
".btn-primary:active {"
|
|
" box-shadow: 0 3px 10px rgba(203, 166, 247, 0.35);"
|
|
"}"
|
|
".roadmap-title {"
|
|
" color: #cdd6f4;"
|
|
" font-size: 28px;"
|
|
" font-weight: 800;"
|
|
"}"
|
|
".roadmap-sub {"
|
|
" color: #a6adc8;"
|
|
" font-size: 14px;"
|
|
"}"
|
|
".back-btn {"
|
|
" min-width: 42px;"
|
|
" min-height: 42px;"
|
|
" padding: 0;"
|
|
" border-radius: 999px;"
|
|
" background-color: rgba(49, 50, 68, 0.9);"
|
|
" border: 1px solid #45475a;"
|
|
" color: #cdd6f4;"
|
|
" box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);"
|
|
" transition: background-color 150ms ease, color 150ms ease,"
|
|
" border-color 150ms ease, box-shadow 150ms ease;"
|
|
"}"
|
|
".back-btn:hover {"
|
|
" background-color: #45475a;"
|
|
" border-color: #585b70;"
|
|
" color: #cba6f7;"
|
|
" box-shadow: 0 6px 20px rgba(203, 166, 247, 0.35);"
|
|
"}"
|
|
".back-btn:active {"
|
|
" background-color: #313244;"
|
|
" box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);"
|
|
"}"
|
|
".unit-node {"
|
|
" background-image: none;"
|
|
" background-color: #2b2d40;"
|
|
" color: #cdd6f4;"
|
|
" border: 2px solid #3b3e56;"
|
|
" border-radius: 999px;"
|
|
" padding: 0;"
|
|
" box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);"
|
|
" transition: transform 150ms ease, box-shadow 150ms ease,"
|
|
" border-color 150ms ease;"
|
|
"}"
|
|
".unit-node.done {"
|
|
" background-image: linear-gradient(135deg, #a6e3a1 0%, #94e2d5 100%);"
|
|
" border-color: #a6e3a1;"
|
|
" color: #1e1e2e;"
|
|
" box-shadow: 0 4px 16px rgba(166, 227, 161, 0.35);"
|
|
"}"
|
|
".unit-node.current {"
|
|
" background-image: linear-gradient(135deg, #cba6f7 0%, #b4befe 55%, #89b4fa 100%);"
|
|
" border-color: #cba6f7;"
|
|
" color: #1e1e2e;"
|
|
" box-shadow: 0 4px 18px rgba(203, 166, 247, 0.5),"
|
|
" 0 0 0 4px rgba(30, 30, 46, 0.9),"
|
|
" 0 0 0 7px rgba(203, 166, 247, 0.45);"
|
|
"}"
|
|
".unit-node.done:hover,"
|
|
".unit-node.current:hover {"
|
|
" transform: translateY(-3px) scale(1.05);"
|
|
"}"
|
|
".unit-node.done:hover {"
|
|
" box-shadow: 0 8px 22px rgba(166, 227, 161, 0.5);"
|
|
"}"
|
|
".unit-node.current:hover {"
|
|
" box-shadow: 0 8px 26px rgba(203, 166, 247, 0.65),"
|
|
" 0 0 0 4px rgba(30, 30, 46, 0.9),"
|
|
" 0 0 0 7px rgba(203, 166, 247, 0.55);"
|
|
"}"
|
|
".unit-node.done:active,"
|
|
".unit-node.current:active {"
|
|
" transform: translateY(0px) scale(0.98);"
|
|
"}"
|
|
".unit-node.locked {"
|
|
" background-image: none;"
|
|
" background-color: #24263a;"
|
|
" border: 2px solid #383b52;"
|
|
" color: #6c7086;"
|
|
" box-shadow: none;"
|
|
"}"
|
|
".unit-node.locked:hover {"
|
|
" transform: none;"
|
|
" box-shadow: none;"
|
|
"}"
|
|
".unit-number {"
|
|
" font-size: 24px;"
|
|
" font-weight: 800;"
|
|
"}"
|
|
".unit-node.done .unit-number,"
|
|
".unit-node.current .unit-number {"
|
|
" color: #1e1e2e;"
|
|
"}"
|
|
".unit-node.locked .unit-number {"
|
|
" color: #6c7086;"
|
|
"}"
|
|
".state-icon {"
|
|
" color: #1e1e2e;"
|
|
"}"
|
|
".unit-node.locked .lock-icon {"
|
|
" color: #585b70;"
|
|
"}"
|
|
".node-caption {"
|
|
" background-color: rgba(30, 30, 46, 0.88);"
|
|
" border: 1px solid #45475a;"
|
|
" border-radius: 999px;"
|
|
" padding: 7px 15px;"
|
|
" color: #cdd6f4;"
|
|
" font-size: 13px;"
|
|
" font-weight: 700;"
|
|
" box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);"
|
|
"}"
|
|
"tooltip {"
|
|
" background-color: #181825;"
|
|
" border: 1px solid #45475a;"
|
|
" border-radius: 10px;"
|
|
" color: #cdd6f4;"
|
|
"}"
|
|
);
|
|
|
|
// Apply CSS to the window
|
|
gtk_style_context_add_provider_for_display(
|
|
gdk_display_get_default(),
|
|
GTK_STYLE_PROVIDER(provider),
|
|
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
|
|
g_object_unref(provider);
|
|
|
|
gtk_window_present(window);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
GtkApplication *app;
|
|
int status;
|
|
|
|
app = gtk_application_new("org.example.Sprechen", G_APPLICATION_DEFAULT_FLAGS);
|
|
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
|
|
status = g_application_run(G_APPLICATION(app), argc, argv);
|
|
g_object_unref(app);
|
|
|
|
return status;
|
|
}
|