Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 201 additions & 34 deletions custom/ui/pages/ui_page_media.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,70 +5,237 @@
*/
#include "ui_page_media.h"

#include "../ui_theme.h"
#include "../ui_wallpaper.h"
#include "../widgets/ui_room_card.h"

static void ui_page_media_delete_cb(lv_event_t *event)
static void ui_page_media_delete_cb(lv_event_t* event)
{
ui_wallpaper_t *wallpaper = (ui_wallpaper_t *)lv_event_get_user_data(event);
ui_wallpaper_t* wallpaper = (ui_wallpaper_t*)lv_event_get_user_data(event);
ui_wallpaper_detach(wallpaper);
}

static lv_obj_t *ui_page_create_content(lv_obj_t *page, const char *title_text)
static lv_obj_t* ui_page_create_content(lv_obj_t* page, const char* title_text)
{
lv_obj_t *content = lv_obj_create(page);
LV_UNUSED(title_text);

lv_obj_t* content = lv_obj_create(page);
lv_obj_remove_style_all(content);
lv_obj_set_size(content, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_bg_opa(content, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_style_pad_left(content, 192, LV_PART_MAIN);
lv_obj_set_style_pad_right(content, 48, LV_PART_MAIN);
lv_obj_set_style_pad_top(content, 40, LV_PART_MAIN);
lv_obj_set_style_pad_bottom(content, 40, LV_PART_MAIN);
lv_obj_set_style_pad_row(content, 32, LV_PART_MAIN);
lv_obj_set_style_pad_all(content, 48, LV_PART_MAIN);
lv_obj_set_style_pad_gap(content, 32, LV_PART_MAIN);
lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(content, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);

lv_obj_t *title = lv_obj_create(content);
lv_obj_remove_style_all(title);
lv_obj_set_width(title, LV_PCT(100));
lv_obj_set_style_bg_color(title, lv_color_hex(0x171f2b), LV_PART_MAIN);
lv_obj_set_style_bg_opa(title, LV_OPA_80, LV_PART_MAIN);
lv_obj_set_style_radius(title, 16, LV_PART_MAIN);
lv_obj_set_style_shadow_width(title, 24, LV_PART_MAIN);
lv_obj_set_style_shadow_opa(title, LV_OPA_50, LV_PART_MAIN);
lv_obj_set_style_shadow_ofs_y(title, 10, LV_PART_MAIN);
lv_obj_set_style_border_width(title, 0, LV_PART_MAIN);
lv_obj_set_style_pad_all(title, 28, LV_PART_MAIN);

lv_obj_t *label = lv_label_create(title);
lv_label_set_text(label, title_text);
lv_obj_set_width(label, LV_PCT(100));
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN);
lv_obj_set_style_text_font(label, &lv_font_montserrat_32, LV_PART_MAIN);
lv_obj_set_style_text_color(label, lv_color_hex(0xf8fafc), LV_PART_MAIN);
lv_obj_clear_flag(content, LV_OBJ_FLAG_SCROLLABLE);
Comment on lines +22 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Allow page content to grow so media cards can scroll

The new layout stacks two full ui_room_card instances and a 240 px album art area, but the content container remains hard‑sized to LV_PCT(100) height and is marked non‑scrollable. When the stacked card heights exceed the display height (e.g. on Tab5’s 540 px panel), the second card and lower controls are clipped and unreachable because the parent page never enlarges its scrollable area. Consider letting content use LV_SIZE_CONTENT (or enabling scrolling on it) so the page can actually scroll through tall media sections.

Useful? React with 👍 / 👎.


return content;
}

lv_obj_t *ui_page_media_create(lv_obj_t *parent)
lv_obj_t* ui_page_media_create(lv_obj_t* parent)
{
if (parent == NULL) {
if (parent == NULL)
{
return NULL;
}

lv_obj_t *page = lv_obj_create(parent);
lv_obj_t* page = lv_obj_create(parent);
lv_obj_remove_style_all(page);
lv_obj_set_size(page, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_bg_opa(page, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_scroll_dir(page, LV_DIR_VER);
lv_obj_set_scrollbar_mode(page, LV_SCROLLBAR_MODE_OFF);
lv_obj_add_flag(page, LV_OBJ_FLAG_CLICKABLE);

ui_wallpaper_t *wallpaper = ui_wallpaper_attach(page);
if (wallpaper != NULL) {
ui_wallpaper_t* wallpaper = ui_wallpaper_attach(page);
if (wallpaper != NULL)
{
lv_obj_add_event_cb(page, ui_page_media_delete_cb, LV_EVENT_DELETE, wallpaper);
}

ui_page_create_content(page, "TV Controls");
lv_obj_t* content = ui_page_create_content(page, "Media");

ui_room_card_config_t now_playing_config = {
.room_id = "media.now_playing",
.title = "Now Playing",
.icon_text = LV_SYMBOL_AUDIO,
};

ui_room_card_t* now_playing_card = ui_room_card_create(content, &now_playing_config);
if (now_playing_card != NULL)
{
lv_obj_t* card_obj = ui_room_card_get_obj(now_playing_card);
if (card_obj != NULL)
{
lv_obj_t* toggle = ui_room_card_get_toggle(now_playing_card);
if (toggle != NULL)
Comment on lines +64 to +71

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Clear scrollable flag on room cards to keep page scroll responsive

Both media cards are created via ui_room_card_create but the code never clears LV_OBJ_FLAG_SCROLLABLE on their container objects. ui_room_card leaves the flag set by default, so vertical swipe gestures on the card try to scroll the card itself rather than the page, effectively blocking page scrolling even when the page has overflow. Other pages (e.g. the Rooms page) explicitly call lv_obj_clear_flag(card_obj, LV_OBJ_FLAG_SCROLLABLE) after card creation; the media page should do the same for now_playing_card and the quick scenes card.

Useful? React with 👍 / 👎.

{
lv_obj_add_flag(toggle, LV_OBJ_FLAG_HIDDEN);
}

lv_obj_t* specs = lv_obj_get_child(card_obj, 2);
if (specs != NULL)
{
lv_obj_add_flag(specs, LV_OBJ_FLAG_HIDDEN);
}

lv_obj_t* info_row = lv_obj_create(card_obj);
lv_obj_remove_style_all(info_row);
lv_obj_set_width(info_row, LV_PCT(100));
lv_obj_set_style_bg_opa(info_row, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_style_pad_gap(info_row, 24, LV_PART_MAIN);
lv_obj_set_flex_flow(info_row, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(
info_row, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_clear_flag(info_row, LV_OBJ_FLAG_SCROLLABLE);

lv_obj_t* album_art = lv_obj_create(info_row);
lv_obj_remove_style_all(album_art);
lv_obj_set_size(album_art, 240, 240);
lv_obj_set_style_bg_color(album_art, ui_theme_color_surface(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(album_art, LV_OPA_70, LV_PART_MAIN);
lv_obj_set_style_radius(album_art, 16, LV_PART_MAIN);
lv_obj_set_style_border_width(album_art, 0, LV_PART_MAIN);
lv_obj_clear_flag(album_art, LV_OBJ_FLAG_SCROLLABLE);

lv_obj_t* track_info = lv_obj_create(info_row);
lv_obj_remove_style_all(track_info);
lv_obj_set_width(track_info, LV_PCT(100));
lv_obj_set_style_bg_opa(track_info, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_style_pad_gap(track_info, 12, LV_PART_MAIN);
lv_obj_set_flex_flow(track_info, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(
track_info, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_clear_flag(track_info, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_flex_grow(track_info, 1);

lv_obj_t* track_title = lv_label_create(track_info);
lv_label_set_text(track_title, "Placeholder Track");
lv_obj_set_style_text_font(track_title, &lv_font_montserrat_26, LV_PART_MAIN);
lv_obj_set_style_text_color(track_title, ui_theme_color_on_surface(), LV_PART_MAIN);
lv_label_set_long_mode(track_title, LV_LABEL_LONG_WRAP);
lv_obj_set_width(track_title, LV_PCT(100));

lv_obj_t* track_artist = lv_label_create(track_info);
lv_label_set_text(track_artist, "Artist Name");
lv_obj_set_style_text_font(track_artist, &lv_font_montserrat_20, LV_PART_MAIN);
lv_obj_set_style_text_color(track_artist, ui_theme_color_muted(), LV_PART_MAIN);
lv_label_set_long_mode(track_artist, LV_LABEL_LONG_WRAP);
lv_obj_set_width(track_artist, LV_PCT(100));

lv_obj_t* track_source = lv_label_create(track_info);
lv_label_set_text(track_source, "Source · Placeholder");
lv_obj_set_style_text_font(track_source, &lv_font_montserrat_18, LV_PART_MAIN);
lv_obj_set_style_text_color(track_source, ui_theme_color_muted(), LV_PART_MAIN);
lv_label_set_long_mode(track_source, LV_LABEL_LONG_WRAP);
lv_obj_set_width(track_source, LV_PCT(100));

lv_obj_t* transport_row = lv_obj_create(card_obj);
lv_obj_remove_style_all(transport_row);
lv_obj_set_width(transport_row, LV_PCT(100));
lv_obj_set_style_bg_opa(transport_row, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_style_pad_gap(transport_row, 16, LV_PART_MAIN);
lv_obj_set_flex_flow(transport_row, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(
transport_row, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_clear_flag(transport_row, LV_OBJ_FLAG_SCROLLABLE);

const char* button_labels[] = {"Prev", "Play/Pause", "Next"};
for (size_t i = 0; i < LV_ARRAY_SIZE(button_labels); i++)
{
lv_obj_t* control_btn = lv_btn_create(transport_row);
lv_obj_remove_style_all(control_btn);
lv_obj_set_flex_grow(control_btn, 1);
lv_obj_set_height(control_btn, 56);
lv_obj_set_style_radius(control_btn, 18, LV_PART_MAIN);
lv_obj_set_style_bg_color(control_btn, ui_theme_color_accent(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(control_btn, LV_OPA_80, LV_PART_MAIN);
lv_obj_set_style_border_width(control_btn, 0, LV_PART_MAIN);
lv_obj_set_style_shadow_width(control_btn, 0, LV_PART_MAIN);
lv_obj_set_style_pad_hor(control_btn, 24, LV_PART_MAIN);
lv_obj_set_style_pad_ver(control_btn, 8, LV_PART_MAIN);
lv_obj_clear_flag(control_btn, LV_OBJ_FLAG_SCROLLABLE);

lv_obj_t* btn_label = lv_label_create(control_btn);
lv_label_set_text(btn_label, button_labels[i]);
lv_obj_center(btn_label);
lv_obj_set_style_text_font(btn_label, &lv_font_montserrat_18, LV_PART_MAIN);
lv_obj_set_style_text_color(btn_label, lv_color_white(), LV_PART_MAIN);
}

lv_obj_t* volume_slider = lv_slider_create(transport_row);
lv_obj_set_flex_grow(volume_slider, 2);
lv_obj_set_height(volume_slider, 36);
lv_slider_set_range(volume_slider, 0, 100);
lv_slider_set_value(volume_slider, 40, LV_ANIM_OFF);
lv_obj_clear_flag(volume_slider, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_bg_color(volume_slider, ui_theme_color_muted(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(volume_slider, LV_OPA_30, LV_PART_MAIN);
lv_obj_set_style_radius(volume_slider, 18, LV_PART_MAIN);
lv_obj_set_style_border_width(volume_slider, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(volume_slider, ui_theme_color_accent(), LV_PART_INDICATOR);
lv_obj_set_style_bg_opa(volume_slider, LV_OPA_COVER, LV_PART_INDICATOR);
lv_obj_set_style_radius(volume_slider, 18, LV_PART_INDICATOR);
}
}

ui_room_card_config_t scenes_config = {
.room_id = "media.quick_scenes",
.title = "Quick Scenes",
.icon_text = LV_SYMBOL_LIST,
};

ui_room_card_t* scenes_card = ui_room_card_create(content, &scenes_config);
if (scenes_card != NULL)
{
lv_obj_t* card_obj = ui_room_card_get_obj(scenes_card);
if (card_obj != NULL)
{
lv_obj_t* toggle = ui_room_card_get_toggle(scenes_card);
if (toggle != NULL)
{
lv_obj_add_flag(toggle, LV_OBJ_FLAG_HIDDEN);
}

lv_obj_t* specs = lv_obj_get_child(card_obj, 2);
if (specs != NULL)
{
lv_obj_add_flag(specs, LV_OBJ_FLAG_HIDDEN);
}

lv_obj_t* scene_grid = lv_obj_create(card_obj);
lv_obj_remove_style_all(scene_grid);
lv_obj_set_width(scene_grid, LV_PCT(100));
lv_obj_set_style_bg_opa(scene_grid, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_style_pad_gap(scene_grid, 16, LV_PART_MAIN);
lv_obj_set_flex_flow(scene_grid, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(
scene_grid, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
lv_obj_clear_flag(scene_grid, LV_OBJ_FLAG_SCROLLABLE);

const char* scene_labels[] = {"Morning", "Movie", "Night", "Party"};
for (size_t i = 0; i < LV_ARRAY_SIZE(scene_labels); i++)
{
lv_obj_t* scene_btn = lv_btn_create(scene_grid);
lv_obj_remove_style_all(scene_btn);
lv_obj_set_style_radius(scene_btn, 14, LV_PART_MAIN);
lv_obj_set_style_bg_color(scene_btn, ui_theme_color_surface(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(scene_btn, LV_OPA_60, LV_PART_MAIN);
lv_obj_set_style_border_width(scene_btn, 0, LV_PART_MAIN);
lv_obj_set_style_shadow_width(scene_btn, 0, LV_PART_MAIN);
lv_obj_set_style_pad_hor(scene_btn, 24, LV_PART_MAIN);
lv_obj_set_style_pad_ver(scene_btn, 16, LV_PART_MAIN);
lv_obj_set_flex_grow(scene_btn, 1);
lv_obj_clear_flag(scene_btn, LV_OBJ_FLAG_SCROLLABLE);

lv_obj_t* label = lv_label_create(scene_btn);
lv_label_set_text(label, scene_labels[i]);
lv_obj_center(label);
lv_obj_set_style_text_font(label, &lv_font_montserrat_18, LV_PART_MAIN);
lv_obj_set_style_text_color(label, ui_theme_color_on_surface(), LV_PART_MAIN);
}
}
}

return page;
}
Loading