framer

Slide-show application for nerds ☝️🤓

Commit
516bfe358bab5324b5c35c354924bdd683b66c60
Parent
a4e9d1a767bff5c4ae81b4ec1e05dfb4a178861d
Author
Pablo <pablo-pie@riseup.net>
Date

Refactored the UI library

Diffstats

6 files changed, 338 insertions, 242 deletions

Status Name Changes Insertions Deletions
Modified nob.c 2 files changed 1 1
Added src/framer.c 1 file changed 126 0
Modified src/frames.h 2 files changed 42 102
Deleted src/main.c 1 file changed 0 120
Modified src/plug.c 2 files changed 16 19
Added src/uix.h 1 file changed 153 0
diff --git a/nob.c b/nob.c
@@ -21,7 +21,7 @@ int main(int argc, char **argv)
   const char *output_path = BUILD_PATH "framer";
   nob_cmd_append(&cmd, CC, CFLAGS);
   nob_cmd_append(&cmd, "-o", output_path);
-  nob_cmd_append(&cmd, SRC_PATH "main.c");
+  nob_cmd_append(&cmd, SRC_PATH "framer.c");
   if (!nob_cmd_run_sync_and_reset(&cmd)) return EXIT_FAILURE;
 
   const char *libplug_path = BUILD_PATH "libplug.so";
