From 20f3c023ddf371972898e65a6aa6fb18855ab281 Mon Sep 17 00:00:00 2001 From: tovjemam Date: Tue, 26 May 2026 17:05:40 +0200 Subject: [PATCH] Better tuning --- src/game/player.cpp | 18 +- src/game/remote_menu.cpp | 22 ++- src/game/remote_menu.hpp | 11 +- src/game/tuning_world.cpp | 268 ++++++++++++++---------------- src/game/tuning_world.hpp | 13 +- src/gameview/remote_menu_view.cpp | 25 ++- src/gameview/remote_menu_view.hpp | 1 + src/gui/menu.cpp | 111 +++++++++++-- src/gui/menu.hpp | 14 +- src/net/defs.hpp | 2 + src/net/inmessage.hpp | 9 + 11 files changed, 327 insertions(+), 167 deletions(-) diff --git a/src/game/player.cpp b/src/game/player.cpp index 0e62194..159ba1e 100644 --- a/src/game/player.cpp +++ b/src/game/player.cpp @@ -309,7 +309,23 @@ bool game::Player::ProcessMenuActionMsg(net::InMessage& msg) return false; if (!remote_menu_ || remote_menu_->GetId() != id) - return true; // not illegal, might be just a late message + { + // not illegal, might be just a late message + // need to skip specific amount of bytes :(((( + switch (type) + { + case net::MA_CLICK: + return msg.Skip(sizeof(net::MenuItemId)); + case net::MA_SELECT: + return msg.Skip(sizeof(net::MenuItemId) + sizeof(net::MenuSelectDir)); + case net::MA_HOVER: + return msg.Skip(sizeof(net::MenuItemId)); + case net::MA_EXIT: + return true; + default: + return false; + } + } return remote_menu_->ProcessActionMsg(msg, type); } diff --git a/src/game/remote_menu.cpp b/src/game/remote_menu.cpp index 7aa42b0..15e5709 100644 --- a/src/game/remote_menu.cpp +++ b/src/game/remote_menu.cpp @@ -50,11 +50,19 @@ bool game::RemoteMenu::ProcessActionMsg(net::InMessage& msg, net::MenuActionType return ProcessSelectMsg(msg); case net::MA_HOVER: return ProcessHoverMsg(msg); + case net::MA_EXIT: + return ProcessExitMsg(msg); default: return false; } } +void game::RemoteMenu::SetHoveredIdx(int hovered) +{ + hovered_ = hovered; + synced_ = false; +} + void game::RemoteMenu::Update() { if (synced_ && items_synced_) @@ -66,6 +74,7 @@ void game::RemoteMenu::Update() auto msg = BeginMenuMsg(net::MMSG_UPDATE); msg.Write(net::MenuTitle(title_)); msg.Write(items_.size()); + msg.Write(hovered_); for (auto& item : items_) { msg.Write(item->type_); @@ -124,7 +133,7 @@ bool game::RemoteMenu::ProcessClickMsg(net::InMessage& msg) if (!msg.Read(id)) return false; - if (id > items_.size()) + if (id >= items_.size()) return true; // not illegal auto& cb = items_[id]->on_click_; @@ -142,7 +151,7 @@ bool game::RemoteMenu::ProcessSelectMsg(net::InMessage& msg) if (!msg.Read(id) || !msg.Read(dir)) return false; - if (id > items_.size()) + if (id >= items_.size()) return true; // not illegal int idir = dir ? 1 : -1; @@ -161,7 +170,7 @@ bool game::RemoteMenu::ProcessHoverMsg(net::InMessage& msg) if (!msg.Read(id)) return false; - if (id > items_.size()) + if (id >= items_.size()) return true; // not illegal if (id == hovered_) @@ -175,3 +184,10 @@ bool game::RemoteMenu::ProcessHoverMsg(net::InMessage& msg) return true; } + +bool game::RemoteMenu::ProcessExitMsg(net::InMessage& msg) +{ + if (on_exit_) + on_exit_(); + return true; +} diff --git a/src/game/remote_menu.hpp b/src/game/remote_menu.hpp index 8b23462..da2eb32 100644 --- a/src/game/remote_menu.hpp +++ b/src/game/remote_menu.hpp @@ -24,6 +24,7 @@ class RemoteMenuItem; using RemoteMenuClickCallback = std::function; using RemoteMenuHoveredCallback = std::function; using RemoteMenuSelectCallback = std::function; +using RemoteMenuExitCallback = std::function; class RemoteMenuItem { @@ -37,6 +38,7 @@ public: void SetOnSelect(RemoteMenuSelectCallback on_select) { on_select_ = std::move(on_select); } void SetOnHovered(RemoteMenuHoveredCallback on_hovered) { on_hovered_ = std::move(on_hovered); } + private: RemoteMenu& menu_; RemoteMenuItemType type_ = RM_LABEL; @@ -63,6 +65,10 @@ public: bool ProcessActionMsg(net::InMessage& msg, net::MenuActionType type); + void SetHoveredIdx(int hovered); + + void SetOnExit(RemoteMenuExitCallback on_exit) { on_exit_ = std::move(on_exit); } + void Update(); private: @@ -72,6 +78,7 @@ private: bool ProcessClickMsg(net::InMessage& msg); bool ProcessSelectMsg(net::InMessage& msg); bool ProcessHoverMsg(net::InMessage& msg); + bool ProcessExitMsg(net::InMessage& msg); private: net::MenuId id_; @@ -79,7 +86,9 @@ private: bool synced_ = false; bool items_synced_ = false; std::vector> items_; - int hovered_ = -1; + int hovered_ = 0; + + RemoteMenuExitCallback on_exit_; friend class RemoteMenuItem; }; diff --git a/src/game/tuning_world.cpp b/src/game/tuning_world.cpp index fce699c..960b01b 100644 --- a/src/game/tuning_world.cpp +++ b/src/game/tuning_world.cpp @@ -53,171 +53,150 @@ const std::string& game::TuningWorld::GetOccupantName() const void game::TuningWorld::Setup() { tuning_ = vehicle_->GetTuning(); - // UpdateTuningVals(); - DisplayTuningMenu(); + OpenMainTuningMenu(); } -// void game::TuningWorld::UpdateTuningVals() -// { -// tun_primary_color_ = ColorU32ToU8Vec3(tuning_.primary_color); - -// tun_wheel_idx_ = tuning_.wheels_idx; -// tun_wheel_color_ = ColorU32ToU8Vec3(tuning_.wheel_color); -// } - -// void game::TuningWorld::UpdateTuning() -// { -// tuning_.primary_color = ColorU8Vec3ToU32(tun_primary_color_); - -// tuning_.wheels_idx = tun_wheel_idx_; -// tuning_.wheel_color = ColorU8Vec3ToU32(tun_wheel_color_); - -// vehicle_->SetTuning(tuning_); -// } - -// static void AddColorChannelSlider(game::RemoteMenu& menu, uint8_t& ch, std::string name, std::function on_change) -// { -// auto& slider = menu.AddItem(game::RM_SELECT, std::move(name)); - -// auto on_select = [&slider, &ch, on_change = std::move(on_change)] (int dir) { -// int new_val = ch + dir * 5; -// if (new_val < 0) -// ch = 0; -// else if (new_val > 255) -// ch = 255; -// else -// ch = new_val; - -// slider.SetSelection(std::to_string(ch)); -// on_change(); -// }; - -// on_select(0); -// slider.SetOnSelect(on_select); -// } - -// static void AddColorSliders(game::RemoteMenu& menu, glm::u8vec3& color, std::string name, std::function on_change) -// { -// AddColorChannelSlider(menu, color.r, name + " ^f00R", on_change); -// AddColorChannelSlider(menu, color.g, name + " ^0f0G", on_change); -// AddColorChannelSlider(menu, color.b, name + " ^00fB", on_change); -// } - -// static void AddWheelTypeSlider(game::RemoteMenu& menu, const assets::VehicleTuningList& tuning_list, size_t& idx, std::function on_change) -// { -// auto& slider = menu.AddItem(game::RM_SELECT, "kola"); - -// auto on_select = [&slider, &idx, &tuning_list, on_change = std::move(on_change)] (int dir) { -// auto& wheels = tuning_list.wheels; - -// if (dir < 0 && idx == 0) -// return; - -// if (dir > 0 && (idx + 1) >= wheels.size()) -// return; - -// idx += dir; - -// slider.SetSelection(tuning_list.wheels[idx].displayname); -// on_change(); -// }; - -// on_select(0); -// slider.SetOnSelect(on_select); -// } - - -void game::TuningWorld::AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group) +void game::TuningWorld::OpenMainTuningMenu() { - size_t num_parts = group.parts.size(); - if (num_parts == 0) - return; + CloseMenu(); - struct SliderState - { - const VehicleTuningGroup* group; - int idx = 0; - std::vector ids; - }; - - std::string current_part; - - auto current_it = tuning_.parts.find(group.id); - if (current_it != tuning_.parts.end()) - { - current_part = current_it->second; - } - - auto state = std::make_shared(); - state->group = &group; - - state->ids.reserve(num_parts); - size_t i = 0; - for (const auto& part : group.parts) - { - state->ids.push_back(part.first); - - if (part.first == current_part) - state->idx = i; - - ++i; - } - - auto& slider = menu.AddItem(game::RM_SELECT, group.displayname); - - auto on_select = [&slider, this, state](int dir) mutable { - if (dir < 0 && state->idx == 0) - state->idx = state->ids.size() - 1; - else if (dir > 0 && (state->idx + 1) >= state->ids.size()) - state->idx = 0; - else - state->idx += dir; - - auto& part_id = state->ids[state->idx]; - tuning_.parts[state->group->id] = part_id; - slider.SetSelection(state->group->parts.at(part_id).displayname); - - if (dir != 0) - vehicle_->SetTuning(tuning_); - - }; - - on_select(0); - slider.SetOnSelect(on_select); -} - -void game::TuningWorld::DisplayTuningMenu() -{ // display menu auto& menu = player_->DisplayMenu("tuning"); menu_ = &menu; - // auto on_change = [this] { UpdateTuning(); }; - - // AddColorSliders(menu, tun_primary_color_, "primární", on_change); - - // if (!tuning_list_->wheels.empty()) - // { - // AddWheelTypeSlider(menu, *tuning_list_, tun_wheel_idx_, on_change); - // AddColorSliders(menu, tun_wheel_color_, "kola", on_change); - // } + size_t i = 0; for (const auto& group : tuning_list_->groups) { - AddTuningGroupSelect(menu, group); + auto& btn = menu.AddItem(game::RM_BUTTON, group.displayname); + + btn.SetOnClick([this, i, &group]() { + main_menu_hover_ = i; + OpenGroupMenu(group); + }); + + ++i; } - auto& exit_btn = menu.AddItem(RM_BUTTON, "vylézt"); - exit_btn.SetOnClick([this] { + menu.SetOnExit([this]() { Reset(); }); + + menu.SetHoveredIdx(main_menu_hover_); +} + +void game::TuningWorld::OpenGroupMenu(const VehicleTuningGroup& group) +{ + CloseMenu(); + + preview_tuning_ = tuning_; + + auto& menu = player_->DisplayMenu(group.displayname); + menu_ = &menu; + + int current_idx = -1; + int i = 0; + + std::string state_text; + + for (const auto& [part_id, part] : group.parts) + { + auto& btn = menu.AddItem(RM_BUTTON, part.displayname); + + btn.SetOnHovered([this, &group, &part_id]() { + preview_tuning_.parts[group.id] = part_id; + vehicle_->SetTuning(preview_tuning_); + }); + + btn.SetOnClick([this, &btn, &group_id = group.id, &part_id]() { + std::string prev_part_id = GetCurrentPartId(group_id); + + if (prev_part_id == part_id) + return; + + tuning_.parts[group_id] = part_id; // apply + + std::string state_text; + + if (mounted_part_menu_item_) + { + GetPartState(group_id, prev_part_id, &state_text); + mounted_part_menu_item_->SetSelection(state_text); + } + + GetPartState(group_id, part_id, &state_text); + btn.SetSelection(state_text); + + mounted_part_menu_item_ = &btn; + }); + + if (GetPartState(group.id, part_id, &state_text)) + { + current_idx = i; + mounted_part_menu_item_ = &btn; + } + + btn.SetSelection(state_text); + + ++i; + } + + if (current_idx >= 0) + { + menu.SetHoveredIdx(current_idx); + } + + menu.SetOnExit([this]() { + mounted_part_menu_item_ = nullptr; + vehicle_->SetTuning(tuning_); // undo preview + OpenMainTuningMenu(); + }); + +} + +std::string game::TuningWorld::GetCurrentPartId(const std::string& group_id) +{ + auto part_it = tuning_.parts.find(group_id); + if (part_it != tuning_.parts.end()) + { + return part_it->second; + } + + return std::string(); +} + +bool game::TuningWorld::GetPartState(const std::string& group_name, const std::string& part_id, + std::string* state_text) +{ + bool mounted = part_id == GetCurrentPartId(group_name); + + if (state_text) + { + *state_text = mounted ? "aktuální" : "0 Kč"; + } + + return mounted; +} + + +void game::TuningWorld::CloseMenu() +{ + if (!menu_) + return; + + player_->CloseMenu(*menu_); + menu_ = nullptr; } void game::TuningWorld::Reset() { if (player_) { - player_->CloseMenu(*menu_); + CloseMenu(); + + // make sure to undo preview tuning + vehicle_->SetTuning(tuning_); + game_.MovePlayerToWorld(*player_, exit_world_, true, exit_pos_, exit_yaw_); if (exit_cb_) @@ -228,4 +207,5 @@ void game::TuningWorld::Reset() vehicle_ = nullptr; tuning_list_ = nullptr; menu_ = nullptr; + main_menu_hover_ = 0; } diff --git a/src/game/tuning_world.hpp b/src/game/tuning_world.hpp index 1753e25..83d83a2 100644 --- a/src/game/tuning_world.hpp +++ b/src/game/tuning_world.hpp @@ -2,6 +2,7 @@ #include "enterable_world.hpp" #include "drivable_vehicle.hpp" +#include "remote_menu.hpp" namespace game { @@ -29,7 +30,12 @@ private: void Setup(); void AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group); - void DisplayTuningMenu(); + + void OpenMainTuningMenu(); + void OpenGroupMenu(const VehicleTuningGroup& group); + std::string GetCurrentPartId(const std::string& group_id); + bool GetPartState(const std::string& group_id, const std::string& part_id, std::string* state_text); // mounted? + void CloseMenu(); void Reset(); @@ -46,7 +52,12 @@ private: game::RemoteMenu* menu_ = nullptr; + size_t main_menu_hover_ = 0; + VehicleTuning tuning_; + VehicleTuning preview_tuning_; + + RemoteMenuItem* mounted_part_menu_item_ = nullptr; std::function exit_cb_; diff --git a/src/gameview/remote_menu_view.cpp b/src/gameview/remote_menu_view.cpp index 63e112b..580aafc 100644 --- a/src/gameview/remote_menu_view.cpp +++ b/src/gameview/remote_menu_view.cpp @@ -23,8 +23,9 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg) { net::MenuTitle title; net::MenuItemCount itemcount; + net::MenuItemId hovered_idx; - if (!msg.Read(title) || !msg.Read(itemcount)) + if (!msg.Read(title) || !msg.Read(itemcount) || !msg.Read(hovered_idx)) return false; SetTitle(title); @@ -44,6 +45,7 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg) case RM_BUTTON: { auto& btn = Add(text); + btn.SetText2(selection); btn.SetClickCallback([this, i] { OnItemClick(i); }); break; } @@ -63,6 +65,7 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg) } + SetFocusedItemIndex(hovered_idx); return true; } @@ -105,11 +108,18 @@ bool game::view::RemoteMenuView::ProcessItemUpdateSelectionMsg(net::InMessage& m auto& item = GetItem(idx); - auto select = dynamic_cast(&item); - if (!select) - return false; + if (auto select = dynamic_cast(&item); select) + { + select->SetSelectionText(selection); + return true; + } + + if (auto btn = dynamic_cast(&item); btn) + { + btn->SetText2(selection); + return true; + } - select->SetSelectionText(selection); return true; } @@ -135,6 +145,11 @@ void game::view::RemoteMenuView::OnFocusChanged() msg.Write(GetFocusedItemIndex()); } +void game::view::RemoteMenuView::OnExit() +{ + BeginActionMsg(net::MA_EXIT); +} + net::OutMessage game::view::RemoteMenuView::BeginActionMsg(net::MenuActionType type) { auto msg = session_.BeginMsg(net::MSG_MENUACTION); diff --git a/src/gameview/remote_menu_view.hpp b/src/gameview/remote_menu_view.hpp index 558161c..ed2006d 100644 --- a/src/gameview/remote_menu_view.hpp +++ b/src/gameview/remote_menu_view.hpp @@ -32,6 +32,7 @@ private: protected: void OnFocusChanged() override; + void OnExit() override; net::OutMessage BeginActionMsg(net::MenuActionType type); diff --git a/src/gui/menu.cpp b/src/gui/menu.cpp index 2c76088..6d740dd 100644 --- a/src/gui/menu.cpp +++ b/src/gui/menu.cpp @@ -10,6 +10,7 @@ void gui::Menu::Clear() { items_.clear(); focus_ = 0; + UpdateScroll(); } void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const @@ -23,15 +24,37 @@ void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const ctx.DrawRect(pos, pos + title_size, 0x55000000); ctx.DrawTextAligned(title_, pos + title_size * 0.5f, glm::vec2(-0.5f)); + auto res_itemsize = itemsize_; + + // draw scrollbar + if (items_.size() > max_items_) + { + glm::vec2 scrollbar_size(5.0f, size.y - title_size.y); + glm::vec2 scrollbar_pos(pos.x + size.x - scrollbar_size.x, pos.y + title_size.y); + + // scrollbar background + ctx.DrawRect(scrollbar_pos, scrollbar_pos + scrollbar_size, 0x55000000); + + // scroll box + float y0 = static_cast(scroll_) / static_cast(items_.size()); + float y1 = y0 + static_cast(max_items_) / static_cast(items_.size()); + glm::vec2 scrollbox_p0(scrollbar_pos.x, scrollbar_pos.y + y0 * scrollbar_size.y); + glm::vec2 scrollbox_p1(scrollbar_pos.x + scrollbar_size.x, scrollbar_pos.y + y1 * scrollbar_size.y); + ctx.DrawRect(scrollbox_p0, scrollbox_p1, 0x44FFFFFF); + + res_itemsize.x -= scrollbar_size.x; + } + // draw items DrawMenuItemArgs args(ctx); - args.size = itemsize_; + args.size = res_itemsize; args.pos.x = pos.x; - for (size_t i = 0; i < items_.size(); ++i) + size_t end = glm::min(items_.size(), scroll_ + max_items_); + for (size_t i = scroll_; i < end; ++i) { args.focused = focus_ == i; - args.pos.y = pos.y + menu_title_height + static_cast(i) * itemsize_.y; + args.pos.y = pos.y + menu_title_height + static_cast(i - scroll_) * res_itemsize.y; items_[i]->Draw(args); } @@ -49,6 +72,10 @@ void gui::Menu::Input(MenuInput in) SwitchFocus(1); break; + case MI_BACK: + OnExit(); + break; + default: if (!items_.empty()) items_[focus_]->Input(in); @@ -61,9 +88,18 @@ void gui::Menu::SetTitle(std::string title) title_ = std::move(title); } +void gui::Menu::SetFocusedItemIndex(size_t idx) +{ + if (idx >= items_.size()) + return; + + focus_ = idx; + UpdateScroll(); +} + glm::vec2 gui::Menu::MeasureSize() const { - return glm::vec2(itemsize_.x, menu_title_height + itemsize_.y * static_cast(items_.size())); + return glm::vec2(itemsize_.x, menu_title_height + itemsize_.y * static_cast(glm::min(items_.size(), max_items_))); } void gui::Menu::SwitchFocus(int dir) @@ -81,9 +117,34 @@ void gui::Menu::SwitchFocus(int dir) if (focus_ != old_focus) { OnFocusChanged(); + UpdateScroll(); } } +void gui::Menu::UpdateScroll() +{ + const size_t num_items = items_.size(); + + if (scroll_ + max_items_ > num_items) + { + if (num_items > max_items_) + scroll_ = num_items - max_items_; + else + scroll_ = 0; + } + + if (focus_ >= scroll_ + max_items_) + { + scroll_ = focus_ - max_items_ + 1; + } + else if (focus_ < scroll_) + { + scroll_ = focus_; + } + + +} + // ButtonMenuItem gui::ButtonMenuItem::ButtonMenuItem(std::string text) @@ -94,8 +155,36 @@ gui::ButtonMenuItem::ButtonMenuItem(std::string text) void gui::ButtonMenuItem::Draw(const DrawMenuItemArgs& args) const { Super::Draw(args); - glm::vec2 center = args.pos + glm::vec2(10.0f, args.size.y * 0.5f); - args.ctx.DrawTextAligned(text_, center, glm::vec2(0.0f, -0.5f), args.focused ? COLOR_FOCUSED : COLOR_INACTIVE); + + uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE; + uint32_t arrow_color = 0xFFFFFFFF; + + const float padding = 30.0f; + glm::vec2 text_pos = args.pos + glm::vec2(padding, args.size.y * 0.5f); + + // text 1 + auto text_size = args.ctx.MeasureText(text_); + args.ctx.DrawText(text_, text_pos + text_size * glm::vec2(0.0f, -0.5f), text_color); + + // text 2 + if (!text2_.empty()) + { + glm::vec2 text2_pos = args.pos + glm::vec2(args.size.x - padding, args.size.y * 0.5f); + args.ctx.DrawTextAligned(text2_, text2_pos, glm::vec2(-1.0f, -0.5f), text_color); + } + + // hover arrows + if (args.focused) + { + auto arrow_size = args.ctx.MeasureText("» "); + + // left arrow + args.ctx.DrawText("» ", text_pos + arrow_size * glm::vec2(-1.0f, -0.5f), arrow_color); + + // right arrow + glm::vec2 right_arrow_pos(text2_.empty() ? (text_pos.x + text_size.x) : (args.pos.x + args.size.x - padding), text_pos.y); + args.ctx.DrawText(" «", right_arrow_pos + arrow_size * glm::vec2(0.0f, -0.5f), arrow_color); + } } void gui::ButtonMenuItem::Input(MenuInput in) @@ -118,16 +207,18 @@ gui::SelectMenuItem::SelectMenuItem(std::string text) void gui::SelectMenuItem::Draw(const DrawMenuItemArgs& args) const { - Super::Draw(args); + // Super::Draw(args); + uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE; + uint32_t arrow_color = 0xFFFFFFFF; + + glm::vec2 center = args.pos + glm::vec2(10.0f, args.size.y * 0.5f); + args.ctx.DrawTextAligned(text_, center, glm::vec2(0.0f, -0.5f), text_color); auto text_size = args.ctx.MeasureText(select_text_); glm::vec2 cursor = args.pos + glm::vec2(args.size.x - 10.0f, args.size.y * 0.5f); cursor.y -= text_size.y * 0.5f; // centered - uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE; - uint32_t arrow_color = 0xFFFFFFFF; - float arrow_width = 0.0f; if (args.focused) { diff --git a/src/gui/menu.hpp b/src/gui/menu.hpp index e7f81d2..c7f3fae 100644 --- a/src/gui/menu.hpp +++ b/src/gui/menu.hpp @@ -52,6 +52,7 @@ public: void SetTitle(std::string title); void SetItemSize(const glm::vec2& itemsize) { itemsize_ = itemsize; } + void SetFocusedItemIndex(size_t idx); size_t GetFocusedItemIndex() const { return focus_; } size_t GetNumItems() const { return items_.size(); } @@ -61,15 +62,20 @@ public: protected: virtual void OnFocusChanged() {} + virtual void OnExit() {} private: void SwitchFocus(int dir); + void UpdateScroll(); + private: std::string title_; std::vector> items_; - glm::vec2 itemsize_ = glm::vec2(300.0f, 40.0f); + glm::vec2 itemsize_ = glm::vec2(400.0f, 35.0f); size_t focus_ = 0; + size_t max_items_ = 15; + size_t scroll_ = 0; }; @@ -106,11 +112,15 @@ public: void SetClickCallback(std::function click_cb); void SetText(std::string text) { text_ = std::move(text); } + void SetText2(std::string text2) { text2_ = std::move(text2); } virtual ~ButtonMenuItem() = default; -private: +protected: std::string text_; + std::string text2_; + +private: std::function click_cb_; }; diff --git a/src/net/defs.hpp b/src/net/defs.hpp index 3f6ea4c..93767d3 100644 --- a/src/net/defs.hpp +++ b/src/net/defs.hpp @@ -166,6 +166,7 @@ enum MenuMessageType MMSG_UPDATE, MMSG_ITEM_UPDATE_TEXT, MMSG_ITEM_UPDATE_SELECTION, + MMSG_SET_HOVER, MMSG_CLOSE, }; @@ -185,6 +186,7 @@ enum MenuActionType MA_CLICK, MA_SELECT, MA_HOVER, + MA_EXIT, }; using MenuSelectDir = uint8_t; // 0=left, 1=right diff --git a/src/net/inmessage.hpp b/src/net/inmessage.hpp index 3781afa..25710eb 100644 --- a/src/net/inmessage.hpp +++ b/src/net/inmessage.hpp @@ -131,6 +131,15 @@ public: return true; } + bool Skip(size_t count) + { + if (!CheckAvail(count)) + return false; + + ptr_ += count; + return true; + } + bool SubMessage(InMessage& sub, size_t n) { if (!CheckAvail(n))