summaryrefslogtreecommitdiff
path: root/src/fruits/cmc/ui/widgets
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-04-25 15:56:56 -0400
committerAndrew Opalach <andrew@akon.city> 2025-04-25 15:56:56 -0400
commitb1985414e54edf1eebe597551a48609ad08c6cfa (patch)
tree4a4def499348e0c9ccb52472e7c862f814993931 /src/fruits/cmc/ui/widgets
parent76fc9fe33dd58926e552b6bd3b79d798b452c6ed (diff)
downloadcamu-b1985414e54edf1eebe597551a48609ad08c6cfa.tar.gz
camu-b1985414e54edf1eebe597551a48609ad08c6cfa.tar.bz2
camu-b1985414e54edf1eebe597551a48609ad08c6cfa.zip
Partial cmc ui refactor
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/fruits/cmc/ui/widgets')
-rw-r--r--src/fruits/cmc/ui/widgets/now_playing.c223
-rw-r--r--src/fruits/cmc/ui/widgets/now_playing.h25
-rw-r--r--src/fruits/cmc/ui/widgets/tile.h5
3 files changed, 253 insertions, 0 deletions
diff --git a/src/fruits/cmc/ui/widgets/now_playing.c b/src/fruits/cmc/ui/widgets/now_playing.c
new file mode 100644
index 0000000..d30590c
--- /dev/null
+++ b/src/fruits/cmc/ui/widgets/now_playing.c
@@ -0,0 +1,223 @@
+#include <math.h>
+
+#include "../../../../liana/list.h"
+#include "../../../../sink/util.h"
+
+#include "../../cmc.h"
+
+#include "../util/waveform.h"
+#include "../util/notcurses_ext.h"
+
+#include "now_playing.h"
+
+void cmc_now_playing_init(struct cmc_now_playing *np)
+{
+ al_array_init(np->visuals);
+}
+
+bool cmc_now_playing_layout(struct cmc_now_playing *np, struct ncplane *parent, u32 y)
+{
+ if (np->n) ncplane_destroy(np->n);
+ u32 width = ncplane_dim_x(parent);
+ u32 height = ncplane_dim_y(parent);
+ struct ncplane_options nopts = { 0 };
+ nopts.x = 0;
+ nopts.y = y;
+ nopts.cols = width;
+ nopts.rows = height - y;
+ np->n = ncplane_create(parent, &nopts);
+ if (np->wave) ncplane_destroy(np->wave);
+ nopts.x = 1;
+ nopts.y = 3;
+ nopts.rows = 5;
+ nopts.cols = width - 2;
+ np->wave = ncplane_create(np->n, &nopts);
+ np->blitted = NULL;
+ struct cmc_now_playing_visual *visual;
+ al_array_foreach_ptr(np->visuals, i, visual) {
+ if (visual->visual) ncvisual_destroy(visual->visual);
+ }
+ np->visuals.count = 0;
+ return true;
+}
+
+static void get_entry_duration_and_pos(struct cmc_list_entry *entry, u64 *duration, u64 *pos)
+{
+ *pos = entry->offset;
+ *duration = entry->duration;
+ u64 now = nn_get_timestamp();
+ bool paused = entry->start == LIANA_TIMESTAMP_INVALID;
+ if (!paused && now > entry->start) {
+ *pos += now - entry->start;
+ }
+ *pos = MIN(*pos, *duration);
+}
+
+bool cmc_now_playing_tick(struct cmc_now_playing *np, struct cmc_list_entry *entry)
+{
+ return true;
+ u64 duration = 0, pos = 0;
+ if (entry) get_entry_duration_and_pos(entry, &duration, &pos);
+ u32 width = ncplane_dim_x(np->n);
+ f32 percent = 1.f;
+ if (duration > 0) percent = MIN(pos / (f32)duration, 1.f);
+ u32 segments = (width - 2) * percent;
+ if (segments != np->segment_count) {
+ np->segment_count = segments;
+ return true;
+ } else {
+ // @TODO: Avoiding judder: Implement and utilize tick timing change.
+ if (pos < np->last_pos || pos - np->last_pos > 1000000) {
+ np->last_pos = pos;
+ return true;
+ }
+ }
+ return false;
+}
+
+void cmc_now_playing_erase(struct cmc_now_playing *np)
+{
+ ncplane_erase(np->n);
+}
+
+void cmc_now_playing_render(struct cmc_now_playing *np, struct cmc_list_entry *entry)
+{
+ u64 duration = 0, pos = 0;
+ if (entry) get_entry_duration_and_pos(entry, &duration, &pos);
+ u64 remaining = duration - pos;
+
+ ncplane_set_styles(np->n, NCSTYLE_BOLD);
+ if (remaining == 0) {
+ ncplane_putstr_yx(np->n, 1, 2, "∨");
+ } else {
+ al_assert(entry);
+ if (remaining == 0) {
+ ncplane_putstr_yx(np->n, 1, 2, "∨");
+ } else {
+ bool paused = entry->start == LIANA_TIMESTAMP_INVALID;
+ ncplane_putstr_yx(np->n, 1, 2, paused ? "^": ">");
+ }
+ }
+ ncplane_set_styles(np->n, NCSTYLE_NONE);
+
+ u32 width = ncplane_dim_x(np->n);
+ u32 height = ncplane_dim_y(np->n);
+
+ // ━ ╸╺ ╼ ╾ ╴╶
+ //ncplane_set_fg_palindex(np->n, 8);
+ f32 percent = 1.f;
+ if (duration > 0) percent = MIN(pos / (f32)duration, 1.f);
+ u32 bar_width = width - 1;
+ u32 segments = roundf((bar_width * 4) * percent);
+ u32 head = segments % 4;
+ segments /= 4;
+ for (u32 i = 0; i < segments; i++) {
+ ncplane_putstr_yx(np->n, 2, i + 1, "━");
+ }
+ char *head_segment;
+ switch (head) {
+ case 0:
+ head_segment = " ";
+ break;
+ case 1:
+ head_segment = "╴";
+ break;
+ case 2:
+ head_segment = "╸";
+ break;
+ case 3:
+ head_segment = "╾";
+ break;
+ }
+ ncplane_putstr_yx(np->n, 2, segments + 1, head_segment);
+ ncplane_set_fg_default(np->n);
+
+ char timestr[16] = { 0 }; // max = 829128280:01:49\0
+ s32 offset = camu_print_time(timestr, sizeof(timestr), pos, true, 4);
+ ncplane_putstr_yx(np->n, 1, 4, timestr);
+ ncplane_putstr_yx(np->n, 1, 4 + offset, "⎹");
+
+ if (entry) {
+ cmc_ui_putnwstr_yx(np->n, 1, 6 + offset, MIN(entry->name.length, (u32)60), &entry->name);
+ }
+
+ bool show_remaining = false;
+ if (show_remaining) {
+ offset = camu_print_time(timestr, sizeof(timestr), remaining, true, 4);
+ } else {
+ offset = camu_print_time(timestr, sizeof(timestr), duration, true, 4);
+ }
+ ncplane_putstr_yx(np->n, 1, width - 4 - offset, "⎸");
+ // "=" Duration, "+" Segment, "-" Remaining.
+ if (show_remaining) {
+ ncplane_putstr_yx(np->n, 1, width - 3 - offset, "-");
+ } else {
+ ncplane_putstr_yx(np->n, 1, width - 3 - offset, "=");
+ }
+ ncplane_putstr_yx(np->n, 1, width - 2 - offset, timestr);
+
+ if (entry) {
+ bool visual_cached = false;
+ struct cmc_now_playing_visual *visual;
+ al_array_foreach_ptr(np->visuals, i, visual) {
+ if (visual->entry == entry) {
+ visual_cached = true;
+ break;
+ }
+ }
+
+ if (!visual_cached) {
+ if (entry->raw.data != NULL) {
+ al_array_push(np->visuals, (struct cmc_now_playing_visual){});
+ visual = &al_array_last(np->visuals);
+ visual->entry = entry;
+ u32 *rgba;
+ u32 pixel_width, pixel_height;
+ ncplane_pixel_geom(np->wave, &pixel_height, &pixel_width, NULL, NULL, NULL, NULL);
+ cmc_draw_waveform(entry, pixel_width, pixel_height, false, entry->raw.drawing, &rgba);
+ visual->visual = ncvisual_from_rgba(rgba, pixel_height, pixel_width * 4, pixel_width);
+ } else {
+ visual = NULL;
+ }
+ }
+
+ if (visual && entry != np->blitted) {
+ ncplane_erase(np->wave);
+ struct ncvisual_options vopts = { 0 };
+ vopts.blitter = NCBLIT_PIXEL;
+ vopts.scaling = NCSCALE_STRETCH;
+ vopts.flags = NCVISUAL_OPTION_NOINTERPOLATE;
+ vopts.n = np->wave;
+ vopts.x = 0;
+ vopts.y = 0;
+ ncvisual_blit(ncplane_notcurses(np->wave), visual->visual, &vopts);
+ np->blitted = entry;
+ }
+ }
+
+ u64 c = 0;
+ ncchannels_set_fg_default(&c);
+ //ncchannels_set_fg_palindex(&c, 8);
+ ncchannels_set_bg_default(&c);
+ ncchannels_set_bg_alpha(&c, 0);
+ ncchannels_set_fg_alpha(&c, 255);
+ ncplane_cursor_move_yx(np->n, 0, 0);
+ //ncplane_double_box(np->n, NCSTYLE_NONE, c, height - 1, width - 1, 0);
+ ncplane_light_box(np->n, NCSTYLE_NONE, c, height - 1, width - 1, 0);
+ //ncplane_set_fg_palindex(np->n, 8);
+ if (segments > 0 || head > 0) {
+ if (segments == 0 && head == 1) {
+ ncplane_putstr_yx(np->n, 2, 0, "├");
+ } else {
+ ncplane_putstr_yx(np->n, 2, 0, "┝");
+ }
+ if (segments == bar_width || (segments == bar_width - 1 && head > 0)) {
+ if (head == 1) {
+ ncplane_putstr_yx(np->n, 2, width - 1, "┤");
+ } else {
+ ncplane_putstr_yx(np->n, 2, width - 1, "┥");
+ }
+ }
+ }
+ ncplane_set_fg_default(np->n);
+}
diff --git a/src/fruits/cmc/ui/widgets/now_playing.h b/src/fruits/cmc/ui/widgets/now_playing.h
new file mode 100644
index 0000000..5993c59
--- /dev/null
+++ b/src/fruits/cmc/ui/widgets/now_playing.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <al/array.h>
+#include <notcurses/notcurses.h>
+
+struct cmc_now_playing_visual {
+ struct ncvisual *visual;
+ struct cmc_list_entry *entry;
+};
+
+struct cmc_now_playing {
+ struct ncplane *n;
+ bool tick;
+ u64 last_pos;
+ u32 segment_count;
+ struct ncplane *wave;
+ array(struct cmc_now_playing_visual) visuals;
+ struct cmc_list_entry *blitted;
+};
+
+void cmc_now_playing_init(struct cmc_now_playing *np);
+bool cmc_now_playing_layout(struct cmc_now_playing *np, struct ncplane *parent, u32 y);
+bool cmc_now_playing_tick(struct cmc_now_playing *np, struct cmc_list_entry *entry);
+void cmc_now_playing_erase(struct cmc_now_playing *np);
+void cmc_now_playing_render(struct cmc_now_playing *np, struct cmc_list_entry *entry);
diff --git a/src/fruits/cmc/ui/widgets/tile.h b/src/fruits/cmc/ui/widgets/tile.h
new file mode 100644
index 0000000..6f15c7d
--- /dev/null
+++ b/src/fruits/cmc/ui/widgets/tile.h
@@ -0,0 +1,5 @@
+#pragma once
+
+struct cmc_tile {
+
+};