feat: add scroll bar to Music app
This commit is contained in:
+102
-10
@@ -39,6 +39,8 @@ static constexpr int FONT_SIZE_LG = 20;
|
||||
|
||||
static constexpr int LIST_TOP = TOOLBAR_H;
|
||||
static constexpr int LIST_ITEM_H = 32;
|
||||
static constexpr int LIST_SB_W = 12;
|
||||
static constexpr int LIST_SB_MIN_TH = 20;
|
||||
static constexpr int INFO_H = 56;
|
||||
static constexpr int PROGRESS_H = 24;
|
||||
static constexpr int TRANSPORT_H = 44;
|
||||
@@ -144,6 +146,13 @@ struct PlayerState {
|
||||
// Progress dragging
|
||||
bool dragging_progress;
|
||||
|
||||
// List scrollbar
|
||||
bool sb_dragging;
|
||||
int sb_drag_offset;
|
||||
bool sb_hovered;
|
||||
int mouse_x;
|
||||
int mouse_y;
|
||||
|
||||
// Transport icons (dark for normal, white for active/colored backgrounds)
|
||||
SvgIcon ico_rewind;
|
||||
SvgIcon ico_play;
|
||||
@@ -1047,6 +1056,26 @@ static int visible_items() {
|
||||
return count > 0 ? count : 0;
|
||||
}
|
||||
|
||||
static bool list_scrollbar_visible() {
|
||||
return g.filtered_count > visible_items() && visible_items() > 0;
|
||||
}
|
||||
|
||||
static void list_scrollbar_rects(int W, Rect* track, Rect* thumb) {
|
||||
int list_h = list_bottom() - LIST_TOP;
|
||||
int visible = visible_items();
|
||||
int tx = W - LIST_SB_W;
|
||||
*track = {tx, LIST_TOP, LIST_SB_W, list_h};
|
||||
|
||||
int max_scroll = g.filtered_count - visible;
|
||||
if (max_scroll < 1) max_scroll = 1;
|
||||
int th = (visible * list_h) / g.filtered_count;
|
||||
if (th < LIST_SB_MIN_TH) th = LIST_SB_MIN_TH;
|
||||
if (th > list_h) th = list_h;
|
||||
int range = list_h - th;
|
||||
int ty = LIST_TOP + (g.scroll_y * range) / max_scroll;
|
||||
*thumb = {tx + 1, ty, LIST_SB_W - 2, th};
|
||||
}
|
||||
|
||||
static Rect content_panel_rect() {
|
||||
int y = list_bottom() + 1;
|
||||
return {0, LIST_TOP, g.win_w, y - LIST_TOP};
|
||||
@@ -1135,6 +1164,10 @@ static void render(uint32_t* pixels) {
|
||||
px_hline(pixels, W, H, 0, list_end, W, BORDER_COLOR);
|
||||
|
||||
if (!g.show_visualizer) {
|
||||
bool sb_vis = list_scrollbar_visible();
|
||||
int row_w = sb_vis ? (W - LIST_SB_W) : W;
|
||||
int tag_right = sb_vis ? (W - LIST_SB_W) : W;
|
||||
|
||||
for (int i = 0; i < visible_count && (i + g.scroll_y) < g.filtered_count; i++) {
|
||||
int idx = filtered_raw_at(i + g.scroll_y);
|
||||
if (idx < 0) break;
|
||||
@@ -1149,7 +1182,7 @@ static void render(uint32_t* pixels) {
|
||||
bg = g_list_hover;
|
||||
|
||||
if (bg.r != BG_COLOR.r || bg.g != BG_COLOR.g || bg.b != BG_COLOR.b)
|
||||
px_fill(pixels, W, H, 0, iy, W, LIST_ITEM_H, bg);
|
||||
px_fill(pixels, W, H, 0, iy, row_w, LIST_ITEM_H, bg);
|
||||
|
||||
if (idx == g.selected_item && !(idx == g.current_track && g.play_state != PlayState::Stopped))
|
||||
px_fill(pixels, W, H, 0, iy, 3, LIST_ITEM_H, g_accent);
|
||||
@@ -1168,16 +1201,19 @@ static void render(uint32_t* pixels) {
|
||||
|
||||
const char* tag = g.files[idx].is_mp3 ? "MP3" : "WAV";
|
||||
int tw = text_w(tag, FONT_SIZE_SM);
|
||||
px_text(pixels, W, H, W - tw - 12, iy + (LIST_ITEM_H - fh_sm) / 2,
|
||||
px_text(pixels, W, H, tag_right - tw - 12, iy + (LIST_ITEM_H - fh_sm) / 2,
|
||||
tag, DIM_TEXT, FONT_SIZE_SM);
|
||||
}
|
||||
|
||||
if (g.filtered_count > visible_count && visible_count > 0) {
|
||||
int list_h = list_end - LIST_TOP;
|
||||
int sb_h = (visible_count * list_h) / g.filtered_count;
|
||||
if (sb_h < 20) sb_h = 20;
|
||||
int sb_y = LIST_TOP + (g.scroll_y * (list_h - sb_h)) / (g.filtered_count - visible_count);
|
||||
px_fill_rounded(pixels, W, H, W - 6, sb_y, 4, sb_h, 2, TRACK_BG);
|
||||
if (sb_vis) {
|
||||
Rect track, thumb;
|
||||
list_scrollbar_rects(W, &track, &thumb);
|
||||
px_fill(pixels, W, H, track.x, track.y, track.w, track.h,
|
||||
Color::from_rgb(0xF0, 0xF0, 0xF0));
|
||||
Color tc = (g.sb_hovered || g.sb_dragging)
|
||||
? Color::from_rgb(0xA0, 0xA0, 0xA0)
|
||||
: Color::from_rgb(0xC0, 0xC0, 0xC0);
|
||||
px_fill(pixels, W, H, thumb.x, thumb.y, thumb.w, thumb.h, tc);
|
||||
}
|
||||
|
||||
if (g.filtered_count == 0) {
|
||||
@@ -1620,10 +1656,64 @@ extern "C" void _start() {
|
||||
|
||||
// Mouse
|
||||
if (ev.type == 1) {
|
||||
g.mouse_x = ev.mouse.x;
|
||||
g.mouse_y = ev.mouse.y;
|
||||
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
|
||||
bool released = !(ev.mouse.buttons & 1) && (ev.mouse.prev_buttons & 1);
|
||||
bool held = (ev.mouse.buttons & 1) != 0;
|
||||
|
||||
if (clicked) {
|
||||
// List scrollbar drag/hover
|
||||
bool sb_consume = false;
|
||||
if (!g.show_visualizer && list_scrollbar_visible()) {
|
||||
Rect sb_track, sb_thumb;
|
||||
list_scrollbar_rects(g.win_w, &sb_track, &sb_thumb);
|
||||
bool in_track = sb_track.contains(ev.mouse.x, ev.mouse.y);
|
||||
|
||||
if (g.sb_dragging && held) {
|
||||
int list_h = sb_track.h;
|
||||
int range = list_h - sb_thumb.h;
|
||||
int max_scroll = g.filtered_count - visible_items();
|
||||
if (range > 0 && max_scroll > 0) {
|
||||
int new_ty = ev.mouse.y - g.sb_drag_offset;
|
||||
int rel = new_ty - LIST_TOP;
|
||||
if (rel < 0) rel = 0;
|
||||
if (rel > range) rel = range;
|
||||
int new_scroll = (rel * max_scroll + range / 2) / range;
|
||||
if (new_scroll != g.scroll_y) {
|
||||
g.scroll_y = new_scroll;
|
||||
clamp_list_scroll();
|
||||
redraw = true;
|
||||
}
|
||||
}
|
||||
sb_consume = true;
|
||||
} else if (g.sb_dragging && released) {
|
||||
g.sb_dragging = false;
|
||||
redraw = true;
|
||||
sb_consume = true;
|
||||
} else if (clicked && in_track) {
|
||||
if (sb_thumb.contains(ev.mouse.x, ev.mouse.y)) {
|
||||
g.sb_dragging = true;
|
||||
g.sb_drag_offset = ev.mouse.y - sb_thumb.y;
|
||||
} else {
|
||||
int visible = visible_items();
|
||||
int delta = visible > 1 ? visible - 1 : 1;
|
||||
if (ev.mouse.y < sb_thumb.y) g.scroll_y -= delta;
|
||||
else g.scroll_y += delta;
|
||||
clamp_list_scroll();
|
||||
}
|
||||
redraw = true;
|
||||
sb_consume = true;
|
||||
}
|
||||
if (in_track != g.sb_hovered) {
|
||||
g.sb_hovered = in_track;
|
||||
redraw = true;
|
||||
}
|
||||
} else if (g.sb_hovered) {
|
||||
g.sb_hovered = false;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
if (clicked && !sb_consume) {
|
||||
if (handle_click(ev.mouse.x, ev.mouse.y))
|
||||
redraw = true;
|
||||
}
|
||||
@@ -1645,7 +1735,9 @@ extern "C" void _start() {
|
||||
|
||||
// Hover tracking for file list
|
||||
int lb = list_bottom();
|
||||
if (!g.show_visualizer && ev.mouse.y >= LIST_TOP && ev.mouse.y < lb) {
|
||||
int hover_right = list_scrollbar_visible() ? (g.win_w - LIST_SB_W) : g.win_w;
|
||||
if (!g.show_visualizer && ev.mouse.y >= LIST_TOP && ev.mouse.y < lb
|
||||
&& ev.mouse.x >= 0 && ev.mouse.x < hover_right) {
|
||||
int new_hover = filtered_raw_at((ev.mouse.y - LIST_TOP) / LIST_ITEM_H + g.scroll_y);
|
||||
if (new_hover != g.hovered_item) {
|
||||
g.hovered_item = new_hover;
|
||||
|
||||
Reference in New Issue
Block a user