mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
made the exercise 2 and 10 selection be able to just be clicked instead of just dragged
This commit is contained in:
parent
c106f5d0e2
commit
cc838ee5db
1 changed files with 163 additions and 2 deletions
165
sprechen.c
165
sprechen.c
|
|
@ -1434,11 +1434,13 @@ typedef struct {
|
||||||
GtkWidget *pool;
|
GtkWidget *pool;
|
||||||
GtkWidget *target;
|
GtkWidget *target;
|
||||||
GtkWidget *group;
|
GtkWidget *group;
|
||||||
|
GtkWidget *stage; /* transparent overlay layer for the fly sprite */
|
||||||
GtkWidget *trans; /* hidden meaning label under the group */
|
GtkWidget *trans; /* hidden meaning label under the group */
|
||||||
GtkWidget *chips[6];
|
GtkWidget *chips[6];
|
||||||
int placed[6];
|
int placed[6];
|
||||||
int placed_count;
|
int placed_count;
|
||||||
int n_words;
|
int n_words;
|
||||||
|
gboolean flying; /* a click-move animation is in progress */
|
||||||
} SentBuilder;
|
} SentBuilder;
|
||||||
|
|
||||||
static void sent_rebuild(SentBuilder *sb) {
|
static void sent_rebuild(SentBuilder *sb) {
|
||||||
|
|
@ -1555,6 +1557,148 @@ static gboolean asm_drop_to_pool(GtkDropTarget *target, const GValue *value,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Click-to-move with a fly ("glide") animation */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SentBuilder *sb;
|
||||||
|
GtkWidget *chip; /* real chip, kept invisible in its final slot */
|
||||||
|
GtkWidget *ghost; /* sprite flying from start to finish */
|
||||||
|
GtkWidget *stage;
|
||||||
|
double sx, sy; /* start position (stage coords) */
|
||||||
|
double tx, ty; /* target position (stage coords) */
|
||||||
|
guint wait; /* ticks waited for relayout */
|
||||||
|
gint64 t0; /* < 0 while still measuring the target */
|
||||||
|
} FlyCtx;
|
||||||
|
|
||||||
|
static void asm_finish_fly(FlyCtx *fc) {
|
||||||
|
gtk_widget_remove_css_class(fc->chip, "ghost-host");
|
||||||
|
gtk_fixed_remove(GTK_FIXED(fc->stage), fc->ghost);
|
||||||
|
fc->sb->flying = FALSE;
|
||||||
|
g_free(fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean asm_fly_tick(gpointer data) {
|
||||||
|
FlyCtx *fc = data;
|
||||||
|
gint64 now = g_get_monotonic_time() / 1000;
|
||||||
|
|
||||||
|
if (fc->t0 < 0) {
|
||||||
|
double nx = 0.0, ny = 0.0;
|
||||||
|
gboolean ok;
|
||||||
|
|
||||||
|
fc->wait++;
|
||||||
|
ok = gtk_widget_translate_coordinates(fc->chip, fc->stage,
|
||||||
|
0, 0, &nx, &ny);
|
||||||
|
if (fc->wait < 2)
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
|
||||||
|
if (ok && fc->wait < 14 &&
|
||||||
|
fabs(nx - fc->sx) + fabs(ny - fc->sy) < 0.5)
|
||||||
|
return G_SOURCE_CONTINUE; /* still waiting for relayout */
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
fc->tx = nx;
|
||||||
|
fc->ty = ny;
|
||||||
|
} else {
|
||||||
|
fc->tx = fc->sx;
|
||||||
|
fc->ty = fc->sy;
|
||||||
|
}
|
||||||
|
fc->t0 = now;
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
double t = (double)(now - fc->t0) / 240.0;
|
||||||
|
double e, x, y;
|
||||||
|
|
||||||
|
if (t > 1.0)
|
||||||
|
t = 1.0;
|
||||||
|
e = 1.0 - pow(1.0 - t, 3.0); /* ease-out cubic */
|
||||||
|
x = fc->sx + (fc->tx - fc->sx) * e;
|
||||||
|
y = fc->sy + (fc->ty - fc->sy) * e;
|
||||||
|
gtk_fixed_move(GTK_FIXED(fc->stage), fc->ghost, (int)x, (int)y);
|
||||||
|
|
||||||
|
if (t >= 1.0) {
|
||||||
|
asm_finish_fly(fc);
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void asm_move_by_click(GtkButton *button, gpointer data) {
|
||||||
|
ChipRef *ref = data;
|
||||||
|
SentBuilder *sb = ref->sb;
|
||||||
|
int idx = ref->idx;
|
||||||
|
int pos = -1;
|
||||||
|
double sx = 0.0, sy = 0.0;
|
||||||
|
GtkWidget *chip = GTK_WIDGET(button);
|
||||||
|
GtkWidget *ghost;
|
||||||
|
GtkWidget *child;
|
||||||
|
const char *txt = NULL;
|
||||||
|
FlyCtx *fc;
|
||||||
|
|
||||||
|
(void)button;
|
||||||
|
|
||||||
|
if (sb->flying)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < sb->placed_count; i++) {
|
||||||
|
if (sb->placed[i] == idx) {
|
||||||
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sb->stage &&
|
||||||
|
gtk_widget_translate_coordinates(chip, sb->stage, 0, 0, &sx, &sy)) {
|
||||||
|
if (pos >= 0) {
|
||||||
|
for (int i = pos; i < sb->placed_count - 1; i++)
|
||||||
|
sb->placed[i] = sb->placed[i + 1];
|
||||||
|
sb->placed_count--;
|
||||||
|
} else {
|
||||||
|
sb->placed[sb->placed_count++] = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
sent_rebuild(sb);
|
||||||
|
|
||||||
|
child = gtk_button_get_child(GTK_BUTTON(chip));
|
||||||
|
if (child && GTK_IS_LABEL(child))
|
||||||
|
txt = gtk_label_get_text(GTK_LABEL(child));
|
||||||
|
|
||||||
|
ghost = gtk_button_new_with_label(txt ? txt : "");
|
||||||
|
gtk_widget_add_css_class(ghost, "chip");
|
||||||
|
gtk_widget_set_sensitive(ghost, FALSE);
|
||||||
|
gtk_widget_set_can_focus(ghost, FALSE);
|
||||||
|
gtk_fixed_put(GTK_FIXED(sb->stage), ghost, (int)sx, (int)sy);
|
||||||
|
|
||||||
|
fc = g_new0(FlyCtx, 1);
|
||||||
|
fc->sb = sb;
|
||||||
|
fc->chip = chip;
|
||||||
|
fc->ghost = ghost;
|
||||||
|
fc->stage = sb->stage;
|
||||||
|
fc->sx = sx;
|
||||||
|
fc->sy = sy;
|
||||||
|
fc->t0 = -1;
|
||||||
|
sb->flying = TRUE;
|
||||||
|
|
||||||
|
gtk_widget_add_css_class(chip, "ghost-host");
|
||||||
|
|
||||||
|
g_timeout_add(16, asm_fly_tick, fc);
|
||||||
|
} else {
|
||||||
|
/* No usable animation layer yet – just move instantly. */
|
||||||
|
if (pos >= 0) {
|
||||||
|
for (int i = pos; i < sb->placed_count - 1; i++)
|
||||||
|
sb->placed[i] = sb->placed[i + 1];
|
||||||
|
sb->placed_count--;
|
||||||
|
} else {
|
||||||
|
sb->placed[sb->placed_count++] = idx;
|
||||||
|
}
|
||||||
|
sent_rebuild(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SentBuilder **sbs;
|
SentBuilder **sbs;
|
||||||
int n;
|
int n;
|
||||||
|
|
@ -1618,9 +1762,11 @@ static GtkWidget *build_assembly(const char *title, const char *subtitle,
|
||||||
for (int s = 0; s < n; s++) {
|
for (int s = 0; s < n; s++) {
|
||||||
const AssemblyItem *item = &items[s];
|
const AssemblyItem *item = &items[s];
|
||||||
SentBuilder *sb = g_new0(SentBuilder, 1);
|
SentBuilder *sb = g_new0(SentBuilder, 1);
|
||||||
|
GtkWidget *holder;
|
||||||
GtkWidget *group;
|
GtkWidget *group;
|
||||||
GtkWidget *target;
|
GtkWidget *target;
|
||||||
GtkWidget *pool;
|
GtkWidget *pool;
|
||||||
|
GtkWidget *stage;
|
||||||
|
|
||||||
sb->n_words = item->n;
|
sb->n_words = item->n;
|
||||||
ctx->sbs[s] = sb;
|
ctx->sbs[s] = sb;
|
||||||
|
|
@ -1632,9 +1778,13 @@ static GtkWidget *build_assembly(const char *title, const char *subtitle,
|
||||||
gtk_box_append(GTK_BOX(body), prompt);
|
gtk_box_append(GTK_BOX(body), prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
holder = gtk_overlay_new();
|
||||||
|
gtk_widget_set_hexpand(holder, TRUE);
|
||||||
|
|
||||||
group = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
|
group = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
|
||||||
gtk_widget_add_css_class(group, "sent-group");
|
gtk_widget_add_css_class(group, "sent-group");
|
||||||
sb->group = group;
|
sb->group = group;
|
||||||
|
gtk_overlay_set_child(GTK_OVERLAY(holder), group);
|
||||||
|
|
||||||
target = gtk_flow_box_new();
|
target = gtk_flow_box_new();
|
||||||
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(target), GTK_SELECTION_NONE);
|
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(target), GTK_SELECTION_NONE);
|
||||||
|
|
@ -1650,6 +1800,13 @@ static GtkWidget *build_assembly(const char *title, const char *subtitle,
|
||||||
sb->pool = pool;
|
sb->pool = pool;
|
||||||
gtk_box_append(GTK_BOX(group), pool);
|
gtk_box_append(GTK_BOX(group), pool);
|
||||||
|
|
||||||
|
stage = gtk_fixed_new();
|
||||||
|
gtk_widget_set_halign(stage, GTK_ALIGN_FILL);
|
||||||
|
gtk_widget_set_valign(stage, GTK_ALIGN_FILL);
|
||||||
|
gtk_widget_set_can_target(stage, FALSE);
|
||||||
|
gtk_overlay_add_overlay(GTK_OVERLAY(holder), stage);
|
||||||
|
sb->stage = stage;
|
||||||
|
|
||||||
{
|
{
|
||||||
GtkDropTarget *dt = gtk_drop_target_new(G_TYPE_INT, GDK_ACTION_COPY);
|
GtkDropTarget *dt = gtk_drop_target_new(G_TYPE_INT, GDK_ACTION_COPY);
|
||||||
GtkDropTarget *dp = gtk_drop_target_new(G_TYPE_INT, GDK_ACTION_COPY);
|
GtkDropTarget *dp = gtk_drop_target_new(G_TYPE_INT, GDK_ACTION_COPY);
|
||||||
|
|
@ -1671,6 +1828,7 @@ static GtkWidget *build_assembly(const char *title, const char *subtitle,
|
||||||
gtk_drag_source_set_actions(src, GDK_ACTION_COPY);
|
gtk_drag_source_set_actions(src, GDK_ACTION_COPY);
|
||||||
g_signal_connect(src, "prepare", G_CALLBACK(asm_drag_prepare), ref);
|
g_signal_connect(src, "prepare", G_CALLBACK(asm_drag_prepare), ref);
|
||||||
g_signal_connect(src, "drag-end", G_CALLBACK(asm_drag_end), ref);
|
g_signal_connect(src, "drag-end", G_CALLBACK(asm_drag_end), ref);
|
||||||
|
g_signal_connect(chip, "clicked", G_CALLBACK(asm_move_by_click), ref);
|
||||||
gtk_widget_add_controller(chip, GTK_EVENT_CONTROLLER(src));
|
gtk_widget_add_controller(chip, GTK_EVENT_CONTROLLER(src));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1683,7 +1841,7 @@ static GtkWidget *build_assembly(const char *title, const char *subtitle,
|
||||||
gtk_flow_box_insert(GTK_FLOW_BOX(pool), sb->chips[order[w]], -1);
|
gtk_flow_box_insert(GTK_FLOW_BOX(pool), sb->chips[order[w]], -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_box_append(GTK_BOX(body), group);
|
gtk_box_append(GTK_BOX(body), holder);
|
||||||
|
|
||||||
if (meanings[s])
|
if (meanings[s])
|
||||||
sb->trans = meaning_add(body, meanings[s]);
|
sb->trans = meaning_add(body, meanings[s]);
|
||||||
|
|
@ -1821,7 +1979,7 @@ static const AssemblyItem ex2_items[] = {
|
||||||
{NULL, {"Ich", "heiße", "Anna", "."}, 4},
|
{NULL, {"Ich", "heiße", "Anna", "."}, 4},
|
||||||
{NULL, {"Woher", "kommst", "du", "?"}, 4},
|
{NULL, {"Woher", "kommst", "du", "?"}, 4},
|
||||||
{NULL, {"Ich", "komme", "aus", "Tschechien", "."}, 5},
|
{NULL, {"Ich", "komme", "aus", "Tschechien", "."}, 5},
|
||||||
{NULL, {"Wie", "geht", "es", "dir", "?"}, 4},
|
{NULL, {"Wie", "geht", "es", "dir", "?"}, 5},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *ex2_meaning[] = {
|
static const char *ex2_meaning[] = {
|
||||||
|
|
@ -3142,6 +3300,9 @@ static void activate(GtkApplication *app, gpointer user_data) {
|
||||||
" background-color: #45475a;"
|
" background-color: #45475a;"
|
||||||
" border-color: #cba6f7;"
|
" border-color: #cba6f7;"
|
||||||
"}"
|
"}"
|
||||||
|
".chip.ghost-host {"
|
||||||
|
" opacity: 0;"
|
||||||
|
"}"
|
||||||
".group-btn {"
|
".group-btn {"
|
||||||
" background-color: #2b2d40;"
|
" background-color: #2b2d40;"
|
||||||
" color: #cdd6f4;"
|
" color: #cdd6f4;"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue