latest change on laptop Bible and SDDownload
This commit is contained in:
@@ -31,6 +31,7 @@ LV_FONT_DECLARE(georgia_regular_24)
|
||||
// books.json : array of {bnumber, bname, verses, chapters}
|
||||
|
||||
#define MAX_BIBLE_BOOKS 66
|
||||
#define MAX_FAVORITES 512
|
||||
#define MAX_BOOK_NAME 40
|
||||
#define MAX_VERSE_TEXT 2048
|
||||
#define MAX_PATH 128
|
||||
@@ -115,11 +116,23 @@ typedef struct {
|
||||
lv_obj_t* book_center_num;
|
||||
lv_obj_t* book_center_verses;
|
||||
lv_obj_t* book_slider; // fast scrub
|
||||
lv_obj_t* btn_favorites;
|
||||
int book_dial_idx; // 0..book_count-1 currently selected
|
||||
int favorite_indices[MAX_FAVORITES];
|
||||
int favorite_count;
|
||||
int favorite_dial_idx;
|
||||
bool browsing_favorites;
|
||||
bool favorite_navigation_active;
|
||||
int book_drag_start_x;
|
||||
bool book_dragging;
|
||||
bool book_browser_visible;
|
||||
|
||||
// Reading-view swipe tracking
|
||||
int verse_drag_start_x;
|
||||
int verse_drag_start_y;
|
||||
bool verse_dragging;
|
||||
bool verse_swipe_consumed;
|
||||
|
||||
// Timer
|
||||
lv_timer_t* auto_timer;
|
||||
lv_timer_t* chrome_auto_hide_timer; // menu auto-hide after 10s on start
|
||||
@@ -135,6 +148,7 @@ static bool load_book(AppCtx* ctx, int bnumber);
|
||||
static void unload_book(AppCtx* ctx);
|
||||
static void show_current_verse(AppCtx* ctx);
|
||||
static void advance_verse(AppCtx* ctx, int delta);
|
||||
static void manually_advance_verse(AppCtx* ctx, int delta);
|
||||
static void jump_to_book(AppCtx* ctx, int book_idx);
|
||||
static void jump_to_global(AppCtx* ctx, int global_idx);
|
||||
static void save_progress(AppCtx* ctx);
|
||||
@@ -142,6 +156,7 @@ static bool load_progress(AppCtx* ctx);
|
||||
static void toggle_ui(AppCtx* ctx);
|
||||
static void set_chrome_visible(AppCtx* ctx, bool visible);
|
||||
static bool is_favorite(AppCtx* ctx, int global_idx);
|
||||
static void load_favorites(AppCtx* ctx);
|
||||
static void toggle_favorite(AppCtx* ctx);
|
||||
static void ensure_user_dir(AppCtx* ctx);
|
||||
static void set_book_browser_visible(AppCtx* ctx, bool visible);
|
||||
@@ -524,13 +539,33 @@ static bool is_favorite(AppCtx* ctx, int global_idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void load_favorites(AppCtx* ctx) {
|
||||
ctx->favorite_count = 0;
|
||||
FILE* f = fopen(ctx->favorites_path, "r");
|
||||
if (!f) return;
|
||||
int global_idx;
|
||||
while (ctx->favorite_count < MAX_FAVORITES && fscanf(f, "%d", &global_idx) == 1) {
|
||||
if (global_idx >= 0 && global_idx < ctx->total_verses) {
|
||||
ctx->favorite_indices[ctx->favorite_count++] = global_idx;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static int find_favorite_position(AppCtx* ctx, int global_idx) {
|
||||
for (int i = 0; i < ctx->favorite_count; i++) {
|
||||
if (ctx->favorite_indices[i] == global_idx) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void toggle_favorite(AppCtx* ctx) {
|
||||
// load all
|
||||
int vals[512];
|
||||
int vals[MAX_FAVORITES];
|
||||
int cnt=0;
|
||||
FILE* f = fopen(ctx->favorites_path, "r");
|
||||
if (f) {
|
||||
while (cnt<512 && fscanf(f, "%d", &vals[cnt])==1) cnt++;
|
||||
while (cnt<MAX_FAVORITES && fscanf(f, "%d", &vals[cnt])==1) cnt++;
|
||||
fclose(f);
|
||||
}
|
||||
// check existence
|
||||
@@ -542,7 +577,7 @@ static void toggle_favorite(AppCtx* ctx) {
|
||||
cnt--;
|
||||
ctx->is_highlighted=false;
|
||||
} else {
|
||||
if (cnt<512) {
|
||||
if (cnt<MAX_FAVORITES) {
|
||||
vals[cnt++]=ctx->cur_global;
|
||||
ctx->is_highlighted=true;
|
||||
}
|
||||
@@ -552,6 +587,7 @@ static void toggle_favorite(AppCtx* ctx) {
|
||||
if (!f) return;
|
||||
for (int i=0;i<cnt;i++) fprintf(f, "%d\n", vals[i]);
|
||||
fclose(f);
|
||||
load_favorites(ctx);
|
||||
}
|
||||
|
||||
/* ── UI logic ── */
|
||||
@@ -709,9 +745,24 @@ static void show_current_verse(AppCtx* ctx) {
|
||||
}
|
||||
|
||||
ctx->is_highlighted = is_favorite(ctx, ctx->cur_global);
|
||||
if (ctx->favorite_navigation_active) {
|
||||
load_favorites(ctx);
|
||||
int favorite_pos = find_favorite_position(ctx, ctx->cur_global);
|
||||
if (favorite_pos >= 0) {
|
||||
ctx->favorite_dial_idx = favorite_pos;
|
||||
if (ctx->lbl_position) {
|
||||
char favorite_pos_buf[40];
|
||||
snprintf(favorite_pos_buf, sizeof(favorite_pos_buf), "Fav %d / %d",
|
||||
favorite_pos + 1, ctx->favorite_count);
|
||||
lv_label_set_text(ctx->lbl_position, favorite_pos_buf);
|
||||
}
|
||||
} else if (ctx->favorite_count == 0) {
|
||||
ctx->favorite_navigation_active = false;
|
||||
}
|
||||
}
|
||||
if (ctx->btn_highlight) {
|
||||
lv_obj_t* lbl = lv_obj_get_child(ctx->btn_highlight, 0);
|
||||
if (lbl) lv_label_set_text(lbl, ctx->is_highlighted ? LV_SYMBOL_OK : LV_SYMBOL_DUMMY);
|
||||
if (lbl) lv_label_set_text(lbl, ctx->is_highlighted ? LV_SYMBOL_OK : LV_SYMBOL_PLUS);
|
||||
if (ctx->is_highlighted) {
|
||||
lv_obj_set_style_bg_color(ctx->btn_highlight, lv_color_hex(0xF5C16C), 0);
|
||||
lv_obj_set_style_text_color(ctx->btn_highlight, lv_color_hex(0x1E1E2E), 0);
|
||||
@@ -734,11 +785,17 @@ static void show_current_verse(AppCtx* ctx) {
|
||||
}
|
||||
|
||||
if (ctx->btn_prev) {
|
||||
if (ctx->cur_global<=0) lv_obj_add_state(ctx->btn_prev, LV_STATE_DISABLED);
|
||||
bool at_start = ctx->favorite_navigation_active
|
||||
? ctx->favorite_dial_idx <= 0
|
||||
: ctx->cur_global <= 0;
|
||||
if (at_start) lv_obj_add_state(ctx->btn_prev, LV_STATE_DISABLED);
|
||||
else lv_obj_clear_state(ctx->btn_prev, LV_STATE_DISABLED);
|
||||
}
|
||||
if (ctx->btn_next) {
|
||||
if (ctx->cur_global >= ctx->total_verses-1) lv_obj_add_state(ctx->btn_next, LV_STATE_DISABLED);
|
||||
bool at_end = ctx->favorite_navigation_active
|
||||
? ctx->favorite_dial_idx >= ctx->favorite_count - 1
|
||||
: ctx->cur_global >= ctx->total_verses - 1;
|
||||
if (at_end) lv_obj_add_state(ctx->btn_next, LV_STATE_DISABLED);
|
||||
else lv_obj_clear_state(ctx->btn_next, LV_STATE_DISABLED);
|
||||
}
|
||||
free(verse_buf);
|
||||
@@ -747,6 +804,28 @@ static void show_current_verse(AppCtx* ctx) {
|
||||
}
|
||||
|
||||
static void advance_verse(AppCtx* ctx, int delta) {
|
||||
if (ctx->favorite_navigation_active) {
|
||||
load_favorites(ctx);
|
||||
if (ctx->favorite_count <= 0) {
|
||||
ctx->favorite_navigation_active = false;
|
||||
return;
|
||||
}
|
||||
int current_pos = find_favorite_position(ctx, ctx->cur_global);
|
||||
if (current_pos < 0) current_pos = ctx->favorite_dial_idx;
|
||||
if (current_pos < 0) current_pos = 0;
|
||||
if (current_pos >= ctx->favorite_count) current_pos = ctx->favorite_count - 1;
|
||||
int next_pos = current_pos + delta;
|
||||
if (next_pos < 0) next_pos = 0;
|
||||
if (next_pos >= ctx->favorite_count) next_pos = ctx->favorite_count - 1;
|
||||
ctx->favorite_dial_idx = next_pos;
|
||||
int next_global = ctx->favorite_indices[next_pos];
|
||||
if (next_global != ctx->cur_global) {
|
||||
ctx->cur_global = next_global;
|
||||
show_current_verse(ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int ng = ctx->cur_global + delta;
|
||||
if (ng < 0) ng = 0;
|
||||
if (ng >= ctx->total_verses) ng = ctx->total_verses-1;
|
||||
@@ -756,6 +835,11 @@ static void advance_verse(AppCtx* ctx, int delta) {
|
||||
}
|
||||
}
|
||||
|
||||
static void manually_advance_verse(AppCtx* ctx, int delta) {
|
||||
advance_verse(ctx, delta);
|
||||
if (ctx->auto_timer) lv_timer_reset(ctx->auto_timer);
|
||||
}
|
||||
|
||||
static void jump_to_global(AppCtx* ctx, int global_idx) {
|
||||
if (global_idx < 0) global_idx = 0;
|
||||
if (global_idx >= ctx->total_verses) global_idx = ctx->total_verses-1;
|
||||
@@ -769,6 +853,7 @@ static void jump_to_global(AppCtx* ctx, int global_idx) {
|
||||
|
||||
static void jump_to_book(AppCtx* ctx, int book_idx) {
|
||||
if (book_idx < 0 || book_idx >= ctx->book_count) return;
|
||||
ctx->favorite_navigation_active = false;
|
||||
// compute global start of book
|
||||
int global = 0;
|
||||
for (int i=0;i<book_idx;i++) global += ctx->books[i].verse_count;
|
||||
@@ -779,7 +864,10 @@ static void jump_to_book(AppCtx* ctx, int book_idx) {
|
||||
static void on_auto_timer(lv_timer_t* t) {
|
||||
AppCtx* ctx = (AppCtx*)lv_timer_get_user_data(t);
|
||||
if (ctx->auto_paused) return;
|
||||
if (ctx->cur_global < ctx->total_verses-1) {
|
||||
bool has_next = ctx->favorite_navigation_active
|
||||
? ctx->favorite_dial_idx < ctx->favorite_count - 1
|
||||
: ctx->cur_global < ctx->total_verses - 1;
|
||||
if (has_next) {
|
||||
advance_verse(ctx, 1);
|
||||
} else {
|
||||
ctx->auto_paused = true;
|
||||
@@ -803,6 +891,51 @@ static void book_fade_anim_cb(void* var, int32_t v) {
|
||||
|
||||
static void book_dial_update_center(AppCtx* ctx) {
|
||||
if (!ctx->book_center_name) return;
|
||||
if (ctx->browsing_favorites) {
|
||||
if (ctx->favorite_count <= 0) {
|
||||
lv_label_set_text(ctx->book_center_name, "No favorites yet");
|
||||
lv_label_set_text(ctx->book_center_verses, "Mark a verse with the check button, then return here.");
|
||||
lv_label_set_text(ctx->book_center_num, "0 / 0");
|
||||
lv_slider_set_range(ctx->book_slider, 0, 0);
|
||||
lv_slider_set_value(ctx->book_slider, 0, LV_ANIM_OFF);
|
||||
lv_obj_set_style_text_color(ctx->book_center_name, lv_color_hex(0xF5C16C), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->favorite_dial_idx < 0) ctx->favorite_dial_idx = 0;
|
||||
if (ctx->favorite_dial_idx >= ctx->favorite_count) ctx->favorite_dial_idx = ctx->favorite_count - 1;
|
||||
int global_idx = ctx->favorite_indices[ctx->favorite_dial_idx];
|
||||
int remaining = global_idx;
|
||||
int book_idx = 0;
|
||||
while (book_idx < ctx->book_count - 1 &&
|
||||
remaining >= ctx->books[book_idx].verse_count) {
|
||||
remaining -= ctx->books[book_idx].verse_count;
|
||||
book_idx++;
|
||||
}
|
||||
|
||||
const char* verse_text = "Unable to load verse";
|
||||
char ref[64];
|
||||
snprintf(ref, sizeof(ref), "%s", ctx->books[book_idx].bname);
|
||||
if (load_book(ctx, ctx->books[book_idx].bnumber) &&
|
||||
remaining >= 0 && remaining < ctx->book_idx_count) {
|
||||
VerseIndex* vi = &ctx->book_idx[remaining];
|
||||
snprintf(ref, sizeof(ref), "%s %d:%d", ctx->books[book_idx].bname, vi->cnum, vi->vnum);
|
||||
if (vi->offset < ctx->book_bin_size) {
|
||||
verse_text = (const char*)(ctx->book_bin + vi->offset);
|
||||
}
|
||||
}
|
||||
|
||||
lv_label_set_text(ctx->book_center_name, ref);
|
||||
lv_label_set_text(ctx->book_center_verses, verse_text);
|
||||
char count[24];
|
||||
snprintf(count, sizeof(count), "%d / %d", ctx->favorite_dial_idx + 1, ctx->favorite_count);
|
||||
lv_label_set_text(ctx->book_center_num, count);
|
||||
lv_slider_set_range(ctx->book_slider, 0, ctx->favorite_count - 1);
|
||||
lv_slider_set_value(ctx->book_slider, ctx->favorite_dial_idx, LV_ANIM_OFF);
|
||||
lv_obj_set_style_text_color(ctx->book_center_name, lv_color_hex(0xF5C16C), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->book_dial_idx <0) ctx->book_dial_idx=0;
|
||||
if (ctx->book_dial_idx >= ctx->book_count) ctx->book_dial_idx = ctx->book_count-1;
|
||||
BookInfo* b = &ctx->books[ctx->book_dial_idx];
|
||||
@@ -835,6 +968,7 @@ static void book_dial_update_center(AppCtx* ctx) {
|
||||
lv_obj_set_style_text_color(ctx->book_center_name, lv_color_hex(col), 0);
|
||||
|
||||
if (ctx->book_slider) {
|
||||
lv_slider_set_range(ctx->book_slider, 0, ctx->book_count>0 ? ctx->book_count-1 : 0);
|
||||
// avoid recursive event loop - set without anim, but we add subtle anim for drag
|
||||
lv_slider_set_value(ctx->book_slider, ctx->book_dial_idx, LV_ANIM_OFF);
|
||||
}
|
||||
@@ -843,7 +977,13 @@ static void book_dial_update_center(AppCtx* ctx) {
|
||||
static void set_book_browser_visible(AppCtx* ctx, bool visible) {
|
||||
ctx->book_browser_visible = visible;
|
||||
if (visible) {
|
||||
ctx->browsing_favorites = false;
|
||||
ctx->book_dial_idx = ctx->cur_book_idx;
|
||||
load_favorites(ctx);
|
||||
if (ctx->btn_favorites) {
|
||||
lv_obj_set_style_bg_color(ctx->btn_favorites, lv_color_hex(0x252538), 0);
|
||||
lv_obj_set_style_text_color(ctx->btn_favorites, lv_color_hex(0xC4C4D4), 0);
|
||||
}
|
||||
book_dial_update_center(ctx);
|
||||
if (ctx->book_browser) {
|
||||
lv_obj_remove_flag(ctx->book_browser, LV_OBJ_FLAG_HIDDEN);
|
||||
@@ -890,20 +1030,65 @@ static void on_books_close_click(lv_event_t* e) {
|
||||
}
|
||||
static void on_book_center_click(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
jump_to_book(ctx, ctx->book_dial_idx);
|
||||
if (ctx->browsing_favorites) {
|
||||
if (ctx->favorite_count > 0) {
|
||||
jump_to_global(ctx, ctx->favorite_indices[ctx->favorite_dial_idx]);
|
||||
}
|
||||
} else {
|
||||
jump_to_book(ctx, ctx->book_dial_idx);
|
||||
}
|
||||
}
|
||||
static void on_dial_prev_click(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
if (ctx->book_dial_idx>0) { ctx->book_dial_idx--; book_dial_update_center(ctx); }
|
||||
if (ctx->browsing_favorites) {
|
||||
if (ctx->favorite_dial_idx > 0) {
|
||||
ctx->favorite_dial_idx--;
|
||||
book_dial_update_center(ctx);
|
||||
}
|
||||
} else if (ctx->book_dial_idx>0) {
|
||||
ctx->book_dial_idx--;
|
||||
book_dial_update_center(ctx);
|
||||
}
|
||||
}
|
||||
static void on_dial_next_click(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
if (ctx->book_dial_idx < ctx->book_count-1) { ctx->book_dial_idx++; book_dial_update_center(ctx); }
|
||||
if (ctx->browsing_favorites) {
|
||||
if (ctx->favorite_dial_idx < ctx->favorite_count - 1) {
|
||||
ctx->favorite_dial_idx++;
|
||||
book_dial_update_center(ctx);
|
||||
}
|
||||
} else if (ctx->book_dial_idx < ctx->book_count-1) {
|
||||
ctx->book_dial_idx++;
|
||||
book_dial_update_center(ctx);
|
||||
}
|
||||
}
|
||||
static void on_slider_changed(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
int v = lv_slider_get_value(ctx->book_slider);
|
||||
if (v!=ctx->book_dial_idx) { ctx->book_dial_idx=v; book_dial_update_center(ctx); }
|
||||
if (ctx->browsing_favorites) {
|
||||
if (v != ctx->favorite_dial_idx) {
|
||||
ctx->favorite_dial_idx = v;
|
||||
book_dial_update_center(ctx);
|
||||
}
|
||||
} else if (v!=ctx->book_dial_idx) {
|
||||
ctx->book_dial_idx=v;
|
||||
book_dial_update_center(ctx);
|
||||
}
|
||||
}
|
||||
static void on_favorites_click(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
load_favorites(ctx);
|
||||
if (ctx->favorite_count <= 0) {
|
||||
ctx->browsing_favorites = true;
|
||||
book_dial_update_center(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
int current_pos = find_favorite_position(ctx, ctx->cur_global);
|
||||
ctx->favorite_dial_idx = current_pos >= 0 ? current_pos : 0;
|
||||
ctx->favorite_navigation_active = true;
|
||||
ctx->browsing_favorites = false;
|
||||
jump_to_global(ctx, ctx->favorite_indices[ctx->favorite_dial_idx]);
|
||||
}
|
||||
static void on_swipe_area_event(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
@@ -919,11 +1104,15 @@ static void on_swipe_area_event(lv_event_t* e) {
|
||||
int thresh = 28; // px per book
|
||||
if (abs(dx) >= thresh) {
|
||||
int steps = dx / thresh;
|
||||
int new_idx = ctx->book_dial_idx - steps; // swipe left -> next
|
||||
int old_idx = ctx->browsing_favorites ? ctx->favorite_dial_idx : ctx->book_dial_idx;
|
||||
int max_idx = ctx->browsing_favorites ? ctx->favorite_count - 1 : ctx->book_count - 1;
|
||||
if (max_idx < 0) max_idx = 0;
|
||||
int new_idx = old_idx - steps; // swipe left -> next
|
||||
if (new_idx<0) new_idx=0;
|
||||
if (new_idx>=ctx->book_count) new_idx=ctx->book_count-1;
|
||||
if (new_idx!=ctx->book_dial_idx) {
|
||||
ctx->book_dial_idx = new_idx;
|
||||
if (new_idx>max_idx) new_idx=max_idx;
|
||||
if (new_idx!=old_idx) {
|
||||
if (ctx->browsing_favorites) ctx->favorite_dial_idx = new_idx;
|
||||
else ctx->book_dial_idx = new_idx;
|
||||
book_dial_update_center(ctx);
|
||||
}
|
||||
ctx->book_drag_start_x = p.x; // reset for next step
|
||||
@@ -992,6 +1181,9 @@ static void build_book_browser(AppCtx* ctx, lv_obj_t* parent) {
|
||||
|
||||
ctx->book_center_verses = lv_label_create(mid_wrap);
|
||||
lv_label_set_text(ctx->book_center_verses, "—");
|
||||
lv_obj_set_width(ctx->book_center_verses, LV_PCT(100));
|
||||
lv_obj_set_height(ctx->book_center_verses, 72);
|
||||
lv_label_set_long_mode(ctx->book_center_verses, LV_LABEL_LONG_MODE_DOTS);
|
||||
lv_obj_set_style_text_color(ctx->book_center_verses, lv_color_hex(0x5A5A78), 0);
|
||||
lv_obj_set_style_text_font(ctx->book_center_verses, lvgl_get_text_font(FONT_SIZE_SMALL), 0);
|
||||
lv_obj_set_style_text_align(ctx->book_center_verses, LV_TEXT_ALIGN_CENTER, 0);
|
||||
@@ -1005,7 +1197,7 @@ static void build_book_browser(AppCtx* ctx, lv_obj_t* parent) {
|
||||
lv_obj_t* l_r = lv_label_create(btn_r); lv_label_set_text(l_r, LV_SYMBOL_NEXT); lv_obj_center(l_r);
|
||||
lv_obj_add_event_cb(btn_r, on_dial_next_click, LV_EVENT_CLICKED, ctx);
|
||||
|
||||
// Bottom bar: [X cancel] [slider] [N / 66]
|
||||
// Bottom bar: [X cancel] [Fav] [slider] [N / 66]
|
||||
lv_obj_t* bottom = lv_obj_create(ctx->book_browser);
|
||||
lv_obj_set_size(bottom, LV_PCT(100), 56);
|
||||
lv_obj_align(bottom, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||
@@ -1029,6 +1221,17 @@ static void build_book_browser(AppCtx* ctx, lv_obj_t* parent) {
|
||||
lv_obj_t* l_cancel = lv_label_create(btn_cancel); lv_label_set_text(l_cancel, LV_SYMBOL_CLOSE); lv_obj_center(l_cancel);
|
||||
lv_obj_add_event_cb(btn_cancel, on_books_close_click, LV_EVENT_CLICKED, ctx);
|
||||
|
||||
ctx->btn_favorites = lv_button_create(bottom);
|
||||
lv_obj_set_size(ctx->btn_favorites, 44, 32);
|
||||
lv_obj_set_style_radius(ctx->btn_favorites, 8, 0);
|
||||
lv_obj_set_style_bg_color(ctx->btn_favorites, lv_color_hex(0x252538), 0);
|
||||
lv_obj_set_style_text_color(ctx->btn_favorites, lv_color_hex(0xC4C4D4), 0);
|
||||
lv_obj_t* l_favorites = lv_label_create(ctx->btn_favorites);
|
||||
lv_label_set_text(l_favorites, "Fav");
|
||||
lv_obj_set_style_text_font(l_favorites, lvgl_get_text_font(FONT_SIZE_SMALL), 0);
|
||||
lv_obj_center(l_favorites);
|
||||
lv_obj_add_event_cb(ctx->btn_favorites, on_favorites_click, LV_EVENT_CLICKED, ctx);
|
||||
|
||||
ctx->book_slider = lv_slider_create(bottom);
|
||||
lv_obj_set_flex_grow(ctx->book_slider, 1);
|
||||
lv_obj_set_height(ctx->book_slider, 8);
|
||||
@@ -1052,8 +1255,35 @@ static void build_book_browser(AppCtx* ctx, lv_obj_t* parent) {
|
||||
}
|
||||
|
||||
/* ── UI callbacks ── */
|
||||
static void on_verse_pointer_event(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
lv_event_code_t code = lv_event_get_code(e);
|
||||
lv_indev_t* indev = lv_indev_active();
|
||||
if (!indev) return;
|
||||
lv_point_t p;
|
||||
lv_indev_get_point(indev, &p);
|
||||
if (code == LV_EVENT_PRESSED) {
|
||||
ctx->verse_drag_start_x = p.x;
|
||||
ctx->verse_drag_start_y = p.y;
|
||||
ctx->verse_dragging = true;
|
||||
ctx->verse_swipe_consumed = false;
|
||||
} else if ((code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) && ctx->verse_dragging) {
|
||||
int dx = p.x - ctx->verse_drag_start_x;
|
||||
int dy = p.y - ctx->verse_drag_start_y;
|
||||
ctx->verse_dragging = false;
|
||||
if (abs(dx) >= 40 && abs(dx) > abs(dy)) {
|
||||
ctx->verse_swipe_consumed = true;
|
||||
manually_advance_verse(ctx, dx < 0 ? 1 : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void on_verse_tap(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
if (ctx->verse_swipe_consumed) {
|
||||
ctx->verse_swipe_consumed = false;
|
||||
return;
|
||||
}
|
||||
toggle_ui(ctx);
|
||||
}
|
||||
|
||||
@@ -1103,11 +1333,11 @@ static void set_chrome_visible(AppCtx* ctx, bool visible) {
|
||||
|
||||
static void on_prev_click(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
advance_verse(ctx, -1);
|
||||
manually_advance_verse(ctx, -1);
|
||||
}
|
||||
static void on_next_click(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
advance_verse(ctx, 1);
|
||||
manually_advance_verse(ctx, 1);
|
||||
}
|
||||
static void on_play_pause_click(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
@@ -1120,6 +1350,17 @@ static void on_play_pause_click(lv_event_t* e) {
|
||||
static void on_highlight_click(lv_event_t* e) {
|
||||
AppCtx* ctx = (AppCtx*)lv_event_get_user_data(e);
|
||||
toggle_favorite(ctx);
|
||||
if (ctx->favorite_navigation_active && !ctx->is_highlighted) {
|
||||
if (ctx->favorite_count <= 0) {
|
||||
ctx->favorite_navigation_active = false;
|
||||
} else {
|
||||
if (ctx->favorite_dial_idx >= ctx->favorite_count) {
|
||||
ctx->favorite_dial_idx = ctx->favorite_count - 1;
|
||||
}
|
||||
ctx->cur_global = ctx->favorite_indices[ctx->favorite_dial_idx];
|
||||
if (ctx->auto_timer) lv_timer_reset(ctx->auto_timer);
|
||||
}
|
||||
}
|
||||
// refresh highlight visuals without reloading
|
||||
show_current_verse(ctx);
|
||||
}
|
||||
@@ -1198,6 +1439,9 @@ static void onShowApp(AppHandle app, void* data, lv_obj_t* parent) {
|
||||
lv_obj_set_style_pad_all(g_ctx.verse_wrapper, 0, 0);
|
||||
lv_obj_remove_flag(g_ctx.verse_wrapper, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_flag(g_ctx.verse_wrapper, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(g_ctx.verse_wrapper, on_verse_pointer_event, LV_EVENT_PRESSED, &g_ctx);
|
||||
lv_obj_add_event_cb(g_ctx.verse_wrapper, on_verse_pointer_event, LV_EVENT_RELEASED, &g_ctx);
|
||||
lv_obj_add_event_cb(g_ctx.verse_wrapper, on_verse_pointer_event, LV_EVENT_PRESS_LOST, &g_ctx);
|
||||
lv_obj_add_event_cb(g_ctx.verse_wrapper, on_verse_tap, LV_EVENT_CLICKED, &g_ctx);
|
||||
|
||||
// Center reading column with generous breathing room - avoid top chrome crop
|
||||
@@ -1213,6 +1457,9 @@ static void onShowApp(AppHandle app, void* data, lv_obj_t* parent) {
|
||||
lv_obj_set_style_pad_all(center_col, 0, 0);
|
||||
lv_obj_remove_flag(center_col, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_flag(center_col, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(center_col, on_verse_pointer_event, LV_EVENT_PRESSED, &g_ctx);
|
||||
lv_obj_add_event_cb(center_col, on_verse_pointer_event, LV_EVENT_RELEASED, &g_ctx);
|
||||
lv_obj_add_event_cb(center_col, on_verse_pointer_event, LV_EVENT_PRESS_LOST, &g_ctx);
|
||||
lv_obj_add_event_cb(center_col, on_verse_tap, LV_EVENT_CLICKED, &g_ctx);
|
||||
|
||||
g_ctx.lbl_verse = lv_label_create(center_col);
|
||||
@@ -1324,7 +1571,7 @@ static void onShowApp(AppHandle app, void* data, lv_obj_t* parent) {
|
||||
lv_obj_set_style_radius(g_ctx.btn_highlight, 10, 0);
|
||||
lv_obj_set_style_bg_color(g_ctx.btn_highlight, lv_color_hex(0x252538), 0);
|
||||
lv_obj_set_style_text_color(g_ctx.btn_highlight, lv_color_hex(0x8C8CB0), 0);
|
||||
lbl = lv_label_create(g_ctx.btn_highlight); lv_label_set_text(lbl, LV_SYMBOL_DUMMY); lv_obj_center(lbl);
|
||||
lbl = lv_label_create(g_ctx.btn_highlight); lv_label_set_text(lbl, LV_SYMBOL_PLUS); lv_obj_center(lbl);
|
||||
lv_obj_add_event_cb(g_ctx.btn_highlight, on_highlight_click, LV_EVENT_CLICKED, &g_ctx);
|
||||
|
||||
set_chrome_visible(&g_ctx, true); // start with menu showing per user request
|
||||
|
||||
Reference in New Issue
Block a user