|
@@ -1,7 +1,506 @@
|
|
|
|
|
|
+#include "monolog.h"
|
|
|
+#include "zori.h"
|
|
|
#include "zori_longtext.h"
|
|
|
|
|
|
|
|
|
+#define ZORI_LONGTEXT_LINE_POS_MAX 99999
|
|
|
+
|
|
|
+
|
|
|
+struct zori_longtext * zori_widget_to_longtext(struct zori_widget * widget) {
|
|
|
+ return ZORI_CONTAINER_OF(widget, struct zori_longtext, widget);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+struct zori_longtext *
|
|
|
+zori_longtext_set(struct zori_longtext * longtext, const zori_string * text) {
|
|
|
+ if (longtext) {
|
|
|
+ if (longtext->text) {
|
|
|
+ ustr_free(longtext->text);
|
|
|
+ longtext->text = NULL;
|
|
|
+ }
|
|
|
+ if (text) {
|
|
|
+ longtext->text = ustr_dup(text);
|
|
|
+ if (!longtext->text) {
|
|
|
+ LOG_ERROR("Out of memory in longtext setup.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return longtext;
|
|
|
+}
|
|
|
+
|
|
|
+struct zori_longtext *
|
|
|
+zori_longtext_set_cstr(struct zori_longtext * longtext, const char * cstr) {
|
|
|
+ const USTR * ustr;
|
|
|
+ USTR_INFO info;
|
|
|
+ ustr = ustr_refcstr(&info, cstr);
|
|
|
+ return zori_longtext_set(longtext, ustr);
|
|
|
+}
|
|
|
+
|
|
|
+struct zori_longtext *
|
|
|
+zori_longtext_init_cstr(struct zori_longtext * longtext, const char * cstr) {
|
|
|
+ longtext->text = NULL;
|
|
|
+ return zori_longtext_set_cstr(longtext, cstr);
|
|
|
+}
|
|
|
+
|
|
|
+void zori_longtext_done(struct zori_longtext * longtext) {
|
|
|
+ zori_longtext_set(longtext, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int zori_longtext_page(struct zori_longtext * longtext) {
|
|
|
+ if (!longtext) return -2;
|
|
|
+ return longtext->line_start / longtext->page_lines;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int zori_longtext_last_page(struct zori_longtext * longtext) {
|
|
|
+ if (!longtext) return -2;
|
|
|
+ return longtext->line_max / longtext->page_lines;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_longtext_page_(struct zori_longtext * longtext, int page) {
|
|
|
+ if (!longtext) return -2;
|
|
|
+ if (page < 0) return -3;
|
|
|
+ int line = page * longtext->page_lines;
|
|
|
+
|
|
|
+ if (line >= longtext->line_max) {
|
|
|
+ return -5;
|
|
|
+ }
|
|
|
+
|
|
|
+ longtext->paused = false;
|
|
|
+ longtext->line_start = line;
|
|
|
+
|
|
|
+
|
|
|
+ if (longtext->delay < 0) {
|
|
|
+ longtext->line_stop = longtext->line_start + longtext->page_lines;
|
|
|
+ longtext->line_pos = ZORI_LONGTEXT_LINE_POS_MAX;
|
|
|
+ } else {
|
|
|
+ longtext->line_stop = longtext->line_start + 1;
|
|
|
+ longtext->line_pos = 0;
|
|
|
+ }
|
|
|
+ return page;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * false if not (more text to display) */
|
|
|
+int zori_longtext_at_end(struct zori_longtext * longtext) {
|
|
|
+ if (!longtext) return FALSE;
|
|
|
+ return ((longtext->line_stop >= longtext->line_max) &&
|
|
|
+ (longtext->line_pos >= ZORI_LONGTEXT_LINE_POS_MAX));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * updating scrolled/partial text for Longtexts.
|
|
|
+ */
|
|
|
+static bool
|
|
|
+zori_longtext_update_custom_partial_text(int line_num, const char *line, int size, void * extra) {
|
|
|
+ struct zori_longtext * longtext = extra;
|
|
|
+ longtext->line_max = line_num;
|
|
|
+
|
|
|
+
|
|
|
+ if (line_num < longtext->line_start) return true;
|
|
|
+
|
|
|
+ if (line_num >= longtext->line_stop) return true;
|
|
|
+
|
|
|
+ if (line_num == (longtext->line_stop - 1)) {
|
|
|
+
|
|
|
+ if (!longtext->paused) {
|
|
|
+ longtext->line_pos++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (longtext->line_pos >= size) {
|
|
|
+
|
|
|
+ if ((longtext->line_stop - longtext->line_start) >= longtext->page_lines) {
|
|
|
+ longtext->paused = true;
|
|
|
+ } else {
|
|
|
+ longtext->line_stop++;
|
|
|
+ longtext->line_pos = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ (void) line;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void zori_longtext_update_longtext(struct zori_longtext * longtext, double dt) {
|
|
|
+ float width = longtext->widget.inner.size.x;
|
|
|
+ zori_text * text = longtext->text;
|
|
|
+
|
|
|
+ if (!text) {
|
|
|
+ LOG_WARNING("Attempted to update a NULL longtext.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ longtext->delay_total += dt;
|
|
|
+ if (longtext->delay_total < longtext->delay) return;
|
|
|
+ longtext->delay_total = 0;
|
|
|
+
|
|
|
+ longtext->line_max = 0;
|
|
|
+ al_do_multiline_text(zori_widget_font(&longtext->widget), width,
|
|
|
+ (const char *) text, zori_longtext_update_custom_partial_text, longtext);
|
|
|
+ longtext->line_max++;
|
|
|
+
|
|
|
+
|
|
|
+ if (longtext->line_stop > longtext->line_max) {
|
|
|
+ longtext->paused = true;
|
|
|
+ longtext->line_pos = ZORI_LONGTEXT_LINE_POS_MAX;
|
|
|
+ longtext->line_stop = longtext->line_max;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * false if not (more text to display) */
|
|
|
+int zori_longtext_longtext_at_end(struct zori_longtext * longtext) {
|
|
|
+ return ((longtext->line_stop >= longtext->line_max) &&
|
|
|
+ (longtext->line_pos >= ZORI_LONGTEXT_LINE_POS_MAX));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * margin into consideration. */
|
|
|
+static BeVec
|
|
|
+zori_longtext_calculate_text_position(struct zori_longtext * longtext) {
|
|
|
+ BeVec result;
|
|
|
+ int flags = longtext->widget.style.text.font_flags;
|
|
|
+ result.x = longtext->widget.inner.at.x;
|
|
|
+ result.y = longtext->widget.inner.at.y;
|
|
|
+
|
|
|
+ if (flags & ALLEGRO_ALIGN_CENTER) {
|
|
|
+ result.x += longtext->widget.inner.size.x / 2;
|
|
|
+ } else if (flags & ALLEGRO_ALIGN_RIGHT) {
|
|
|
+ result.x += longtext->widget.inner.size.x;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+void zori_longtext_draw_text(struct zori_longtext * longtext) {
|
|
|
+ zori_font * font;
|
|
|
+ int flags;
|
|
|
+ BeVec pos = zori_longtext_calculate_text_position(longtext);
|
|
|
+ font = zori_widget_font(&longtext->widget);
|
|
|
+ flags = longtext->widget.style.text.font_flags | ALLEGRO_ALIGN_INTEGER;
|
|
|
+ zori_color shadow = longtext->widget.style.back.color;
|
|
|
+ zori_color fore = longtext->widget.style.text.color;
|
|
|
+
|
|
|
+
|
|
|
+ and once normally with foreground color. */
|
|
|
+ al_draw_ustr(font, shadow, pos.x + 1, pos.y + 1,
|
|
|
+ flags, longtext->text);
|
|
|
+ al_draw_ustr(font, fore, pos.x, pos.y,
|
|
|
+ flags, longtext->text);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * for zori_longtext_draw_partial_text.
|
|
|
+ */
|
|
|
+static bool zori_longtext_draw_custom_partial_text(int line_num, const char *line,
|
|
|
+ int size, void *extra) {
|
|
|
+ float x, y;
|
|
|
+ struct zori_longtext * longtext = extra;
|
|
|
+ ALLEGRO_USTR_INFO info;
|
|
|
+ int real_size, flags;
|
|
|
+ double width;
|
|
|
+ ALLEGRO_FONT * font;
|
|
|
+ BeVec pos;
|
|
|
+
|
|
|
+ font = zori_widget_font(&longtext->widget);
|
|
|
+ flags = longtext->widget.style.text.font_flags | ALLEGRO_ALIGN_INTEGER;
|
|
|
+ pos = zori_longtext_calculate_text_position(longtext);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (line_num < longtext->line_start) return true;
|
|
|
+
|
|
|
+ if (line_num >= longtext->line_stop) return false;
|
|
|
+ real_size = size;
|
|
|
+
|
|
|
+ x = pos.x;
|
|
|
+ y = pos.y + (longtext->line_height * (line_num - longtext->line_start));
|
|
|
+
|
|
|
+
|
|
|
+ if (line_num == (longtext->line_stop - 1)) {
|
|
|
+ real_size = (longtext->line_pos < size) ? longtext->line_pos : size;
|
|
|
+
|
|
|
+
|
|
|
+ * better... Maybe a bitmap member or so? */
|
|
|
+ if (longtext->paused) {
|
|
|
+ float x1, y1, x2, y2, x3, y3;
|
|
|
+ y2 = y + 8;
|
|
|
+ x2 = x + longtext->widget.inner.size.x;
|
|
|
+ x1 = x2 - 8;
|
|
|
+ y1 = y2;
|
|
|
+ x3 = x2 - 4;
|
|
|
+ y3 = y2 + 8;
|
|
|
+ al_draw_filled_triangle(x1, y1, x2, y2, x3, y3, longtext->widget.style.text.color);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ zori_color shadow = longtext->widget.style.back.color;
|
|
|
+ zori_color fore = longtext->widget.style.text.color;
|
|
|
+
|
|
|
+ al_draw_ustr(font, shadow,
|
|
|
+ x + 1, y + 1, flags, al_ref_buffer(&info, line, real_size));
|
|
|
+ al_draw_ustr(font, fore,
|
|
|
+ x, y, flags, al_ref_buffer(&info, line, real_size));
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * line_stop. Draws scrolling text from a prefilled struct. */
|
|
|
+void zori_longtext_draw_partial_text(struct zori_longtext * longtext) {
|
|
|
+ float width = longtext->widget.inner.size.x;
|
|
|
+ zori_font * font = zori_widget_font(&longtext->widget);
|
|
|
+
|
|
|
+ if (!longtext->text) {
|
|
|
+ LOG_WARNING("Trying to draw a NULL longtext.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const char * text = ustr_c(longtext->text);
|
|
|
+
|
|
|
+
|
|
|
+ if (longtext->line_height < 1.0) {
|
|
|
+ longtext->line_height = al_get_font_line_height(font);
|
|
|
+ }
|
|
|
+
|
|
|
+ al_do_multiline_text(font, width,
|
|
|
+ (const char *) text, zori_longtext_draw_custom_partial_text, longtext);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void zori_draw_longtext(struct zori_longtext * longtext) {
|
|
|
+ zori_longtext_draw_partial_text(longtext);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+zori_id zori_set_line_stop(zori_id index, int stop) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ longtext->line_stop = stop;
|
|
|
+ return index;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+zori_id zori_line_start_(zori_id index, int start) {
|
|
|
+struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ longtext->line_start = start;
|
|
|
+ return index;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_delay_(zori_id index, double delay) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ longtext->delay = delay;
|
|
|
+ return index;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int zori_line_stop(int index) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ return longtext->line_stop;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_line_start(int index) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ return longtext->line_start;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double zori_delay(int index) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ return longtext->delay;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int zori_page_lines_(zori_id index, int lines) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ longtext->page_lines = lines;
|
|
|
+ return index;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_page_lines(int index) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ return longtext->page_lines;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_paused_(zori_id index, int paused) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ longtext->paused = paused;
|
|
|
+ return index;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_paused(zori_id index) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return 0;
|
|
|
+ return longtext->paused;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_page(zori_id index) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ return zori_longtext_page(longtext);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_last_page(zori_id index) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ return zori_longtext_last_page(longtext);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_at_end(zori_id index) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ if (!longtext) return ZORI_ID_EINVAL;
|
|
|
+ return zori_longtext_at_end(longtext);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_page_(zori_id index, int page) {
|
|
|
+ struct zori_widget * widget = zori_get_widget(index);
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ return zori_longtext_page_(longtext, page);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_next_page(zori_id index) {
|
|
|
+ int page = zori_page(index);
|
|
|
+ if (page < 0) return page;
|
|
|
+ return zori_page_(index, page + 1);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_previous_page(int index) {
|
|
|
+ int page = zori_page(index);
|
|
|
+ if (page < 0) return page;
|
|
|
+ return zori_page_(index, page - 1);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int zori_longtext_on_mouse_axes(union zori_event * event) {
|
|
|
+ struct zori_widget * widget = event->any.widget;
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ float x = event->sys.ev->mouse.x;
|
|
|
+ float y = event->sys.ev->mouse.y;
|
|
|
+ if (zori_xy_inside_widget_p(widget, x, y)) {
|
|
|
+ zori_widget_hover_(widget, TRUE);
|
|
|
+ } else {
|
|
|
+ zori_widget_hover_(widget, FALSE);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ZORI_HANDLE_PASS;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_longtext_on_mouse_click(union zori_event * event) {
|
|
|
+ struct zori_widget * widget = event->any.widget;
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ float x = event->sys.ev->mouse.x;
|
|
|
+ float y = event->sys.ev->mouse.y;
|
|
|
+ if (zori_xy_inside_widget_p(widget, x, y)) {
|
|
|
+ widget->result = widget->id;
|
|
|
+ LOG_NOTE("Longtext %d clicked: %d, result %d.\n", widget->id, widget->result);
|
|
|
+ return zori_widget_raise_action_event(widget);
|
|
|
+ }
|
|
|
+ return ZORI_HANDLE_PASS;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_longtext_on_draw(union zori_event * event) {
|
|
|
+ struct zori_widget * widget = event->any.widget;
|
|
|
+ struct zori_longtext * longtext = zori_widget_to_longtext(widget);
|
|
|
+ zori_draw_longtext(longtext);
|
|
|
+ return ZORI_HANDLE_PASS;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+struct zori_handler zori_longtext_handlers[] = {
|
|
|
+ { ZORI_SYSTEM_EVENT_MOUSE_BUTTON_DOWN , zori_longtext_on_mouse_click , NULL },
|
|
|
+ { ZORI_SYSTEM_EVENT_MOUSE_AXES , zori_longtext_on_mouse_axes , NULL },
|
|
|
+ { ZORI_EVENT_DRAW , zori_longtext_on_draw , NULL },
|
|
|
+ { -1, NULL, NULL }
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+struct zori_longtext *
|
|
|
+zori_longtext_init(struct zori_longtext * longtext, const char * text) {
|
|
|
+ if (longtext) {
|
|
|
+ zori_longtext_set_cstr(longtext, text);
|
|
|
+ }
|
|
|
+ return longtext;
|
|
|
+}
|
|
|
+
|
|
|
+struct zori_longtext * zori_longtext_new(zori_id id, zori_id parent_id,
|
|
|
+ zori_box * box, const char * text) {
|
|
|
+ struct zori_longtext * longtext = NULL;
|
|
|
+ longtext = calloc(1, sizeof(*longtext));
|
|
|
+ if (!longtext) return NULL;
|
|
|
+ zori_widget_initall(&longtext->widget, ZORI_WIDGET_TYPE_LONGTEXT, id, zori_get_widget(parent_id),
|
|
|
+ box, NULL, ZORI_ARRAY_AND_AMOUNT(zori_longtext_handlers));
|
|
|
+ if (!zori_longtext_init(longtext, text)) {
|
|
|
+ free(longtext);
|
|
|
+ longtext = NULL;
|
|
|
+ }
|
|
|
+ zori_widget_hover_(&longtext->widget, 0);
|
|
|
+
|
|
|
+ return longtext;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+zori_id zori_new_longtext(zori_id id, zori_id parent, zori_box * box, const char * text) {
|
|
|
+ struct zori_longtext * longtext = zori_longtext_new(id, parent, box, text);
|
|
|
+ if (!longtext) return ZORI_ID_ENOMEM;
|
|
|
+ return longtext->widget.id;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
|