Share bottom bar menus across full-width firmware pages

This commit is contained in:
liu weikai committed 2026-10-15 15:22:21 +08:00
1 parent 9a6b7bd62d
commit efcd3f8f7c
48 files changed
+932 -170

No files matched your search

@@ -14,5 +14,7 @@ struct Footer
Footer create(lv_obj_t* parent);
void refresh(Footer& footer);
// Build the same information on demand inside a bottom-menu Info page.
void populate_info(lv_obj_t* parent);
} // namespace ui::components::air_status_footer
@@ -0,0 +1,54 @@
#pragma once
#include "lvgl.h"
namespace ui::components::bottom_bar
{
// A page owns its widgets. These helpers only provide shared taskbar/menu chrome.
lv_obj_t* create(lv_obj_t* parent);
lv_obj_t* add_button(lv_obj_t* bar, const char* text, lv_event_cb_t callback = nullptr, void* context = nullptr);
void style_item(lv_obj_t* item);
void fit_menu(lv_obj_t* panel, lv_coord_t available_width);
struct MenuOptions
{
bool anchor_toggles = true;
bool handle_back = true;
};
// Reuse the page's existing filter widgets instead of retaining a second set of
// menu items. The temporary outside-click surface exists only while open.
class Menu
{
public:
using Changed = void (*)(void* context, bool open);
void init(lv_obj_t* root, lv_obj_t* anchor, lv_obj_t* content,
Changed changed = nullptr, void* context = nullptr, MenuOptions options = {});
void reset();
void open();
void close();
void toggle();
bool is_open() const { return layer_ != nullptr; }
lv_obj_t* button() const { return anchor_; }
lv_obj_t* content() const { return content_; }
bool in_submenu() const { return parent_content_ != nullptr; }
void show_submenu(lv_obj_t* content);
bool back();
private:
static void anchor_event(lv_event_t* event);
static void outside_event(lv_event_t* event);
static void key_event(lv_event_t* event);
void present();
void prepare_keys(lv_obj_t* object);
void park(lv_obj_t* object);
lv_obj_t* root_ = nullptr;
lv_obj_t* anchor_ = nullptr;
lv_obj_t* content_ = nullptr;
lv_obj_t* parent_content_ = nullptr;
lv_obj_t* layer_ = nullptr;
Changed changed_ = nullptr;
void* context_ = nullptr;
MenuOptions options_{};
};
} // namespace ui::components::bottom_bar
@@ -1,6 +1,7 @@
#pragma once
#include "lvgl.h"
#include "ui/components/bottom_bar.h"
#include <cstddef>
#include <cstdint>
@@ -47,6 +48,11 @@ struct Adapter
bool (*handle_list_enter)(void* ctx, lv_obj_t* focused) = nullptr;
void (*on_column_changed)(void* ctx, FocusColumn column) = nullptr;
// Optional bottom-menu presentation; business activation stays in Adapter.
lv_obj_t* filter_panel = nullptr;
lv_obj_t* bottom_bar = nullptr;
void (*build_info)(lv_obj_t* parent) = nullptr;
BackPlacement filter_top_back_placement = BackPlacement::Leading;
BackPlacement list_top_back_placement = BackPlacement::None;
};
@@ -59,6 +65,10 @@ struct Binding
lv_indev_t* encoder = nullptr;
FocusColumn column = FocusColumn::Filter;
bool bound = false;
bottom_bar::Menu menu{};
lv_obj_t* menu_bar = nullptr;
lv_obj_t* info_button = nullptr;
bool owns_menu_bar = false;
};
void init(Binding* binding, const Adapter& adapter);
@@ -12,7 +12,6 @@
#include "chat/domain/chat_types.h"
#include "chat_message_list_input.h"
#include "lvgl.h"
#include "ui/components/air_status_footer.h"
#include "ui/components/floating_search_box.h"
#include "ui/widgets/top_bar.h"
#include <string>
@@ -135,7 +134,6 @@ class ChatMessageListScreen
lv_group_t* modal_prev_group_ = nullptr;
chat::ConversationId modal_conv_{};
ModalButtonContext modal_button_contexts_[5]{};
::ui::components::air_status_footer::Footer air_status_footer_{};
int selected_index_ = -1;
FilterMode filter_mode_ = FilterMode::Direct;
@@ -1,7 +1,6 @@
#pragma once
#include "chat/domain/chat_types.h"
#include "lvgl.h"
#include "ui/components/air_status_footer.h"
namespace chat::ui::layout
{
@@ -15,7 +14,6 @@ struct MessageListLayout
lv_obj_t* direct_btn;
lv_obj_t* broadcast_btn;
lv_obj_t* team_btn;
::ui::components::air_status_footer::Footer air_status_footer;
};
// split content (filter + list)
@@ -15,7 +15,6 @@ namespace layout
lv_obj_t* create_root(lv_obj_t* parent);
lv_obj_t* create_header(lv_obj_t* root, void (*back_callback)(void*), void* user_data);
void create_footer(lv_obj_t* root);
/**
* Create the main two-pane content row for the Contacts page.
@@ -9,7 +9,6 @@
#include "chat/domain/peer_browse_entry.h"
#include "lvgl.h"
#include "platform/ui/map_search_runtime.h"
#include "ui/components/air_status_footer.h"
#include "ui/components/floating_search_box.h"
#include "ui/widgets/top_bar.h"
#include <vector>
@@ -51,7 +50,6 @@ struct ContactsPageState
lv_obj_t* page = nullptr; // Main content row container
::ui::widgets::TopBar top_bar;
::ui::components::air_status_footer::Footer air_status_footer;
// First column: Filter buttons
lv_obj_t* filter_panel = nullptr;
@@ -15,7 +15,6 @@ lv_obj_t* create_content(lv_obj_t* root);
lv_obj_t* create_filter_panel(lv_obj_t* content, int width);
lv_obj_t* create_list_panel(lv_obj_t* content);
lv_obj_t* create_list_container(lv_obj_t* list_panel);
lv_obj_t* create_bottom_bar(lv_obj_t* list_panel);
} // namespace layout
} // namespace ui