From 59284281f6440dab5e8d0be4b67aac2970d05652 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sun, 17 Mar 2019 17:46:42 +0100 Subject: Initial slide clicky stuff --- libstorycode/gtk/gtknarrativeview.h | 10 - libstorycode/gtk/gtkslideview.c | 511 +++++++++++++++++++++++++++++++++++- libstorycode/gtk/gtkslideview.h | 4 +- libstorycode/slide.c | 15 +- libstorycode/slide_priv.h | 5 + libstorycode/stylesheet.c | 9 + libstorycode/stylesheet.h | 4 + meson.build | 3 +- 8 files changed, 544 insertions(+), 17 deletions(-) diff --git a/libstorycode/gtk/gtknarrativeview.h b/libstorycode/gtk/gtknarrativeview.h index ec27a06..fdcfed6 100644 --- a/libstorycode/gtk/gtknarrativeview.h +++ b/libstorycode/gtk/gtknarrativeview.h @@ -53,16 +53,6 @@ #define GTK_NARRATIVE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ GTK_TYPE_NARRATIVE_VIEW, GtkNarrativeViewClass)) -enum corner -{ - CORNER_NONE, - CORNER_TL, - CORNER_TR, - CORNER_BL, - CORNER_BR -}; - - enum drag_status { DRAG_STATUS_NONE, diff --git a/libstorycode/gtk/gtkslideview.c b/libstorycode/gtk/gtkslideview.c index bf32cdb..d1163a5 100644 --- a/libstorycode/gtk/gtkslideview.c +++ b/libstorycode/gtk/gtkslideview.c @@ -37,6 +37,7 @@ #include #include +#include //#include "slide_window.h" #include "gtkslideview.h" @@ -117,6 +118,178 @@ static gint destroy_sig(GtkWidget *window, GtkSlideView *e) } +static void draw_editing_box(cairo_t *cr, struct slide_item *item, + double xmin, double ymin, double width, double height) +{ + const double dash[] = {2.0, 2.0}; + double ptot_w, ptot_h; + double pad_l, pad_r, pad_t, pad_b; + + pad_l = 0.0; pad_r = 0.0; pad_t = 0.0; pad_b = 0.0; /* FIXME */ + + cairo_new_path(cr); + cairo_rectangle(cr, xmin, ymin, width, height); + cairo_set_source_rgb(cr, 0.0, 0.69, 1.0); + cairo_set_line_width(cr, 0.5); + cairo_stroke(cr); + + cairo_new_path(cr); + ptot_w = pad_l + pad_r; + ptot_h = pad_t + pad_b; + cairo_rectangle(cr, xmin+pad_l, ymin+pad_t, width-ptot_w, height-ptot_h); + cairo_set_dash(cr, dash, 2, 0.0); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_set_line_width(cr, 0.1); + cairo_stroke(cr); + + cairo_set_dash(cr, NULL, 0, 0.0); +} + + +static void draw_resize_handle(cairo_t *cr, double x, double y) +{ + cairo_new_path(cr); + cairo_rectangle(cr, x, y, 20.0, 20.0); + cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 0.5); + cairo_fill(cr); +} + + +static size_t pos_trail_to_offset(struct slide_item *item, int para, + size_t offs, int trail) +{ + glong char_offs; + char *ptr; + + char_offs = g_utf8_pointer_to_offset(item->paragraphs[para], + item->paragraphs[para]+offs); + char_offs += trail; + ptr = g_utf8_offset_to_pointer(item->paragraphs[para], char_offs); + return ptr - item->paragraphs[para]; +} + + +static double para_top(struct slide_item *item, int pnum) +{ + int i; + double py = 0.0; + for ( i=0; ilayouts[i], NULL, &rect); + py += pango_units_to_double(rect.height); + } + return py; +} + + +static int get_cursor_pos(struct slide_item *item, struct slide_pos cpos, + double *x, double *y, double *h) +{ + size_t offs; + PangoRectangle rect; + + if ( item->layouts[cpos.para] == NULL ) { + fprintf(stderr, "get_cursor_pos: No layout\n"); + return 1; + } + + offs = pos_trail_to_offset(item, cpos.para, cpos.pos, cpos.trail); + pango_layout_get_cursor_pos(item->layouts[cpos.para], offs, &rect, NULL); + *x = pango_units_to_double(rect.x); + *y = pango_units_to_double(rect.y) + para_top(item, cpos.para); + *h = pango_units_to_double(rect.height); + return 0; +} + + +static void draw_caret(cairo_t *cr, struct slide_item *item, struct slide_pos cpos, + double frx, double fry) +{ + double cx, clow, chigh, h; + const double t = 1.8; + + if ( get_cursor_pos(item, cpos, &cx, &clow, &h) ) return; + + cx += frx; + clow += fry; + chigh = clow + h; + + cairo_move_to(cr, cx, clow); + cairo_line_to(cr, cx, chigh); + + cairo_move_to(cr, cx-t, clow-t); + cairo_line_to(cr, cx, clow); + cairo_move_to(cr, cx+t, clow-t); + cairo_line_to(cr, cx, clow); + + cairo_move_to(cr, cx-t, chigh+t); + cairo_line_to(cr, cx, chigh); + cairo_move_to(cr, cx+t, chigh+t); + cairo_line_to(cr, cx, chigh); + + cairo_set_source_rgb(cr, 0.86, 0.0, 0.0); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); +} + + +static void draw_overlay(cairo_t *cr, GtkSlideView *e) +{ + if ( e->cursor_frame != NULL ) { + + double x, y, w, h; + double slide_w, slide_h; + Stylesheet *stylesheet; + + stylesheet = presentation_get_stylesheet(e->p); + slide_get_logical_size(e->slide, stylesheet, &slide_w, &slide_h); + slide_item_get_geom(e->cursor_frame, stylesheet, &x, &y, &w, &h, + slide_w, slide_h); + draw_editing_box(cr, e->cursor_frame, x, y, w, h); + + if ( e->cursor_frame->resizable ) { + /* Draw resize handles */ + draw_resize_handle(cr, x, y+h-20.0); + draw_resize_handle(cr, x+w-20.0, y); + draw_resize_handle(cr, x, y); + draw_resize_handle(cr, x+w-20.0, y+h-20.0); + } + + if ( e->cursor_frame->type != SLIDE_ITEM_IMAGE ) { + draw_caret(cr, e->cursor_frame, e->cpos, x, y); + } + + } + + if ( e->drag_status == DRAG_STATUS_DRAGGING ) { + + if ( (e->drag_reason == DRAG_REASON_CREATE) + || (e->drag_reason == DRAG_REASON_IMPORT) ) + { + cairo_new_path(cr); + cairo_rectangle(cr, e->start_corner_x, e->start_corner_y, + e->drag_corner_x - e->start_corner_x, + e->drag_corner_y - e->start_corner_y); + cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); + cairo_set_line_width(cr, 0.5); + cairo_stroke(cr); + } + + if ( (e->drag_reason == DRAG_REASON_RESIZE) + || (e->drag_reason == DRAG_REASON_MOVE) ) + { + cairo_new_path(cr); + cairo_rectangle(cr, e->box_x, e->box_y, + e->box_width, e->box_height); + cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); + cairo_set_line_width(cr, 0.5); + cairo_stroke(cr); + } + + } +} + + static gboolean draw_sig(GtkWidget *da, cairo_t *cr, GtkSlideView *e) { PangoContext *pc; @@ -145,8 +318,340 @@ static gboolean draw_sig(GtkWidget *da, cairo_t *cr, GtkSlideView *e) g_object_unref(pc); /* Editing overlay */ - //draw_overlay(cr, e); + draw_overlay(cr, e); + + return FALSE; +} + + +static int within_frame(struct slide_item *item, Stylesheet *ss, + double slide_w, double slide_h, + double xp, double yp) +{ + double x, y, w, h; + + slide_item_get_geom(item, ss, &x, &y, &w, &h, slide_w, slide_h); + + if ( xp < x ) return 0; + if ( yp < y ) return 0; + if ( xp > x + w ) return 0; + if ( yp > y + h ) return 0; + return 1; +} + + +static struct slide_item *find_frame_at_position(Slide *s, Stylesheet *ss, + double slide_w, double slide_h, + double x, double y) +{ + int i; + for ( i=0; in_items; i++ ) { + if ( within_frame(&s->items[i], ss, slide_w, slide_h, x, y) ) { + return &s->items[i]; + } + } + return NULL; +} + + +static enum drag_corner which_corner(double xp, double yp, + double frx, double fry, double frw, double frh) +{ + double x, y; /* Relative to object position */ + + x = xp - frx; + y = yp - fry; + + if ( x < 0.0 ) return CORNER_NONE; + if ( y < 0.0 ) return CORNER_NONE; + if ( x > frw ) return CORNER_NONE; + if ( y > frh ) return CORNER_NONE; + + /* Top left? */ + if ( (x<20.0) && (y<20.0) ) return CORNER_TL; + if ( (x>frw-20.0) && (y<20.0) ) return CORNER_TR; + if ( (x<20.0) && (y>frh-20.0) ) return CORNER_BL; + if ( (x>frw-20.0) && (y>frh-20.0) ) return CORNER_BR; + + return CORNER_NONE; +} + + +static void calculate_box_size(double frx, double fry, double frw, double frh, + GtkSlideView *e, int preserve_aspect, + double x, double y) +{ + double ddx, ddy, dlen, mult; + double vx, vy, dbx, dby; + + ddx = x - e->start_corner_x; + ddy = y - e->start_corner_y; + + if ( !preserve_aspect ) { + + switch ( e->drag_corner ) { + + case CORNER_BR : + e->box_x = frx; + e->box_y = fry; + e->box_width = frw + ddx; + e->box_height = frh + ddy; + break; + + case CORNER_BL : + e->box_x = frx + ddx; + e->box_y = fry; + e->box_width = frw - ddx; + e->box_height = frh + ddy; + break; + + case CORNER_TL : + e->box_x = frx + ddx; + e->box_y = fry + ddy; + e->box_width = frw - ddx; + e->box_height = frh - ddy; + break; + + case CORNER_TR : + e->box_x = frx; + e->box_y = fry + ddy; + e->box_width = frw + ddx; + e->box_height = frh - ddy; + break; + + case CORNER_NONE : + break; + + } + return; + + + } + + switch ( e->drag_corner ) { + + case CORNER_BR : + vx = frw; + vy = frh; + break; + + case CORNER_BL : + vx = -frw; + vy = frh; + break; + + case CORNER_TL : + vx = -frw; + vy = -frh; + break; + + case CORNER_TR : + vx = frw; + vy = -frh; + break; + + case CORNER_NONE : + default: + vx = 0.0; + vy = 0.0; + break; + + } + + dlen = (ddx*vx + ddy*vy) / e->diagonal_length; + mult = (dlen+e->diagonal_length) / e->diagonal_length; + + e->box_width = frw * mult; + e->box_height = frh * mult; + dbx = e->box_width - frw; + dby = e->box_height - frh; + + if ( e->box_width < 40.0 ) { + mult = 40.0 / frw; + } + if ( e->box_height < 40.0 ) { + mult = 40.0 / frh; + } + e->box_width = frw * mult; + e->box_height = frh * mult; + dbx = e->box_width - frw; + dby = e->box_height - frh; + + switch ( e->drag_corner ) { + + case CORNER_BR : + e->box_x = frx; + e->box_y = fry; + break; + + case CORNER_BL : + e->box_x = frx - dbx; + e->box_y = fry; + break; + + case CORNER_TL : + e->box_x = frx - dbx; + e->box_y = fry - dby; + break; + + case CORNER_TR : + e->box_x = frx; + e->box_y = fry - dby; + break; + + case CORNER_NONE : + break; + + } +} + + +static int find_cursor(struct slide_item *item, double x, double y, struct slide_pos *pos) +{ +#if 0 + double cur_y; + int i = 0; + + cur_y = item->space_t; + + do { + cur_y += n->items[i++].h; + } while ( (cur_y < y) && (in_items) ); + + pos->para = i-1; + item = &n->items[pos->para]; + if ( item->type == NARRATIVE_ITEM_SLIDE ) { + pos->pos = 0; + return 0; + } + + pango_layout_xy_to_index(item->layout, + pango_units_from_double(x - n->space_l - item->space_l), + pango_units_from_double(y - n->space_t - para_top(n, pos->para)), + &pos->pos, &pos->trail); +#endif + return 0; +} + + +static void unset_selection(GtkSlideView *e) +{ + int a, b; + + a = e->sel_start.para; + b = e->sel_end.para; + if ( a > b ) { + a = e->sel_end.para; + b = e->sel_start.para; + } + //rewrap_paragraph_range(e->cursor_frame, a, b, e->sel_start, e->sel_end, 0); +} + +static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, + GtkSlideView *e) +{ + enum drag_corner c; + gdouble x, y; + Stylesheet *stylesheet; + struct slide_item *clicked; + int shift; + double slide_w, slide_h; + double frx, fry, frw, frh; + + stylesheet = presentation_get_stylesheet(e->p); + slide_get_logical_size(e->slide, stylesheet, &slide_w, &slide_h); + + x = event->x - e->border_offs_x + e->h_scroll_pos; + y = event->y - e->border_offs_y + e->v_scroll_pos; + x /= e->view_scale; + y /= e->view_scale; + shift = event->state & GDK_SHIFT_MASK; + + clicked = find_frame_at_position(e->slide, stylesheet, + slide_w, slide_h, x, y); + + if ( clicked != NULL ) { + slide_item_get_geom(clicked, stylesheet, &frx, &fry, &frw, &frh, + slide_w, slide_h); + } + + /* Clicked within the currently selected frame + * -> resize, move or select text */ + if ( (e->cursor_frame != NULL) && (clicked == e->cursor_frame) ) { + + /* Within the resizing region? */ + c = which_corner(x, y, frx, fry, frw, frh); + if ( (c != CORNER_NONE) && e->cursor_frame->resizable && shift ) { + + e->drag_reason = DRAG_REASON_RESIZE; + e->drag_corner = c; + + e->start_corner_x = x; + e->start_corner_y = y; + e->diagonal_length = pow(frw, 2.0); + e->diagonal_length += pow(frh, 2.0); + e->diagonal_length = sqrt(e->diagonal_length); + + calculate_box_size(frx, fry, frw, frh, e, + e->cursor_frame->type == SLIDE_ITEM_IMAGE ? 1 : 0, + x, y); + + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_RESIZE; + + } else { + + /* Position cursor and prepare for possible drag */ + e->cursor_frame = clicked; + find_cursor(clicked, x-frx, y-fry, &e->cpos); + + e->start_corner_x = x; + e->start_corner_y = y; + + if ( clicked->resizable && shift ) { + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_MOVE; + } else { + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_TEXTSEL; + unset_selection(e); + find_cursor(clicked, x-frx, y-fry, &e->sel_start); + } + + } + + } else if ( clicked == NULL ) { + + /* Clicked no object. Deselect old object. + * If shift held, set up for creating a new one. */ + e->cursor_frame = NULL; + unset_selection(e); + + if ( shift ) { + e->start_corner_x = x; + e->start_corner_y = y; + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_CREATE; + } else { + e->drag_status = DRAG_STATUS_NONE; + e->drag_reason = DRAG_REASON_NONE; + } + + } else { + + /* Clicked an existing frame, no immediate dragging */ + e->drag_status = DRAG_STATUS_COULD_DRAG; + e->drag_reason = DRAG_REASON_TEXTSEL; + unset_selection(e); + find_cursor(clicked, x-frx, y-fry, &e->sel_start); + find_cursor(clicked, x-frx, y-fry, &e->sel_end); + e->cursor_frame = clicked; + find_cursor(clicked, x-frx, y-fry, &e->cpos); + + } + gtk_widget_grab_focus(GTK_WIDGET(da)); + redraw(e); return FALSE; } @@ -220,8 +725,8 @@ GtkWidget *gtk_slide_view_new(Presentation *p, Slide *slide) G_CALLBACK(destroy_sig), sv); g_signal_connect(G_OBJECT(sv), "realize", G_CALLBACK(realise_sig), sv); - //g_signal_connect(G_OBJECT(sv), "button-press-event", - // G_CALLBACK(button_press_sig), sv); + g_signal_connect(G_OBJECT(sv), "button-press-event", + G_CALLBACK(button_press_sig), sv); //g_signal_connect(G_OBJECT(sv), "button-release-event", // G_CALLBACK(button_release_sig), sv); //g_signal_connect(G_OBJECT(sv), "motion-notify-event", diff --git a/libstorycode/gtk/gtkslideview.h b/libstorycode/gtk/gtkslideview.h index 3ad77d9..610463d 100644 --- a/libstorycode/gtk/gtkslideview.h +++ b/libstorycode/gtk/gtkslideview.h @@ -67,7 +67,7 @@ enum drag_reason }; -enum corner +enum drag_corner { CORNER_NONE, CORNER_TL, @@ -127,7 +127,7 @@ struct _gtkslideview double box_height; enum drag_reason drag_reason; enum drag_status drag_status; - enum corner drag_corner; + enum drag_corner drag_corner; struct slide_pos sel_start; /* Where the user dragged from */ struct slide_pos sel_end; diff --git a/libstorycode/slide.c b/libstorycode/slide.c index eaf5f4e..f24e713 100644 --- a/libstorycode/slide.c +++ b/libstorycode/slide.c @@ -55,10 +55,16 @@ void slide_free(Slide *s) static struct slide_item *add_item(Slide *s) { struct slide_item *new_items; + struct slide_item *item; + new_items = realloc(s->items, (s->n_items+1)*sizeof(struct slide_item)); if ( new_items == NULL ) return NULL; s->items = new_items; - return &s->items[s->n_items++]; + item = &s->items[s->n_items++]; + + item->layouts = NULL; + + return item; } @@ -72,6 +78,7 @@ int slide_add_image(Slide *s, char *filename, struct frame_geom geom) item->type = SLIDE_ITEM_IMAGE; item->geom = geom; item->filename = filename; + item->resizable = 1; return 0; } @@ -103,6 +110,12 @@ int add_text_item(Slide *s, char **text, int n_text, struct frame_geom geom, item->geom = geom; item->align = alignment; + if ( slide_item == SLIDE_ITEM_TEXT ) { + item->resizable = 1; + } else { + item->resizable = 0; + } + return 0; } diff --git a/libstorycode/slide_priv.h b/libstorycode/slide_priv.h index 0d39d8a..41dd4cd 100644 --- a/libstorycode/slide_priv.h +++ b/libstorycode/slide_priv.h @@ -56,6 +56,7 @@ struct slide_item /* For TEXT and IMAGE */ struct frame_geom geom; + int resizable; }; @@ -67,4 +68,8 @@ struct _slide struct slide_item *items; }; +extern void slide_item_get_geom(struct slide_item *item, Stylesheet *ss, + double *x, double *y, double *w, double *h, + double slide_w, double slide_h); + #endif /* SLIDE_PRIV_H */ diff --git a/libstorycode/stylesheet.c b/libstorycode/stylesheet.c index d287836..742c880 100644 --- a/libstorycode/stylesheet.c +++ b/libstorycode/stylesheet.c @@ -251,6 +251,15 @@ int stylesheet_set_alignment(Stylesheet *s, enum style_element el, enum alignmen } +int stylesheet_get_geometry(Stylesheet *s, enum style_element el, struct frame_geom *geom) +{ + struct style *sty = get_style(s, el); + if ( sty == NULL ) return 1; + *geom = sty->geom; + return 0; +} + + const char *stylesheet_get_font(Stylesheet *s, enum style_element el, double *fgcol, enum alignment *alignment) { diff --git a/libstorycode/stylesheet.h b/libstorycode/stylesheet.h index 811b901..26b1fe5 100644 --- a/libstorycode/stylesheet.h +++ b/libstorycode/stylesheet.h @@ -76,8 +76,10 @@ enum style_element STYEL_NARRATIVE_BP, STYEL_SLIDE, STYEL_SLIDE_TEXT, + STYEL_SLIDE_IMAGE, STYEL_SLIDE_PRESTITLE, STYEL_SLIDE_SLIDETITLE, + STYEL_SLIDE_FOOTER, }; @@ -96,6 +98,8 @@ extern int stylesheet_set_fgcol(Stylesheet *s, enum style_element el, double rgb extern int stylesheet_set_background(Stylesheet *s, enum style_element el, enum gradient grad, double bgcol[4], double bgcol2[4]); +extern int stylesheet_get_geometry(Stylesheet *s, enum style_element el, + struct frame_geom *geom); extern const char *stylesheet_get_font(Stylesheet *s, enum style_element el, double *fgcol, enum alignment *alignment); extern int stylesheet_get_background(Stylesheet *s, enum style_element el, diff --git a/meson.build b/meson.build index c7747a9..c33cdc0 100644 --- a/meson.build +++ b/meson.build @@ -71,6 +71,7 @@ libstorycode = library('storycode', 'libstorycode/slide_render_cairo.c', 'libstorycode/narrative_render_cairo.c', 'libstorycode/imagestore.c', + 'libstorycode/slide_priv.c', storycode_lex_ch, storycode_parse_ch, ], @@ -90,7 +91,7 @@ libgtkstorycode = library('gtkstorycode', ['libstorycode/gtk/gtknarrativeview.c', 'libstorycode/gtk/gtkslideview.c'], include_directories : libgtkstorycode_includes, - dependencies : [gtk_dep, libstorycode_dep], + dependencies : [gtk_dep, libstorycode_dep, mdep], install : true) libgtkstorycode_dep = declare_dependency(include_directories : libgtkstorycode_includes, -- cgit v1.2.3