diff --git /dev/null b/src/framer.c
@@ -0,0 +1,126 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "raylib.h"
+#define FRAMES_IMPLEMENTATION
+#include "frames.h"
+
+#define MONITOR_WIDTH  1366
+#define MONITOR_HEIGHT 768
+
+static void* libplug = NULL;
+void (*plug_load_frames)(Frames* fs);
+
+bool reload_libplug(char* libplug_path)
+{
+  if (libplug != NULL) dlclose(libplug);
+
+  libplug = dlopen(libplug_path, RTLD_NOW);
+  if (libplug == NULL) {
+    TraceLog(LOG_ERROR, "HOTRELOAD: could not load %s: %s",
+             libplug_path, dlerror());
+    return false;
+  }
+
+  const char* symbol = "plug_load_frames";
+  plug_load_frames = dlsym(libplug, symbol);
+  if (plug_load_frames == NULL) {
+    TraceLog(LOG_ERROR, "HOTRELOAD: could not find %s symbol in %s: %s",
+             symbol, libplug_path, dlerror());
+    return false;
+  }
+
+  return true;
+}
+
+int main(int argc, char **argv)
+{
+  char * program_name = nob_shift(argv, argc);
+
+  if (argc == 0) {
+    TraceLog(LOG_ERROR, "No libplug path provided");
+    TraceLog(LOG_ERROR, "USAGE: %s <libplug.so>", program_name);
+    return EXIT_FAILURE;
+  }
+
+  char* libplug_path = nob_shift(argv, argc);
+  if (!reload_libplug(libplug_path)) return EXIT_FAILURE;
+
+  int width  = 800;
+  int height = 450;
+
+  float monitor_scale = (float)MONITOR_WIDTH/(float)width;
+
+  Frames fs = {0};
+  frames_load(&fs, plug_load_frames);
+
+  InitWindow(width, height, frames_title(&fs));
+  SetTargetFPS(60);
+  SetExitKey(KEY_Q);
+
+  float scale = 1.0;
+  bool was_g_last_pressed  = false;
+  while (!WindowShouldClose()) {
+    if (IsWindowFullscreen()) {
+      scale = monitor_scale;
+      SetWindowSize(MONITOR_WIDTH, MONITOR_HEIGHT);
+    } else {
+      scale = 1.0;
+      SetWindowSize(width, height);
+    }
+
+    int pressed = GetCharPressed();
+    switch (pressed) {
+    case 0: break;
+    case 'G':
+      frames_last(&fs);
+      was_g_last_pressed = false;
+      SetWindowTitle(frames_title(&fs));
+      break;
+    case 'g':
+      if (was_g_last_pressed) {
+        frames_first(&fs);
+        was_g_last_pressed = false;
+        SetWindowTitle(frames_title(&fs));
+      } else {
+        was_g_last_pressed = true;
+      }
+      break;
+    case 'F':
+    case 'f':
+      ToggleFullscreen();
+      was_g_last_pressed = false;
+      break;
+    case 'J':
+    case 'j':
+    case ' ':
+      frames_next(&fs);
+      was_g_last_pressed = false;
+      SetWindowTitle(frames_title(&fs));
+      break;
+    case 'K':
+    case 'k':
+      frames_prev(&fs);
+      was_g_last_pressed = false;
+      SetWindowTitle(frames_title(&fs));
+      break;
+    case 'r':
+    case 'R':
+      if (!reload_libplug(libplug_path)) return EXIT_FAILURE;
+      frames_load(&fs, plug_load_frames);
+      was_g_last_pressed = false;
+      SetWindowTitle(frames_title(&fs));
+      break;
+    default:
+      was_g_last_pressed = false;
+    }
+
+    frames_tick(&fs, GetFrameTime());
+    frames_draw(&fs, scale);
+  }
+
+  CloseWindow();
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/frames.h b/src/frames.h
@@ -4,13 +4,16 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stddef.h>
+#include <assert.h>
+#include "raylib.h"
 
-#include "nob.h"
-
+#ifdef FRAMES_IMPLEMENTATION
+#define NOB_IMPLEMENTATION
 #define ARENA_IMPLEMENTATION
-#include "arena.h"
+#endif // FRAMES_IMPLEMENTATION
 
-// == Frames data-structure ===================================================
+#include "nob.h"
+#include "arena.h"
 
 typedef void (*FrameDraw)(float time, float scale);
 typedef void (*FrameTick)(float time, void* state);
@@ -27,6 +30,8 @@ typedef struct {
 } Frame;
 
 typedef struct {
+  const char* title;
+
   Frame* items;
   size_t count;
   size_t capacity;
@@ -37,6 +42,31 @@ typedef struct {
   Arena states;
 } Frames;
 
+void frames_next(Frames* fs);
+void frames_prev(Frames* fs);
+void frames_first(Frames* fs);
+void frames_last(Frames* fs);
+
+const char* frames_title(Frames* fs);
+void frames_draw(Frames* fs, float scale);
+void frames_tick(Frames* fs, float dt);
+
+void frames_append(Frames* fs, FrameDraw draw, FrameTick tick,
+                   size_t state_size);
+
+typedef void (*FramesLoad)(Frames* fs);
+void frames_load(Frames* fs, FramesLoad load);
+
+#ifdef FRAMES_IMPLEMENTATION
+
+size_t frames__fast_log(size_t n)
+{
+  size_t result = 1, m = n/10;
+
+  while (m > 0) { result += 1; m /= 10; }
+  return result;
+}
+
 void frames_next(Frames* fs)
 {
   assert(fs->cursor < fs->count);
@@ -73,6 +103,13 @@ void frames_last(Frames* fs)
   fs->time = 0;
 }
 
+const char* frames_title(Frames* fs)
+{
+  size_t current = fs->cursor + 1, total = fs->count;
+  size_t padding = frames__fast_log(total) - frames__fast_log(current);
+  return TextFormat("%s [%*zu/%zu]", fs->title, padding, current, total);
+}
+
 void frames_draw(Frames* fs, float scale)
 {
   assert(fs->cursor < fs->count);
@@ -116,8 +153,6 @@ void frames_append(Frames* fs, FrameDraw draw, FrameTick tick,
   nob_da_append(fs, f);
 }
 
-typedef void (*FramesLoad)(Frames* fs);
-
 void frames_load(Frames* fs, FramesLoad load)
 {
   fs->count = 0;
@@ -130,100 +165,5 @@ void frames_load(Frames* fs, FramesLoad load)
   if (fs->cursor >= fs->count) fs->cursor = fs->count - 1;
 }
 
-// == Text layout =============================================================
-
-typedef enum {
-  FRAMER_CENTERED,
-  FRAMER_XY,
-} FramerAlignment;
-
-typedef struct {
-  const char* text;
-  Color       color;
-
-  size_t width;
-  size_t x_offset;
-} FramerLineSpan;
-
-typedef struct {
-  FramerLineSpan*  items;
-  size_t           count, capacity;
-
-  size_t width;
-  int    font_size;
-
-  FramerAlignment align;
-  int             y, x;
-} FramerLine;
-
-static FramerLine framer__line = {0};
-
-void line_begin(int x, int y, int font_size)
-{
-  framer__line.count = 0;
-  framer__line.width = 0;
-
-  framer__line.align = FRAMER_XY;
-  framer__line.x = x;
-  framer__line.y = y;
-  framer__line.font_size = font_size;
-}
-
-void line_begin_centered(int y, int font_size)
-{
-  framer__line.count = 0;
-  framer__line.width = 0;
-
-  framer__line.align = FRAMER_CENTERED;
-  framer__line.y = y;
-  framer__line.font_size = font_size;
-}
-
-void line_add(const char* text, Color color)
-{
-  size_t text_width = MeasureText(text, framer__line.font_size);
-  FramerLineSpan s = {
-    .text     = text,
-    .color    = color,
-    .width    = text_width,
-    .x_offset = framer__line.width,
-  };
-
-  nob_da_append(&framer__line, s);
-  framer__line.width += text_width;
-}
-
-void line_end(void)
-{
-  int x, y = framer__line.y;
-
-  switch (framer__line.align) {
-    case FRAMER_CENTERED:
-      x = (GetScreenWidth() - framer__line.width)/2;
-      break;
-    case FRAMER_XY:
-      x = framer__line.x;
-      break;
-  }
-
-  for (size_t i = 0; i < framer__line.count; i++) {
-    FramerLineSpan s = framer__line.items[i];
-    DrawText(s.text, x + s.x_offset, y, framer__line.font_size, s.color);
-  }
-}
-
-void line(const char* text, Color color, int x, int y, int font_size)
-{
-  line_begin(x, y, font_size);
-  line_add(text, color);
-  line_end();
-}
-
-void line_centered(const char* text, Color color, int y, int font_size)
-{
-  line_begin_centered(y, font_size);
-  line_add(text, color);
-  line_end();
-}
-
+#endif // FRAMES_IMPLEMENTATION
 #endif // FRAMES_H_
diff --git a/src/main.c /dev/null
@@ -1,120 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <dlfcn.h>
-
-#include "raylib.h"
-#include "frames.h"
-
-#define MONITOR_WIDTH  1366
-#define MONITOR_HEIGHT 768
-
-static void* libplug = NULL;
-void (*plug_load_frames)(Frames* fs);
-
-bool reload_libplug(char* libplug_path)
-{
-  if (libplug != NULL) dlclose(libplug);
-
-  libplug = dlopen(libplug_path, RTLD_NOW);
-  if (libplug == NULL) {
-    TraceLog(LOG_ERROR, "HOTRELOAD: could not load %s: %s",
-             libplug_path, dlerror());
-    return false;
-  }
-
-  const char* symbol = "plug_load_frames";
-  plug_load_frames = dlsym(libplug, symbol);
-  if (plug_load_frames == NULL) {
-    TraceLog(LOG_ERROR, "HOTRELOAD: could not find %s symbol in %s: %s",
-             symbol, libplug_path, dlerror());
-    return false;
-  }
-
-  return true;
-}
-
-int main(int argc, char **argv)
-{
-  char * program_name = nob_shift(argv, argc);
-
-  if (argc == 0) {
-    TraceLog(LOG_ERROR, "No libplug path provided");
-    TraceLog(LOG_ERROR, "USAGE: %s <libplug.so>", program_name);
-    return EXIT_FAILURE;
-  }
-
-  char* libplug_path = nob_shift(argv, argc);
-  if (!reload_libplug(libplug_path)) return EXIT_FAILURE;
-
-  int width  = 800;
-  int height = 450;
-
-  float monitor_scale = (float)MONITOR_WIDTH/(float)width;
-
-  InitWindow(width, height, "\"introdução a assembly\" slides");
-  SetTargetFPS(60);
-  SetExitKey(KEY_Q);
-
-  Frames fs = {0};
-  frames_load(&fs, plug_load_frames);
-
-  float scale = 1.0;
-  bool was_g_last_pressed  = false;
-  while (!WindowShouldClose()) {
-    if (IsWindowFullscreen()) {
-      scale = monitor_scale;
-      SetWindowSize(MONITOR_WIDTH, MONITOR_HEIGHT);
-    } else {
-      scale = 1.0;
-      SetWindowSize(width, height);
-    }
-
-    int pressed = GetCharPressed();
-    switch (pressed) {
-    case 0: break;
-    case 'G':
-      frames_last(&fs);
-      was_g_last_pressed = false;
-      break;
-    case 'g':
-      if (was_g_last_pressed) {
-        frames_first(&fs);
-        was_g_last_pressed = false;
-      } else {
-        was_g_last_pressed = true;
-      }
-      break;
-    case 'F':
-    case 'f':
-      ToggleFullscreen();
-      was_g_last_pressed = false;
-      break;
-    case 'J':
-    case 'j':
-    case ' ':
-      frames_next(&fs);
-      was_g_last_pressed = false;
-      break;
-    case 'K':
-    case 'k':
-      frames_prev(&fs);
-      was_g_last_pressed = false;
-      break;
-    case 'r':
-    case 'R':
-      if (!reload_libplug(libplug_path)) return EXIT_FAILURE;
-      frames_load(&fs, plug_load_frames);
-      was_g_last_pressed = false;
-      break;
-    default:
-      was_g_last_pressed = false;
-    }
-
-    frames_tick(&fs, GetFrameTime());
-    frames_draw(&fs, scale);
-  }
-
-  CloseWindow();
-
-  return EXIT_SUCCESS;
-}
diff --git a/src/plug.c b/src/plug.c
@@ -1,5 +1,8 @@
 #include "raylib.h"
+#define FRAMES_IMPLEMENTATION
 #include "frames.h"
+#define UIX_IMPLEMENTATION
+#include "uix.h"
 
 #define BACKGROUND BLACK
 #define FOREGROUND WHITE
@@ -8,44 +11,38 @@
 void title_frame(float time, float scale)
 {
   (void)time;
-  int height = GetScreenHeight();
 
   int title_size    = (int)(50.0 * scale);
   int subtitle_size = (int)(30.0 * scale);
-
   int title_spacing = (int)(10.0 * scale);
-  int title_height  = title_size + title_spacing + subtitle_size;
-
-  int title_y    = (height - title_height)/2;
-  int subtitle_y = title_y + title_size + title_spacing;
 
-  ClearBackground(BACKGROUND);
-
-  line_centered("introdução a assemply", FOREGROUND, title_y, title_size);
-  line_centered("cripto goma 2020",      ACCENT,  subtitle_y, subtitle_size);
+  frame_begin(BACKGROUND);
+    line("introdução a assemply", title_size,    FOREGROUND);
+    hspan(title_spacing);
+    line("cripto goma 2020",      subtitle_size, ACCENT);
+  frame_end();
 }
 
 void oq_frame(float time, float scale)
 {
-  int height    = GetScreenHeight();
   int font_size = (int)(50.0 * scale);
 
   const float animation_speed = .5;
   Color color = FOREGROUND;
   if ((int)(time / animation_speed) % 2 == 0) color = BACKGROUND;
 
-  int y = (height - font_size)/2;
-
-  ClearBackground(BACKGROUND);
-  line_begin_centered(y, font_size);
-    line_add("o quê é ", FOREGROUND);
-    line_add("assembly", ACCENT);
-    line_add(" ?",       color);
-  line_end();
+  frame_begin(BACKGROUND);
+    line_begin(font_size);
+      line_add("o quê é ", FOREGROUND);
+      line_add("assembly", ACCENT);
+      line_add(" ?",       color);
+    line_end();
+  frame_end();
 }
 
 void plug_load_frames(Frames* fs)
 {
+  fs->title = "introdução à assembly";
   frames_append(fs, title_frame, FRAMES_EMPTY_TICK, FRAMES_EMPTY_STATE);
   frames_append(fs, oq_frame,    FRAMES_EMPTY_TICK, FRAMES_EMPTY_STATE);
 }
diff --git /dev/null b/src/uix.h
@@ -0,0 +1,153 @@
+#ifndef UIX_H_
+#define UIX_H_
+
+#include <assert.h>
+
+typedef enum { UI_CENTERED, UI_LEFT, } Alignment;
+
+typedef struct {
+  const char* text;
+  Color       color;
+
+  size_t width;
+  size_t x_offset;
+} LineSpan;
+
+typedef struct {
+  LineSpan*  items;
+  size_t     count, capacity;
+
+  size_t width;
+  int    font_size;
+
+  int y_offset;
+} Line;
+
+#define UIX_LINES_MAX 64
+
+typedef struct {
+  int width, height;
+  int x, y;
+  int y_offset;
+
+  Alignment align;
+
+  Line   lines[UIX_LINES_MAX];
+  size_t lines_count;
+} Layout;
+
+void frame_begin(Color background);
+void frame_end(void);
+
+void line(const char* text, int font_size, Color color);
+void hspan(int space);
+
+void line_begin(int font_size);
+void line_add(const char* text, Color color);
+void line_end(void);
+
+#ifdef UIX_IMPLEMENTATION
+
+static Layout uix__layout = {0};
+
+Line* uix__get_current_line(void)
+{
+  assert(uix__layout.lines_count > 0);
+  assert(uix__layout.lines_count < UIX_LINES_MAX);
+  return &uix__layout.lines[uix__layout.lines_count-1];
+}
+
+Line* uix__alloc_new_line(void)
+{
+  assert(uix__layout.lines_count < UIX_LINES_MAX - 1);
+  Line* result = &uix__layout.lines[uix__layout.lines_count++];
+
+  result->width    = 0;
+  result->count    = 0;
+  result->capacity = 0;
+
+  return result;
+}
+
+void line_begin(int font_size)
+{
+  Line *line = uix__alloc_new_line();
+
+  line->y_offset  = uix__layout.y_offset;
+  line->font_size = font_size;
+}
+
+void line_add(const char* text, Color color)
+{
+  Line *line = uix__get_current_line();
+
+  size_t text_width = MeasureText(text, line->font_size);
+  LineSpan s = {
+    .text     = text,
+    .color    = color,
+    .width    = text_width,
+    .x_offset = line->width,
+  };
+
+  nob_da_append(line, s);
+  line->width += text_width;
+}
+
+void line_end(void)
+{
+  Line *line = uix__get_current_line();
+  uix__layout.y_offset += line->font_size;
+}
+
+void line(const char* text, int font_size, Color color)
+{
+  line_begin(font_size);
+  line_add(text, color);
+  line_end();
+}
+
+void hspan(int space)
+{
+  uix__layout.y_offset += space;
+}
+
+void frame_begin(Color background)
+{
+  ClearBackground(background);
+
+  uix__layout.width  = GetScreenWidth();
+  uix__layout.height = GetScreenHeight();
+
+  uix__layout.x = 0;
+  uix__layout.y = 0;
+  uix__layout.y_offset = 0;
+  uix__layout.lines_count = 0;
+
+  uix__layout.align = UI_CENTERED;
+}
+
+void frame_end(void)
+{
+  int y = uix__layout.y;
+  if (uix__layout.align == UI_CENTERED) {
+    y += (uix__layout.height - uix__layout.y_offset)/2;
+  }
+
+  for (size_t j = 0; j < uix__layout.lines_count; j++) {
+    Line line = uix__layout.lines[j];
+
+    for (size_t i = 0; i < line.count; i++) {
+      int x = uix__layout.x;
+      if (uix__layout.align == UI_CENTERED) {
+        x += (uix__layout.width - line.width)/2;
+      }
+
+      LineSpan s = line.items[i];
+      DrawText(s.text,
+               x + s.x_offset, y + line.y_offset, line.font_size, s.color);
+    }
+  }
+}
+
+#endif // UIX_IMPLEMENTATION
+#endif // UIX_H_