mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
447 lines
15 KiB
C
447 lines
15 KiB
C
#include "maturita.h"
|
||
|
||
/* Vector icons drawn with cairo so they never depend on the system
|
||
* icon theme (which is missing on some installs). */
|
||
|
||
void draw_check_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
Rgb c = color_from_hex(app_theme.on_accent);
|
||
|
||
(void)area;
|
||
(void)data;
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
||
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
|
||
cairo_set_line_width(cr, width * 0.16);
|
||
cairo_move_to(cr, width * 0.24, height * 0.56);
|
||
cairo_line_to(cr, width * 0.43, height * 0.74);
|
||
cairo_line_to(cr, width * 0.78, height * 0.30);
|
||
cairo_stroke(cr);
|
||
}
|
||
|
||
/* Paint a padlock centred in the size x size box with top-left (x0,y0). */
|
||
void lock_paint(cairo_t *cr, double x0, double y0, double size,
|
||
const Rgb *c) {
|
||
double sl = MAX(1.6, size * 0.13); /* shackle stroke width */
|
||
double bw = size * 0.60; /* body width */
|
||
double bh = size * 0.44; /* body height */
|
||
double cx = x0 + size / 2.0;
|
||
double body_top = y0 + size - bh - size * 0.04;
|
||
double bx = cx - bw / 2.0;
|
||
double r = bw / 2.0 - sl / 2.0; /* shackle arc radius */
|
||
|
||
cairo_set_source_rgb(cr, c->r, c->g, c->b);
|
||
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
||
cairo_set_line_width(cr, sl);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, cx, body_top, r, G_PI, 2.0 * G_PI);
|
||
cairo_stroke(cr);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_rectangle(cr, bx, body_top, bw, bh);
|
||
cairo_fill(cr);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, cx, body_top + bh * 0.42, size * 0.07, 0.0, 2.0 * G_PI);
|
||
{
|
||
Rgb hole = color_from_hex(app_theme.crust);
|
||
cairo_set_source_rgb(cr, hole.r, hole.g, hole.b);
|
||
}
|
||
cairo_fill(cr);
|
||
}
|
||
|
||
void draw_lock_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
Rgb c = color_from_hex(app_theme.overlay);
|
||
(void)area;
|
||
(void)data;
|
||
lock_paint(cr, 0.0, 0.0, MIN(width, height), &c);
|
||
}
|
||
|
||
/* Wireless icon: three upward arcs that fan out from a base dot. */
|
||
void draw_wifi_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const double s = MIN(width, height);
|
||
const double cx = width / 2.0;
|
||
const double cy = height * 0.68;
|
||
const double dot_r = s * 0.06;
|
||
const double radii[3] = {s * 0.15, s * 0.27, s * 0.39};
|
||
const double phi = 0.75;
|
||
/* Keep the icon legible on the accent-gradient bubble while giving it a
|
||
* hint of the active theme: blend the contrasting on-accent colour a
|
||
* little way towards the palette's middle accent. */
|
||
const Rgb c = mix_rgb(color_from_hex(app_theme.on_accent),
|
||
color_from_hex(app_theme.accent2), 0.22);
|
||
|
||
(void)area;
|
||
(void)data;
|
||
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
||
cairo_set_line_width(cr, MAX(1.5, s * 0.07));
|
||
|
||
for (int i = 0; i < 3; i++) {
|
||
double r = radii[i];
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, cx, cy, r, -G_PI / 2.0 - phi, -G_PI / 2.0 + phi);
|
||
cairo_stroke(cr);
|
||
}
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, cx, cy, dot_r, 0.0, 2.0 * G_PI);
|
||
cairo_fill(cr);
|
||
}
|
||
|
||
/* CPU / chip icon for Technické vybavení. */
|
||
void draw_chip_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const double s = MIN(width, height);
|
||
const double cx = width / 2.0;
|
||
const double cy = height / 2.0;
|
||
const double body = s * 0.42;
|
||
const double pin_len = s * 0.12;
|
||
const double pin_w = s * 0.055;
|
||
const Rgb c = mix_rgb(color_from_hex(app_theme.on_accent),
|
||
color_from_hex(app_theme.accent2), 0.22);
|
||
|
||
(void)area;
|
||
(void)data;
|
||
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
|
||
cairo_rectangle(cr, cx - body / 2.0, cy - body / 2.0, body, body);
|
||
cairo_fill(cr);
|
||
|
||
cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
|
||
cairo_set_line_width(cr, pin_w);
|
||
for (int i = 0; i < 4; i++) {
|
||
double t = (i + 0.5) / 4.0;
|
||
double x = cx - body / 2.0 + t * body;
|
||
double y = cy - body / 2.0 + t * body;
|
||
|
||
cairo_move_to(cr, x, cy - body / 2.0);
|
||
cairo_line_to(cr, x, cy - body / 2.0 - pin_len);
|
||
cairo_move_to(cr, x, cy + body / 2.0);
|
||
cairo_line_to(cr, x, cy + body / 2.0 + pin_len);
|
||
cairo_move_to(cr, cx - body / 2.0, y);
|
||
cairo_line_to(cr, cx - body / 2.0 - pin_len, y);
|
||
cairo_move_to(cr, cx + body / 2.0, y);
|
||
cairo_line_to(cr, cx + body / 2.0 + pin_len, y);
|
||
}
|
||
cairo_stroke(cr);
|
||
|
||
{
|
||
Rgb hole = color_from_hex(app_theme.crust);
|
||
double core = body * 0.34;
|
||
|
||
cairo_set_source_rgb(cr, hole.r, hole.g, hole.b);
|
||
cairo_rectangle(cr, cx - core / 2.0, cy - core / 2.0, core, core);
|
||
cairo_fill(cr);
|
||
}
|
||
}
|
||
|
||
/* Open book icon – used for the reading list and overview sections. */
|
||
void draw_book_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const double w = width;
|
||
const double h = height;
|
||
const double mid = w * 0.5;
|
||
const double left = w * 0.13;
|
||
const double right = w * 0.87;
|
||
const double top = h * 0.24;
|
||
const double bot = h * 0.80;
|
||
const Rgb c = mix_rgb(color_from_hex(app_theme.accent2),
|
||
color_from_hex(app_theme.accent3), 0.35);
|
||
|
||
(void)area;
|
||
(void)data;
|
||
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_move_to(cr, mid, top);
|
||
cairo_curve_to(cr, w * 0.40, top - h * 0.07,
|
||
w * 0.24, top - h * 0.02, left, top + h * 0.07);
|
||
cairo_line_to(cr, left, bot - h * 0.05);
|
||
cairo_curve_to(cr, w * 0.24, bot - h * 0.11,
|
||
w * 0.40, bot - h * 0.05, mid, bot);
|
||
cairo_close_path(cr);
|
||
cairo_fill(cr);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_move_to(cr, mid, top);
|
||
cairo_curve_to(cr, w * 0.60, top - h * 0.07,
|
||
w * 0.76, top - h * 0.02, right, top + h * 0.07);
|
||
cairo_line_to(cr, right, bot - h * 0.05);
|
||
cairo_curve_to(cr, w * 0.76, bot - h * 0.11,
|
||
w * 0.60, bot - h * 0.05, mid, bot);
|
||
cairo_close_path(cr);
|
||
cairo_fill(cr);
|
||
}
|
||
|
||
/* Question mark in a circle – quiz. */
|
||
void draw_quiz_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const Rgb c = color_from_hex(app_theme.accent);
|
||
PangoLayout *layout;
|
||
PangoFontDescription *fd;
|
||
int tw, th;
|
||
|
||
(void)area;
|
||
(void)data;
|
||
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
layout = pango_cairo_create_layout(cr);
|
||
fd = pango_font_description_new();
|
||
pango_font_description_set_weight(fd, PANGO_WEIGHT_HEAVY);
|
||
pango_font_description_set_absolute_size(
|
||
fd, MIN(width, height) * 1.05 * PANGO_SCALE);
|
||
pango_layout_set_font_description(layout, fd);
|
||
pango_layout_set_text(layout, "?", -1);
|
||
pango_layout_get_pixel_size(layout, &tw, &th);
|
||
cairo_move_to(cr, (width - tw) / 2.0, (height - th) / 2.0);
|
||
pango_cairo_show_layout(cr, layout);
|
||
pango_font_description_free(fd);
|
||
g_object_unref(layout);
|
||
}
|
||
|
||
/* Two opposing arrows – ordering / drag & drop. */
|
||
void draw_order_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const double w = width;
|
||
const double h = height;
|
||
const Rgb c = color_from_hex(app_theme.success2);
|
||
|
||
(void)area;
|
||
(void)data;
|
||
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
cairo_set_line_width(cr, MAX(1.6, MIN(w, h) * 0.11));
|
||
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
||
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_move_to(cr, w * 0.36, h * 0.80);
|
||
cairo_line_to(cr, w * 0.36, h * 0.24);
|
||
cairo_move_to(cr, w * 0.22, h * 0.40);
|
||
cairo_line_to(cr, w * 0.36, h * 0.24);
|
||
cairo_line_to(cr, w * 0.50, h * 0.40);
|
||
cairo_stroke(cr);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_move_to(cr, w * 0.64, h * 0.20);
|
||
cairo_line_to(cr, w * 0.64, h * 0.76);
|
||
cairo_move_to(cr, w * 0.50, h * 0.60);
|
||
cairo_line_to(cr, w * 0.64, h * 0.76);
|
||
cairo_line_to(cr, w * 0.78, h * 0.60);
|
||
cairo_stroke(cr);
|
||
}
|
||
|
||
/* Globe – the world / superpowers. */
|
||
void draw_globe_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const double cx = width / 2.0;
|
||
const double cy = height / 2.0;
|
||
const double r = MIN(width, height) * 0.36;
|
||
const Rgb c = color_from_hex(app_theme.accent2);
|
||
|
||
(void)area;
|
||
(void)data;
|
||
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
cairo_set_line_width(cr, MAX(1.4, r * 0.13));
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, cx, cy, r, 0.0, 2.0 * G_PI);
|
||
cairo_stroke(cr);
|
||
|
||
cairo_save(cr);
|
||
cairo_translate(cr, cx, cy);
|
||
cairo_scale(cr, 0.52, 1.0);
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, 0.0, 0.0, r, 0.0, 2.0 * G_PI);
|
||
cairo_stroke(cr);
|
||
cairo_restore(cr);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_move_to(cr, cx - r, cy);
|
||
cairo_line_to(cr, cx + r, cy);
|
||
cairo_move_to(cr, cx - r * 0.86, cy - r * 0.52);
|
||
cairo_line_to(cr, cx + r * 0.86, cy - r * 0.52);
|
||
cairo_move_to(cr, cx - r * 0.86, cy + r * 0.52);
|
||
cairo_line_to(cr, cx + r * 0.86, cy + r * 0.52);
|
||
cairo_stroke(cr);
|
||
}
|
||
|
||
/* Two people – characters. */
|
||
void draw_people_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const Rgb c = color_from_hex(app_theme.accent3);
|
||
|
||
(void)area;
|
||
(void)data;
|
||
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, width * 0.38, height * 0.36, width * 0.15,
|
||
0.0, 2.0 * G_PI);
|
||
cairo_fill(cr);
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, width * 0.38, height * 1.02, width * 0.30,
|
||
G_PI, 2.0 * G_PI);
|
||
cairo_fill(cr);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, width * 0.67, height * 0.42, width * 0.13,
|
||
0.0, 2.0 * G_PI);
|
||
cairo_fill(cr);
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, width * 0.67, height * 1.06, width * 0.27,
|
||
G_PI, 2.0 * G_PI);
|
||
cairo_fill(cr);
|
||
}
|
||
|
||
/* Light bulb – key terms / ideas. */
|
||
void draw_bulb_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const double cx = width / 2.0;
|
||
const double r = MIN(width, height) * 0.30;
|
||
const double cy = height * 0.40;
|
||
const Rgb c = color_from_hex(app_theme.warning);
|
||
|
||
(void)area;
|
||
(void)data;
|
||
|
||
cairo_set_source_rgb(cr, c.r, c.g, c.b);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, cx, cy, r, 0.0, 2.0 * G_PI);
|
||
cairo_fill(cr);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_rectangle(cr, cx - r * 0.45, cy + r * 0.72,
|
||
r * 0.90, r * 0.66);
|
||
cairo_fill(cr);
|
||
|
||
cairo_new_path(cr);
|
||
cairo_arc(cr, cx, cy + r * 1.72, r * 0.52, 0.0, G_PI);
|
||
cairo_fill(cr);
|
||
}
|
||
|
||
void draw_back_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const double size = 15.0; /* fixed logical icon size */
|
||
const double stroke = 1.8;
|
||
double ox = (width - size) / 2.0;
|
||
double oy = (height - size) / 2.0;
|
||
GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(area));
|
||
GdkRGBA color;
|
||
|
||
(void)data;
|
||
gtk_style_context_get_color(gtk_widget_get_style_context(parent), &color);
|
||
cairo_set_source_rgb(cr, color.red, color.green, color.blue);
|
||
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
||
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
|
||
cairo_set_line_width(cr, stroke);
|
||
cairo_move_to(cr, ox + size * 0.72, oy + size * 0.24);
|
||
cairo_line_to(cr, ox + size * 0.30, oy + size * 0.50);
|
||
cairo_line_to(cr, ox + size * 0.72, oy + size * 0.76);
|
||
cairo_stroke(cr);
|
||
}
|
||
|
||
void draw_settings_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
const int teeth = 8;
|
||
const double size = 16.0;
|
||
double cx = width / 2.0;
|
||
double cy = height / 2.0;
|
||
double r_outer = size * 0.48;
|
||
double r_inner = size * 0.34;
|
||
double r_hole = size * 0.16;
|
||
double tooth_half = G_PI / (double)teeth;
|
||
GtkWidget *color_w = data ? GTK_WIDGET(data)
|
||
: gtk_widget_get_parent(GTK_WIDGET(area));
|
||
GdkRGBA color;
|
||
int i;
|
||
|
||
gtk_style_context_get_color(gtk_widget_get_style_context(color_w), &color);
|
||
cairo_set_source_rgb(cr, color.red, color.green, color.blue);
|
||
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
|
||
|
||
cairo_new_path(cr);
|
||
for (i = 0; i < teeth; i++) {
|
||
double a = -G_PI / 2.0 + (2.0 * G_PI * (double)i) / (double)teeth;
|
||
double a0 = a - tooth_half * 0.55;
|
||
double a1 = a - tooth_half * 0.28;
|
||
double a2 = a + tooth_half * 0.28;
|
||
double a3 = a + tooth_half * 0.55;
|
||
|
||
if (i == 0)
|
||
cairo_move_to(cr, cx + cos(a0) * r_inner, cy + sin(a0) * r_inner);
|
||
else
|
||
cairo_line_to(cr, cx + cos(a0) * r_inner, cy + sin(a0) * r_inner);
|
||
|
||
cairo_line_to(cr, cx + cos(a1) * r_outer, cy + sin(a1) * r_outer);
|
||
cairo_line_to(cr, cx + cos(a2) * r_outer, cy + sin(a2) * r_outer);
|
||
cairo_line_to(cr, cx + cos(a3) * r_inner, cy + sin(a3) * r_inner);
|
||
}
|
||
cairo_close_path(cr);
|
||
cairo_new_sub_path(cr);
|
||
cairo_arc(cr, cx, cy, r_hole, 0.0, 2.0 * G_PI);
|
||
cairo_fill(cr);
|
||
}
|
||
|
||
void draw_stats_icon(GtkDrawingArea *area, cairo_t *cr,
|
||
int width, int height, gpointer data) {
|
||
GtkWidget *color_w = data ? GTK_WIDGET(data)
|
||
: gtk_widget_get_parent(GTK_WIDGET(area));
|
||
GdkRGBA color;
|
||
const double bar_w = 3.2;
|
||
const double gap = 2.6;
|
||
const double total_w = 3.0 * bar_w + 2.0 * gap;
|
||
const double heights[3] = {0.42, 0.72, 1.0};
|
||
double base_x = (width - total_w) / 2.0;
|
||
double base_y = height * 0.82;
|
||
double max_h = height * 0.58;
|
||
int i;
|
||
|
||
(void)area;
|
||
gtk_style_context_get_color(gtk_widget_get_style_context(color_w), &color);
|
||
cairo_set_source_rgb(cr, color.red, color.green, color.blue);
|
||
cairo_set_line_width(cr, 1.5);
|
||
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
|
||
|
||
for (i = 0; i < 3; i++) {
|
||
double x = base_x + (double)i * (bar_w + gap);
|
||
double h = max_h * heights[i];
|
||
double y = base_y - h;
|
||
double r = 1.2;
|
||
|
||
cairo_new_sub_path(cr);
|
||
cairo_arc(cr, x + r, y + r, r, G_PI, 1.5 * G_PI);
|
||
cairo_arc(cr, x + bar_w - r, y + r, r, 1.5 * G_PI, 2.0 * G_PI);
|
||
cairo_line_to(cr, x + bar_w, base_y);
|
||
cairo_line_to(cr, x, base_y);
|
||
cairo_close_path(cr);
|
||
cairo_fill(cr);
|
||
}
|
||
}
|
||
|
||
GtkWidget *icon_area_new(GtkDrawingAreaDrawFunc fn,
|
||
double r, double g, double b, int px) {
|
||
Rgb *col = g_new(Rgb, 1);
|
||
GtkWidget *d = gtk_drawing_area_new();
|
||
|
||
col->r = r;
|
||
col->g = g;
|
||
col->b = b;
|
||
gtk_widget_set_size_request(d, px, px);
|
||
gtk_widget_set_halign(d, GTK_ALIGN_CENTER);
|
||
gtk_widget_set_valign(d, GTK_ALIGN_CENTER);
|
||
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(d), fn,
|
||
col, (GDestroyNotify)g_free);
|
||
return d;
|
||
}
|