text/plain
•
7.34 KB
•
267 lines
#include "ui.h"
#include <stdlib.h>
#include <string.h>
#include "lvgl.h"
#include "moon.h"
#include "playlist.h"
#include "storage.h"
#include "topbar.h"
char string_buffer[256];
// Pending screen transition state
static lv_obj_t* pending_screen;
static anim_dir_t pending_dir;
// Confirm dialog state
static confirm_cb_t confirm_callback;
static screen_type_t confirm_return_screen;
static char confirm_title[64];
struct confirm_screen_state {
lv_obj_t* list;
};
static void button_focus_cb(lv_event_t* e) {
lv_obj_t* btn = lv_event_get_target(e);
if (!btn) {
return;
}
lv_obj_t* label = lv_obj_get_child(btn, 0);
if (!label) {
return;
}
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_FOCUSED) {
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
} else {
lv_label_set_long_mode(label, LV_LABEL_LONG_CLIP);
}
}
lv_obj_t* bvs_list_add_button(lv_obj_t* list,
const void* icon,
const char* txt) {
lv_obj_t* ret = lv_list_add_button(list, icon, txt);
lv_obj_set_style_pad_hor(ret, 2, 0);
lv_obj_set_style_pad_ver(ret, 1, 0);
lv_obj_set_height(ret, 20);
lv_obj_t* label = lv_obj_get_child(ret, 0);
lv_label_set_long_mode(label, LV_LABEL_LONG_DOT);
lv_obj_set_style_anim_duration(label, 12000, 0);
lv_obj_add_event_cb(ret, button_focus_cb, LV_EVENT_FOCUSED, NULL);
lv_obj_add_event_cb(ret, button_focus_cb, LV_EVENT_DEFOCUSED, NULL);
return ret;
}
lv_obj_t* bvs_list_create(lv_obj_t* parent) {
lv_obj_t* list = lv_list_create(parent);
lv_obj_set_style_pad_hor(list, 2, 0);
lv_obj_set_style_radius(list, 0, 0);
lv_obj_set_style_border_side(list, 0, 0);
lv_obj_set_pos(list, 0, TOPBAR_H);
lv_obj_set_size(list, SCREEN_WIDTH, SCREEN_HEIGHT - TOPBAR_H);
return list;
}
void bvs_list_clear(lv_obj_t* list) {
if (!list) {
return;
}
uint32_t count = lv_obj_get_child_count(list);
for (uint32_t i = 0; i < count; i++) {
lv_group_remove_obj(lv_obj_get_child(list, i));
}
lv_obj_delete(list);
}
void path_parent(const char* path,
char* parent,
size_t parent_size,
char* basename,
size_t basename_size) {
strncpy(parent, path, parent_size);
parent[parent_size - 1] = '\0';
char* last_slash = strrchr(parent, '/');
if (!last_slash || last_slash == parent) {
strncpy(basename, parent + 1, basename_size);
basename[basename_size - 1] = '\0';
strcpy(parent, "/");
} else {
strncpy(basename, last_slash + 1, basename_size);
basename[basename_size - 1] = '\0';
*last_slash = '\0';
}
}
int entry_name_cmp(const void* a, const void* b) {
const storage_entry_t* ea = a;
const storage_entry_t* eb = b;
return strcmp(ea->name, eb->name);
}
int get_sorted_entries(const char* dir,
storage_entry_t* entries,
int max_entries) {
int count = storage_list_dir(dir, entries, max_entries);
if (count <= 0) {
return 0;
}
qsort(entries, (size_t)count, sizeof(storage_entry_t), entry_name_cmp);
int visible = 0;
for (int i = 0; i < count; i++) {
if (entries[i].name[0] == '.') {
continue;
}
entries[visible++] = entries[i];
}
return visible;
}
bool get_entry_at_index(const char* dir, int index, storage_entry_t* out) {
storage_entry_t* entries =
malloc(STORAGE_MAX_DIR_ENTRIES * sizeof(storage_entry_t));
if (!entries) {
return false;
}
int count = get_sorted_entries(dir, entries, STORAGE_MAX_DIR_ENTRIES);
bool found = (index >= 0 && index < count);
if (found) {
*out = entries[index];
}
free(entries);
return found;
}
void show_queue_dialog(const char* path,
playlist_type_t type,
screen_type_t return_screen,
const char* cwd,
const char* focus_name) {
strncpy(app_state.pending_path, path, sizeof(app_state.pending_path));
app_state.pending_path[sizeof(app_state.pending_path) - 1] = '\0';
app_state.pending_type = type;
app_state.return_screen = return_screen;
strncpy(app_state.return_cwd, cwd, sizeof(app_state.return_cwd));
app_state.return_cwd[sizeof(app_state.return_cwd) - 1] = '\0';
strncpy(app_state.return_focus, focus_name, sizeof(app_state.return_focus));
app_state.return_focus[sizeof(app_state.return_focus) - 1] = '\0';
navigate_to(SCREEN_QUEUE_DIALOG);
}
lv_obj_t* bvs_list_add_nav_button(lv_obj_t* list,
const void* icon,
const char* txt,
lv_event_cb_t click_cb,
void* click_data,
lv_event_cb_t cancel_cb) {
lv_obj_t* btn = bvs_list_add_button(list, icon, txt);
lv_obj_add_event_cb(btn, click_cb, LV_EVENT_CLICKED, click_data);
lv_obj_add_event_cb(btn, cancel_cb, LV_EVENT_CANCEL, NULL);
lv_group_add_obj(lv_group_get_default(), btn);
return btn;
}
// --- Navigation ---
lv_obj_t* navigate_prepare(anim_dir_t dir) {
free_screen();
lv_obj_t* scr = lv_obj_create(NULL);
pending_screen = scr;
pending_dir = dir;
return scr;
}
void navigate_commit(void) {
if (!pending_screen) {
return;
}
lv_obj_t* scr = pending_screen;
anim_dir_t dir = pending_dir;
pending_screen = NULL;
if (dir == ANIM_FORWARD) {
lv_screen_load_anim(scr, LV_SCR_LOAD_ANIM_MOVE_LEFT, ANIM_DURATION_MS,
0, true);
} else if (dir == ANIM_BACK) {
lv_screen_load_anim(scr, LV_SCR_LOAD_ANIM_MOVE_RIGHT, ANIM_DURATION_MS,
0, true);
} else {
lv_screen_load_anim(scr, LV_SCR_LOAD_ANIM_NONE, 0, 0, true);
}
}
// --- Confirm dialog screen ---
static void confirm_ok_cb(lv_event_t* e) {
if (lv_event_get_code(e) != LV_EVENT_CLICKED) {
return;
}
confirm_cb_t cb = confirm_callback;
screen_type_t ret = confirm_return_screen;
if (cb) {
cb();
}
navigate_back(ret);
}
static void confirm_cancel_cb(lv_event_t* e) {
(void)e;
navigate_back(confirm_return_screen);
}
static void confirm_cancel_click_cb(lv_event_t* e) {
if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
confirm_cancel_cb(e);
}
}
ui_state_t setup_confirm_screen(lv_obj_t* parent) {
confirm_screen_state_t* state = calloc(1, sizeof(confirm_screen_state_t));
topbar_set_title(confirm_title);
state->list = bvs_list_create(parent);
lv_obj_t* ok_btn = bvs_list_add_button(state->list, NULL, "Confirm");
lv_obj_add_event_cb(ok_btn, confirm_ok_cb, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(ok_btn, confirm_cancel_cb, LV_EVENT_CANCEL, NULL);
lv_obj_set_style_bg_opa(ok_btn, LV_OPA_COVER, 0);
lv_obj_t* cancel_btn = bvs_list_add_button(state->list, NULL, "Cancel");
lv_obj_add_event_cb(cancel_btn, confirm_cancel_click_cb, LV_EVENT_CLICKED,
NULL);
lv_obj_add_event_cb(cancel_btn, confirm_cancel_cb, LV_EVENT_CANCEL, NULL);
lv_group_add_obj(lv_group_get_default(), ok_btn);
lv_group_add_obj(lv_group_get_default(), cancel_btn);
lv_group_focus_obj(ok_btn);
return (ui_state_t){.type = SCREEN_CONFIRM, .confirm = state};
}
void free_confirm_screen(confirm_screen_state_t* state) {
if (!state) {
return;
}
free(state);
}
void navigate_to_confirm(const char* title,
confirm_cb_t on_confirm,
screen_type_t return_screen) {
confirm_callback = on_confirm;
confirm_return_screen = return_screen;
strncpy(confirm_title, title, sizeof(confirm_title));
confirm_title[sizeof(confirm_title) - 1] = '\0';
navigate_to(SCREEN_CONFIRM);
}