add touch scrolling, fix scrollbar, fix appstore search

- when fireing an action, the action array may change. so the loop should break early as soon as an action is handled.
  this fixes the appstore search when pressing B.
- scrollbar no longer goes oob. fixes #76

currently, scrolling has no acceleration.
This commit is contained in:
ITotalJustice
2025-01-06 22:29:25 +00:00
parent 705947fefb
commit 70d2e9873c
33 changed files with 542 additions and 512 deletions

View File

@@ -58,8 +58,8 @@ public:
static void NotifyFlashLed();
static auto GetThemeMetaList() -> std::span<ThemeMeta>;
static void SetTheme(u64 theme_index);
static auto GetThemeIndex() -> u64;
static void SetTheme(s64 theme_index);
static auto GetThemeIndex() -> s64;
static auto GetDefaultImage(int* w = nullptr, int* h = nullptr) -> int;
@@ -145,7 +145,7 @@ public:
Theme m_theme{};
fs::FsPath theme_path{};
std::size_t m_theme_index{};
s64 m_theme_index{};
bool m_quit{};

View File

@@ -5,22 +5,53 @@
namespace sphaira::ui {
struct List final : Object {
using Callback = std::function<bool(NVGcontext* vg, Theme* theme, Vec4 v, u64 index)>;
using Callback = std::function<void(NVGcontext* vg, Theme* theme, Vec4 v, s64 index)>;
using TouchCallback = std::function<void(s64 index)>;
List(const Vec4& pos, const Vec4& v, const Vec2& pad = {});
List(s64 row, s64 page, const Vec4& pos, const Vec4& v, const Vec2& pad = {});
void Do(u64 index, u64 count, Callback callback) const {
Do(nullptr, nullptr, index, count, callback);
void OnUpdate(Controller* controller, TouchInfo* touch, s64 count, TouchCallback callback);
void Draw(NVGcontext* vg, Theme* theme, s64 count, Callback callback) const;
auto SetScrollBarPos(float x, float y, float h) {
m_scrollbar.x = x;
m_scrollbar.y = y;
m_scrollbar.h = h;
}
void Do(NVGcontext* vg, Theme* theme, u64 index, u64 count, Callback callback, float y_off = 0) const;
auto ScrollDown(s64& index, s64 step, s64 count) -> bool;
auto ScrollUp(s64& index, s64 step, s64 count) -> bool;
auto GetYoff() const {
return m_yoff;
}
void SetYoff(float y = 0) {
m_yoff = y;
}
auto GetMaxY() const {
return m_v.h + m_pad.y;
}
private:
auto Draw(NVGcontext* vg, Theme* theme) -> void override {}
auto ClampY(float y, s64 count) const -> float;
private:
const s64 m_row;
const s64 m_page;
Vec4 m_v;
Vec2 m_pad;
Vec4 m_scrollbar{};
// current y offset.
float m_yoff{};
// in progress y offset, used when scrolling.
float m_y_prog{};
};
} // namespace sphaira::ui

View File

@@ -78,7 +78,7 @@ struct EntryMenu final : MenuBase {
// void OnFocusGained() override;
void ShowChangelogAction();
void SetIndex(std::size_t index);
void SetIndex(s64 index);
void UpdateOptions();
@@ -98,7 +98,7 @@ private:
const LazyImage& m_default_icon;
Menu& m_menu;
std::size_t m_index{}; // where i am in the array
s64 m_index{}; // where i am in the array
std::vector<Option> m_options;
LazyImage m_banner;
std::unique_ptr<List> m_list;
@@ -150,7 +150,7 @@ struct FeedbackMenu final : MenuBase {
void Draw(NVGcontext* vg, Theme* theme) override;
void OnFocusGained() override;
void SetIndex(std::size_t index);
void SetIndex(s64 index);
void ScanHomebrew();
void Sort();
@@ -158,8 +158,7 @@ private:
const std::vector<Entry>& m_package_entries;
LazyImage& m_default_image;
std::vector<FeedbackEntry> m_entries;
std::size_t m_start{};
std::size_t m_index{}; // where i am in the array
s64 m_index{}; // where i am in the array
ImageDownloadState m_repo_download_state{ImageDownloadState::None};
};
@@ -171,7 +170,7 @@ struct Menu final : MenuBase {
void Draw(NVGcontext* vg, Theme* theme) override;
void OnFocusGained() override;
void SetIndex(std::size_t index);
void SetIndex(s64 index);
void ScanHomebrew();
void Sort();
@@ -202,8 +201,7 @@ private:
SortType m_sort{SortType::SortType_Updated};
OrderType m_order{OrderType::OrderType_Decending};
std::size_t m_start{};
std::size_t m_index{}; // where i am in the array
s64 m_index{}; // where i am in the array
LazyImage m_default_image;
LazyImage m_update;
LazyImage m_get;
@@ -214,8 +212,8 @@ private:
std::string m_search_term;
std::string m_author_term;
u64 m_entry_search_jump_back{};
u64 m_entry_author_jump_back{};
s64 m_entry_search_jump_back{};
s64 m_entry_author_jump_back{};
bool m_is_search{};
bool m_is_author{};
bool m_dirty{}; // if set, does a sort

View File

@@ -23,8 +23,8 @@ private:
std::unique_ptr<ScrollableText> m_scroll_text;
std::size_t m_start{};
std::size_t m_index{}; // where i am in the array
s64 m_start{};
s64 m_index{}; // where i am in the array
};
} // namespace sphaira::ui::menu::fileview

View File

@@ -93,9 +93,9 @@ struct FileAssocEntry {
struct LastFile {
fs::FsPath name;
u64 index;
u64 offset;
u64 entries_count;
s64 index;
float offset;
s64 entries_count;
};
struct FsDirCollection {
@@ -120,7 +120,7 @@ struct Menu final : MenuBase {
}
private:
void SetIndex(std::size_t index);
void SetIndex(s64 index);
void InstallForwarder();
auto Scan(const fs::FsPath& new_path, bool is_walk_up = false) -> Result;
@@ -132,7 +132,7 @@ private:
return GetNewPath(m_path, entry.name);
}
auto GetNewPath(u64 index) const -> fs::FsPath {
auto GetNewPath(s64 index) const -> fs::FsPath {
return GetNewPath(m_path, GetEntry(index).name);
}
@@ -257,9 +257,8 @@ private:
// if it does, the index becomes that file.
std::vector<LastFile> m_previous_highlighted_file;
fs::FsPath m_selected_path;
std::size_t m_index{};
std::size_t m_index_offset{};
std::size_t m_selected_count{};
s64 m_index{};
s64 m_selected_count{};
SelectedType m_selected_type{SelectedType::None};
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Alphabetical};

View File

@@ -50,7 +50,7 @@ struct Menu final : MenuBase {
void OnFocusGained() override;
private:
void SetIndex(std::size_t index);
void SetIndex(s64 index);
void Scan();
void LoadEntriesFromPath(const fs::FsPath& path);
@@ -67,8 +67,8 @@ private:
private:
std::vector<Entry> m_entries;
std::size_t m_index{};
std::size_t m_index_offset{};
s64 m_index{};
s64 m_index_offset{};
std::unique_ptr<List> m_list;
};

View File

@@ -30,7 +30,7 @@ struct Menu final : MenuBase {
void Draw(NVGcontext* vg, Theme* theme) override;
void OnFocusGained() override;
void SetIndex(std::size_t index);
void SetIndex(s64 index);
void InstallHomebrew();
void ScanHomebrew();
void Sort();
@@ -51,13 +51,9 @@ private:
static constexpr inline const char* INI_SECTION = "homebrew";
std::vector<NroEntry> m_entries;
std::size_t m_start{};
std::size_t m_index{}; // where i am in the array
s64 m_index{}; // where i am in the array
std::unique_ptr<List> m_list;
// todo: needed for scroll
float y_off = 0;
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_AlphabeticalStar};
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Decending};
option::OptionBool m_hide_sphaira{INI_SECTION, "hide_sphaira", false};

View File

@@ -61,7 +61,7 @@ private:
Rotation m_rotation{Rotation_90};
Colour m_colour{Colour_Grey};
int m_image{};
std::size_t m_index{};
s64 m_index{};
};
} // namespace sphaira::ui::menu::irs

View File

@@ -34,8 +34,7 @@ struct MainMenu final : Widget {
private:
void OnLRPress(std::shared_ptr<MenuBase> menu, Button b);
void AddOnLPress();
void AddOnRPress();
void AddOnLRPress();
private:
std::shared_ptr<homebrew::Menu> m_homebrew_menu{};

View File

@@ -165,11 +165,10 @@ struct Menu final : MenuBase {
void Draw(NVGcontext* vg, Theme* theme) override;
void OnFocusGained() override;
void SetIndex(std::size_t index) {
void SetIndex(s64 index) {
m_index = index;
if (m_index > m_start && m_index - m_start >= 6) {
m_start = m_index/3*3 - 3;
if (!m_index) {
m_list->SetYoff(0);
}
}
@@ -185,13 +184,12 @@ private:
static constexpr inline u32 MAX_ON_PAGE = 16; // same as website
std::vector<PageEntry> m_pages;
std::size_t m_page_index{};
std::size_t m_page_index_max{1};
s64 m_page_index{};
s64 m_page_index_max{1};
std::string m_search{};
std::size_t m_start{};
std::size_t m_index{}; // where i am in the array
s64 m_index{}; // where i am in the array
std::unique_ptr<List> m_list;
// options

View File

@@ -84,6 +84,9 @@ auto getButton(Button button) -> const char*;
void drawScrollbar(NVGcontext* vg, Theme* theme, u32 index_off, u32 count, u32 max_per_page);
void drawScrollbar(NVGcontext* vg, Theme* theme, float x, float y, float h, u32 index_off, u32 count, u32 max_per_page);
void drawScrollbar2(NVGcontext* vg, Theme* theme, float x, float y, float h, s64 index_off, s64 count, s64 row, s64 page);
void drawScrollbar2(NVGcontext* vg, Theme* theme, s64 index_off, s64 count, s64 row, s64 page);
void updateHighlightAnimation();
void getHighlightAnimation(float* gradientX, float* gradientY, float* color);

View File

@@ -27,16 +27,16 @@ private:
// todo: support upto 4 options.
class OptionBox final : public Widget {
public:
using Callback = std::function<void(std::optional<std::size_t> index)>;
using Callback = std::function<void(std::optional<s64> index)>;
using Option = std::string;
using Options = std::vector<Option>;
public:
OptionBox(const std::string& message, const Option& a, Callback cb = [](auto){}); // confirm
OptionBox(const std::string& message, const Option& a, const Option& b, Callback cb); // yesno
OptionBox(const std::string& message, const Option& a, const Option& b, std::size_t index, Callback cb); // yesno
OptionBox(const std::string& message, const Option& a, const Option& b, s64 index, Callback cb); // yesno
OptionBox(const std::string& message, const Option& a, const Option& b, const Option& c, Callback cb); // tri
OptionBox(const std::string& message, const Option& a, const Option& b, const Option& c, std::size_t index, Callback cb); // tri
OptionBox(const std::string& message, const Option& a, const Option& b, const Option& c, s64 index, Callback cb); // tri
auto Update(Controller* controller, TouchInfo* touch) -> void override;
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
@@ -44,8 +44,8 @@ public:
auto OnFocusLost() noexcept -> void override;
private:
auto Setup(std::size_t index) -> void; // common setup values
void SetIndex(std::size_t index);
auto Setup(s64 index) -> void; // common setup values
void SetIndex(s64 index);
private:
std::string m_message;
@@ -53,7 +53,7 @@ private:
Vec4 m_spacer_line{};
std::size_t m_index{};
s64 m_index{};
std::vector<OptionBoxEntry> m_entries;
};

View File

@@ -9,14 +9,14 @@ namespace sphaira::ui {
class PopupList final : public Widget {
public:
using Items = std::vector<std::string>;
using Callback = std::function<void(std::optional<std::size_t>)>;
using Callback = std::function<void(std::optional<s64>)>;
public:
explicit PopupList(std::string title, Items items, Callback cb, std::size_t index = 0);
explicit PopupList(std::string title, Items items, Callback cb, s64 index = 0);
PopupList(std::string title, Items items, Callback cb, std::string index);
PopupList(std::string title, Items items, std::string& index_str_ref, std::size_t& index);
PopupList(std::string title, Items items, std::string& index_str_ref, s64& index);
PopupList(std::string title, Items items, std::string& index_ref);
PopupList(std::string title, Items items, std::size_t& index_ref);
PopupList(std::string title, Items items, s64& index_ref);
auto Update(Controller* controller, TouchInfo* touch) -> void override;
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
@@ -24,7 +24,7 @@ public:
auto OnFocusLost() noexcept -> void override;
private:
void SetIndex(std::size_t index);
void SetIndex(s64 index);
private:
static constexpr Vec2 m_title_pos{70.f, 28.f};
@@ -35,8 +35,8 @@ private:
std::string m_title;
Items m_items;
Callback m_callback;
std::size_t m_index; // index in list array
std::size_t m_index_offset{}; // drawing from array start
s64 m_index; // index in list array
s64 m_index_offset{}; // drawing from array start
std::unique_ptr<List> m_list;

View File

@@ -22,7 +22,7 @@ struct ProgressBox final : Widget {
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
auto NewTransfer(const std::string& transfer) -> ProgressBox&;
auto UpdateTransfer(u64 offset, u64 size) -> ProgressBox&;
auto UpdateTransfer(s64 offset, s64 size) -> ProgressBox&;
void RequestExit();
auto ShouldExit() -> bool;
@@ -55,8 +55,8 @@ private:
ProgressBoxDoneCallback m_done{};
std::string m_title{};
std::string m_transfer{};
u64 m_size{};
u64 m_offset{};
s64 m_size{};
s64 m_offset{};
bool m_exit_requested{};
};

View File

@@ -50,10 +50,10 @@ class SidebarEntryArray final : public SidebarEntryBase {
public:
using Items = std::vector<std::string>;
using ListCallback = std::function<void()>;
using Callback = std::function<void(std::size_t& index)>;
using Callback = std::function<void(s64& index)>;
public:
explicit SidebarEntryArray(std::string title, Items items, Callback cb, std::size_t index = 0);
explicit SidebarEntryArray(std::string title, Items items, Callback cb, s64 index = 0);
SidebarEntryArray(std::string title, Items items, Callback cb, std::string index);
SidebarEntryArray(std::string title, Items items, std::string& index);
@@ -63,7 +63,7 @@ private:
Items m_items;
ListCallback m_list_callback;
Callback m_callback;
std::size_t m_index;
s64 m_index;
};
template <typename T>
@@ -108,15 +108,16 @@ public:
void Add(std::shared_ptr<SidebarEntryBase> entry);
private:
void SetIndex(std::size_t index);
void SetIndex(s64 index);
void SetupButtons();
private:
std::string m_title;
std::string m_sub;
Side m_side;
Items m_items;
std::size_t m_index{};
std::size_t m_index_offset{};
s64 m_index{};
s64 m_index_offset{};
std::unique_ptr<List> m_list;

View File

@@ -220,6 +220,7 @@ struct TouchInfo {
bool is_tap;
bool is_scroll;
bool is_clicked;
bool is_end;
};
enum class Button : u64 {

View File

@@ -72,13 +72,14 @@ struct Widget : public Object {
return m_pop;
}
auto GetUiButtons() const -> uiButtons;
static auto GetUiButtons(const Actions& actions, float x = 1220, float y = 675) -> uiButtons;
auto SetUiButtonPos(Vec2 pos) {
m_button_pos = pos;
}
static auto ScrollHelperDown(u64& index, u64& start, u64 step, s64 row, s64 page, u64 size) -> bool;
static auto ScrollHelperUp(u64& index, u64& start, s64 step, s64 row, s64 page, s64 size) -> bool;
auto GetUiButtons() const -> uiButtons;
Actions m_actions;
Vec2 m_button_pos{1220, 675};
bool m_focus{false};
bool m_pop{false};
};

View File

@@ -391,12 +391,12 @@ auto App::GetThemeMetaList() -> std::span<ThemeMeta> {
return g_app->m_theme_meta_entries;
}
void App::SetTheme(u64 theme_index) {
void App::SetTheme(s64 theme_index) {
g_app->LoadTheme(g_app->m_theme_meta_entries[theme_index].ini_path.c_str());
g_app->m_theme_index = theme_index;
}
auto App::GetThemeIndex() -> u64 {
auto App::GetThemeIndex() -> s64 {
return g_app->m_theme_index;
}
@@ -723,6 +723,8 @@ void App::Poll() {
m_touch_info.is_scroll = false;
if (m_touch_info.is_tap) {
m_touch_info.is_clicked = true;
} else {
m_touch_info.is_end = true;
}
}

View File

@@ -38,7 +38,7 @@ Mutex g_mutex_share[CURL_LOCK_DATA_LAST]{};
struct DataStruct {
std::vector<u8> data;
u64 offset{};
s64 offset{};
FsFile f{};
s64 file_offset{};
};

View File

@@ -1,25 +1,45 @@
#include "ui/list.hpp"
#include "ui/nvg_util.hpp"
#include "app.hpp"
#include "log.hpp"
namespace sphaira::ui {
List::List(const Vec4& pos, const Vec4& v, const Vec2& pad)
: m_v{v}
List::List(s64 row, s64 page, const Vec4& pos, const Vec4& v, const Vec2& pad)
: m_row{row}
, m_page{page}
, m_v{v}
, m_pad{pad} {
m_pos = pos;
SetScrollBarPos(SCREEN_WIDTH - 50, 100, SCREEN_HEIGHT-200);
}
void List::Do(NVGcontext* vg, Theme* theme, u64 index, u64 count, Callback callback, float y_off) const {
auto v = m_v;
auto List::ClampY(float y, s64 count) const -> float {
float y_max = 0;
if (vg) {
nvgSave(vg);
nvgScissor(vg, GetX(), GetY(), GetW(), GetH());
if (count >= m_page) {
// round up
if (count % m_row) {
count = count + (m_row - count % m_row);
}
y_max = (count - m_page) / m_row * GetMaxY();
}
// index = (y_off / (v.h + m_pad.y)) / (GetW() / (v.w + m_pad.x));
// v.y -= y_off;
if (y < 0) {
y = 0;
} else if (y > y_max) {
y = y_max;
}
for (u64 i = index; i < count; v.y += v.h + m_pad.y) {
return y;
}
void List::OnUpdate(Controller* controller, TouchInfo* touch, s64 count, TouchCallback callback) {
if (touch->is_clicked && touch->in_range(GetPos())) {
auto v = m_v;
v.y -= ClampY(m_yoff + m_y_prog, count);
for (s64 i = 0; i < count; v.y += v.h + m_pad.y) {
if (v.y > GetY() + GetH()) {
break;
}
@@ -32,24 +52,138 @@ void List::Do(NVGcontext* vg, Theme* theme, u64 index, u64 count, Callback callb
break;
}
Vec4 vv = v;
// if not drawing, only return clipped v as its used for touch
if (!vg) {
vv.w = std::min(v.x + v.w, m_pos.x + m_pos.w) - v.x;
vv.h = std::min(v.y + v.h, m_pos.y + m_pos.h) - v.y;
// skip anything not visible
if (v.y + v.h < GetY()) {
continue;
}
if (!callback(vg, theme, vv, i)) {
Vec4 vv = v;
// if not drawing, only return clipped v as its used for touch
vv.w = std::min(v.x + v.w, m_pos.x + m_pos.w) - v.x;
vv.h = std::min(v.y + v.h, m_pos.y + m_pos.h) - v.y;
if (touch->in_range(vv)) {
callback(i);
return;
}
}
v.x = x;
}
if (vg) {
nvgRestore(vg);
} else if (touch->is_scroll && touch->in_range(GetPos())) {
m_y_prog = (float)touch->initial.y - (float)touch->cur.y;
} else if (touch->is_end) {
m_yoff = ClampY(m_yoff + m_y_prog, count);
m_y_prog = 0;
}
}
void List::Draw(NVGcontext* vg, Theme* theme, s64 count, Callback callback) const {
const auto yoff = ClampY(m_yoff + m_y_prog, count);
const s64 start = yoff / GetMaxY() * m_row;
gfx::drawScrollbar2(vg, theme, m_scrollbar.x, m_scrollbar.y, m_scrollbar.h, start, count, m_row, m_page);
auto v = m_v;
v.y -= yoff;
nvgSave(vg);
nvgScissor(vg, GetX(), GetY(), GetW(), GetH());
for (s64 i = 0; i < count; v.y += v.h + m_pad.y) {
if (v.y > GetY() + GetH()) {
break;
}
const auto x = v.x;
for (; i < count; i++, v.x += v.w + m_pad.x) {
// only draw if full x is in bounds
if (v.x + v.w > GetX() + GetW()) {
break;
}
// skip anything not visible
if (v.y + v.h < GetY()) {
continue;
}
callback(vg, theme, v, i);
}
v.x = x;
}
nvgRestore(vg);
}
auto List::ScrollDown(s64& index, s64 step, s64 count) -> bool {
const auto old_index = index;
if (!count) {
return false;
}
if (index + step < count) {
index += step;
} else {
index = count - 1;
}
if (index != old_index) {
App::PlaySoundEffect(SoundEffect_Scroll);
s64 delta = index - old_index;
s64 start = m_yoff / GetMaxY() * m_row;
while (index < start) {
start -= m_row;
m_yoff -= GetMaxY();
}
if (index - start >= m_page) {
do {
start += m_row;
delta -= m_row;
m_yoff += GetMaxY();
} while (delta > 0 && start + m_page < count);
}
return true;
}
return false;
}
auto List::ScrollUp(s64& index, s64 step, s64 count) -> bool {
const auto old_index = index;
if (!count) {
return false;
}
if (index >= step) {
index -= step;
} else {
index = 0;
}
if (index != old_index) {
App::PlaySoundEffect(SoundEffect_Scroll);
s64 start = m_yoff / GetMaxY() * m_row;
while (index < start) {
start -= m_row;
m_yoff -= GetMaxY();
}
while (index - start >= m_page && start + m_page < count) {
start += m_row;
m_yoff += GetMaxY();
}
return true;
}
return false;
}
} // namespace sphaira::ui

View File

@@ -856,7 +856,7 @@ void EntryMenu::UpdateOptions() {
SetIndex(0);
}
void EntryMenu::SetIndex(std::size_t index) {
void EntryMenu::SetIndex(s64 index) {
m_index = index;
const auto option = m_options[m_index];
if (option.confirm_text.empty()) {
@@ -908,22 +908,22 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"_i18n}
}
}}),
std::make_pair(Button::DOWN, Action{[this](){
if (ScrollHelperDown(m_index, m_start, 3, 3, 9, m_entries_current.size())) {
if (m_list->ScrollDown(m_index, 3, m_entries_current.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
if (ScrollHelperUp(m_index, m_start, 3, 3, 9, m_entries_current.size())) {
if (m_list->ScrollUp(m_index, 3, m_entries_current.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::R2, Action{[this](){
if (ScrollHelperDown(m_index, m_start, 9, 3, 9, m_entries_current.size())) {
if (m_list->ScrollDown(m_index, 9, m_entries_current.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::L2, Action{[this](){
if (ScrollHelperUp(m_index, m_start, 9, 3, 9, m_entries_current.size())) {
if (m_list->ScrollUp(m_index, 9, m_entries_current.size())) {
SetIndex(m_index);
}
}}),
@@ -958,17 +958,17 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"_i18n}
order_items.push_back("Decending"_i18n);
order_items.push_back("Ascending"_i18n);
options->Add(std::make_shared<SidebarEntryArray>("Filter"_i18n, filter_items, [this, filter_items](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Filter"_i18n, filter_items, [this, filter_items](s64& index_out){
SetFilter((Filter)index_out);
}, (std::size_t)m_filter));
}, (s64)m_filter));
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
SetSort((SortType)index_out);
}, (std::size_t)m_sort));
}, (s64)m_sort));
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](s64& index_out){
SetOrder((OrderType)index_out);
}, (std::size_t)m_order));
}, (s64)m_order));
options->Add(std::make_shared<SidebarEntryCallback>("Search"_i18n, [this](){
std::string out;
@@ -1003,7 +1003,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"_i18n}
const Vec4 v{75, 110, 370, 155};
const Vec2 pad{10, 10};
m_list = std::make_unique<List>(m_pos, v, pad);
m_list = std::make_unique<List>(3, 9, m_pos, v, pad);
Sort();
}
@@ -1013,19 +1013,13 @@ Menu::~Menu() {
void Menu::Update(Controller* controller, TouchInfo* touch) {
MenuBase::Update(controller, touch);
m_list->Do(m_start, m_entries_current.size(), [this, &touch](auto* vg, auto* theme, auto v, auto pos) {
if (touch->is_clicked && touch->in_range(v)) {
if (pos == m_index) {
m_list->OnUpdate(controller, touch, m_entries_current.size(), [this](auto i) {
if (m_index == i) {
FireAction(Button::A);
} else {
App::PlaySoundEffect(SoundEffect_Focus);
SetIndex(pos);
SetIndex(i);
}
return false;
}
return true;
});
}
@@ -1042,14 +1036,11 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
return;
}
// only draw scrollbar if needed
gfx::drawScrollbar(vg, theme, m_start, m_entries_current.size(), 9);
// max images per frame, in order to not hit io / gpu too hard.
const int image_load_max = 2;
int image_load_count = 0;
m_list->Do(vg, theme, m_start, m_entries_current.size(), [this, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
m_list->Draw(vg, theme, m_entries_current.size(), [this, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
const auto& [x, y, w, h] = v;
const auto index = m_entries_current[pos];
auto& e = m_entries[index];
@@ -1121,9 +1112,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
DrawIcon(vg, e.image, m_default_image, x + 20, y + 20, 115, 115, true, image_scale);
// gfx::drawImage(vg, x + 20, y + 20, image_size, image_size_h, image.image ? image.image : m_default_image);
const auto clip_y = std::min(GetY() + GetH(), y + h) - y;
nvgSave(vg);
nvgScissor(vg, v.x, v.y, w - 30.f, clip_y); // clip
nvgIntersectScissor(vg, v.x, v.y, w - 30.f, h); // clip
{
const float font_size = 18;
gfx::drawTextArgs(vg, x + 148, y + 45, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.title.c_str());
@@ -1147,8 +1137,6 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
gfx::drawImageRounded(vg, x + w - 30.f, y + 110, i_size, i_size, m_update.image);
break;
}
return true;
});
}
@@ -1178,21 +1166,16 @@ void Menu::OnFocusGained() {
if (m_dirty) {
m_dirty = false;
const auto& current_entry = m_entries[m_entries_current[m_index]];
// m_start = 0;
// m_index = 0;
log_write("\nold index: %zu start: %zu\n", m_index, m_start);
// old index: 19 start: 12
Sort();
for (u32 i = 0; i < m_entries_current.size(); i++) {
if (current_entry.name == m_entries[m_entries_current[i]].name) {
SetIndex(i);
if (i >= 9) {
m_start = (i - 9) / 3 * 3 + 3;
m_list->SetYoff((((i - 9) + 3) / 3) * m_list->GetMaxY());
} else {
m_start = 0;
m_list->SetYoff(0);
}
log_write("\nnew index: %zu start: %zu\n", m_index, m_start);
break;
}
}
@@ -1200,14 +1183,10 @@ void Menu::OnFocusGained() {
}
}
void Menu::SetIndex(std::size_t index) {
void Menu::SetIndex(s64 index) {
m_index = index;
if (!m_index) {
m_start = 0;
}
if (m_index > m_start && m_index - m_start >= 9) {
m_start = m_index/3*3 - 6;
m_list->SetYoff(0);
}
this->SetSubHeading(std::to_string(m_index + 1) + " / " + std::to_string(m_entries_current.size()));
@@ -1402,9 +1381,10 @@ void Menu::SetSearch(const std::string& term) {
SetFilter(m_filter);
SetIndex(m_entry_search_jump_back);
if (m_entry_search_jump_back >= 9) {
m_start = (m_entry_search_jump_back - 9) / 3 * 3 + 3;
m_list->SetYoff(0);
m_list->SetYoff((((m_entry_search_jump_back - 9) + 3) / 3) * m_list->GetMaxY());
} else {
m_start = 0;
m_list->SetYoff(0);
}
}});
@@ -1435,11 +1415,12 @@ void Menu::SetAuthor() {
} else {
SetFilter(m_filter);
}
SetIndex(m_entry_author_jump_back);
if (m_entry_author_jump_back >= 9) {
m_start = (m_entry_author_jump_back - 9) / 3 * 3 + 3;
m_list->SetYoff((((m_entry_author_jump_back - 9) + 3) / 3) * m_list->GetMaxY());
} else {
m_start = 0;
m_list->SetYoff(0);
}
}});

View File

@@ -246,22 +246,22 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
}
}}),
std::make_pair(Button::DOWN, Action{[this](){
if (ScrollHelperDown(m_index, m_index_offset, 1, 1, 8, m_entries_current.size())) {
if (m_list->ScrollDown(m_index, 1, m_entries_current.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
if (ScrollHelperUp(m_index, m_index_offset, 1, 1, 8, m_entries_current.size())) {
if (m_list->ScrollUp(m_index, 1, m_entries_current.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::DPAD_RIGHT, Action{[this](){
if (ScrollHelperDown(m_index, m_index_offset, 8, 1, 8, m_entries_current.size())) {
if (m_list->ScrollDown(m_index, 8, m_entries_current.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::DPAD_LEFT, Action{[this](){
if (ScrollHelperUp(m_index, m_index_offset, 8, 1, 8, m_entries_current.size())) {
if (m_list->ScrollUp(m_index, 8, m_entries_current.size())) {
SetIndex(m_index);
}
}}),
@@ -355,12 +355,12 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
order_items.push_back("Decending"_i18n);
order_items.push_back("Ascending"_i18n);
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this](s64& index_out){
m_sort.Set(index_out);
SortAndFindLastFile();
}, m_sort.Get()));
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this](s64& index_out){
m_order.Set(index_out);
SortAndFindLastFile();
}, m_order.Get()));
@@ -542,7 +542,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
mount_items.push_back("Image System memory"_i18n);
mount_items.push_back("Image microSD card"_i18n);
options->Add(std::make_shared<SidebarEntryArray>("Mount"_i18n, mount_items, [this](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Mount"_i18n, mount_items, [this](s64& index_out){
App::PopToMenu();
m_mount.Set(index_out);
SetFs("/", index_out);
@@ -552,7 +552,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
);
const Vec4 v{75, GetY() + 1.f + 42.f, 1220.f-45.f*2, 60};
m_list = std::make_unique<List>(m_pos, v);
m_list = std::make_unique<List>(1, 8, m_pos, v);
fs::FsPath buf;
ini_gets("paths", "last_path", "/", buf, sizeof(buf), App::CONFIG_PATH);
@@ -565,18 +565,13 @@ Menu::~Menu() {
void Menu::Update(Controller* controller, TouchInfo* touch) {
MenuBase::Update(controller, touch);
m_list->Do(m_index_offset, m_entries_current.size(), [this, touch](auto* vg, auto* theme, auto v, auto i) {
if (touch->is_clicked && touch->in_range(v)) {
m_list->OnUpdate(controller, touch, m_entries_current.size(), [this](auto i) {
if (m_index == i) {
FireAction(Button::A);
} else {
App::PlaySoundEffect(SoundEffect_Focus);
SetIndex(i);
}
return false;
}
return true;
});
}
@@ -591,12 +586,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
}
constexpr float text_xoffset{15.f};
gfx::drawScrollbar(vg, theme, m_index_offset, m_entries_current.size(), 8);
nvgSave(vg);
nvgScissor(vg, GetX(), GetY(), GetW(), GetH());
m_list->Do(vg, theme, m_index_offset, m_entries_current.size(), [this, text_col](auto* vg, auto* theme, auto v, auto i) {
m_list->Draw(vg, theme, m_entries_current.size(), [this, text_col](auto* vg, auto* theme, auto v, auto i) {
const auto& [x, y, w, h] = v;
auto& e = GetEntry(i);
@@ -623,7 +614,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
text_id = ThemeEntryID_TEXT_SELECTED;
gfx::drawRectOutline(vg, 4.f, theme->elements[ThemeEntryID_SELECTED_OVERLAY].colour, x, y, w, h, theme->elements[ThemeEntryID_SELECTED].colour);
} else {
if (i == m_index_offset) {
if (i == m_index) {
gfx::drawRect(vg, x, y, w, 1.f, text_col);
}
gfx::drawRect(vg, x, y + h, w, 1.f, text_col);
@@ -650,8 +641,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
}
nvgSave(vg);
const auto txt_clip = std::min(GetY() + GetH(), y + h) - y;
nvgScissor(vg, x + text_xoffset+65, y, w-(x+text_xoffset+65+50), txt_clip);
nvgIntersectScissor(vg, x + text_xoffset+65, y, w-(x+text_xoffset+65+50), h);
gfx::drawText(vg, x + text_xoffset+65, y + (h / 2.f), 20.f, e.name, NULL, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->elements[text_id].colour);
nvgRestore(vg);
@@ -673,11 +663,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->elements[text_id].colour, "%.2f MiB", (double)e.file_size / 1024.0 / 1024.0);
}
}
return true;
});
nvgRestore(vg);
}
void Menu::OnFocusGained() {
@@ -697,14 +683,10 @@ void Menu::OnFocusGained() {
}
}
void Menu::SetIndex(std::size_t index) {
void Menu::SetIndex(s64 index) {
m_index = index;
if (!m_index) {
m_index_offset = 0;
}
if (m_index > m_index_offset && m_index - m_index_offset >= 7) {
m_index_offset = m_index - 7;
m_list->SetYoff();
}
if (!m_entries_current.empty() && !GetEntry().checked_internal_extension && GetEntry().extension == "zip") {
@@ -799,16 +781,14 @@ void Menu::InstallForwarder() {
auto Menu::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result {
log_write("new scan path: %s\n", new_path);
if (!is_walk_up && !m_path.empty() && !m_entries_current.empty()) {
const LastFile f{GetEntry().name, m_index, m_index_offset, m_entries_current.size()};
const LastFile f(GetEntry().name, m_index, m_list->GetYoff(), m_entries_current.size());
m_previous_highlighted_file.emplace_back(f);
}
log_write("\nold index: %zu start: %zu\n", m_index, m_index_offset);
m_path = new_path;
m_entries.clear();
m_index = 0;
m_index_offset = 0;
m_list->SetYoff(0);
SetTitleSubHeading(m_path);
if (m_selected_type == SelectedType::None) {
@@ -1086,7 +1066,7 @@ void Menu::Sort() {
void Menu::SortAndFindLastFile() {
std::optional<LastFile> last_file;
if (!m_path.empty() && !m_entries_current.empty()) {
last_file = LastFile{GetEntry().name, m_index, m_index_offset, m_entries_current.size()};
last_file = LastFile(GetEntry().name, m_index, m_list->GetYoff(), m_entries_current.size());
}
Sort();
@@ -1107,21 +1087,21 @@ void Menu::SetIndexFromLastFile(const LastFile& last_file) {
}
}
if (index >= 0) {
if ((u64)index == last_file.index && m_entries_current.size() == last_file.entries_count) {
m_index_offset = last_file.offset;
if (index == last_file.index && m_entries_current.size() == last_file.entries_count) {
m_list->SetYoff(last_file.offset);
log_write("index is the same as last time\n");
} else {
// file position changed!
log_write("file position changed\n");
// guesstimate where the position is
if (index >= 8) {
m_index_offset = (index - 8) + 1;
m_list->SetYoff(((index - 8) + 1) * m_list->GetMaxY());
} else {
m_index_offset = 0;
m_list->SetYoff(0);
}
}
SetIndex(index);
log_write("\nnew index: %zu start: %zu mod: %zu\n", m_index, m_index_offset, index % 8);
log_write("\nnew index: %zu %zu mod: %zu\n", m_index, index % 8);
}
}

View File

@@ -182,7 +182,7 @@ auto DownloadApp(ProgressBox* pbox, const GhApiAsset& gh_asset, const AssetEntry
}
std::vector<char> buf(chunk_size);
u64 offset{};
s64 offset{};
while (offset < info.uncompressed_size) {
const auto bytes_read = unzReadCurrentFile(zfile, buf.data(), buf.size());
if (bytes_read <= 0) {
@@ -248,22 +248,22 @@ Menu::Menu() : MenuBase{"GitHub"_i18n} {
this->SetActions(
std::make_pair(Button::DOWN, Action{[this](){
if (ScrollHelperDown(m_index, m_index_offset, 1, 1, 8, m_entries.size())) {
if (m_list->ScrollDown(m_index, 1, m_entries.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
if (ScrollHelperUp(m_index, m_index_offset, 1, 1, 8, m_entries.size())) {
if (m_list->ScrollUp(m_index, 1, m_entries.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::DPAD_RIGHT, Action{[this](){
if (ScrollHelperDown(m_index, m_index_offset, 8, 1, 8, m_entries.size())) {
if (m_list->ScrollDown(m_index, 8, m_entries.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::DPAD_LEFT, Action{[this](){
if (ScrollHelperUp(m_index, m_index_offset, 8, 1, 8, m_entries.size())) {
if (m_list->ScrollUp(m_index, 8, m_entries.size())) {
SetIndex(m_index);
}
}}),
@@ -365,7 +365,7 @@ Menu::Menu() : MenuBase{"GitHub"_i18n} {
);
const Vec4 v{75, GetY() + 1.f + 42.f, 1220.f-45.f*2, 60};
m_list = std::make_unique<List>(m_pos, v);
m_list = std::make_unique<List>(1, 8, m_pos, v);
}
Menu::~Menu() {
@@ -373,18 +373,13 @@ Menu::~Menu() {
void Menu::Update(Controller* controller, TouchInfo* touch) {
MenuBase::Update(controller, touch);
m_list->Do(m_index_offset, m_entries.size(), [this, touch](auto* vg, auto* theme, auto v, auto i) {
if (touch->is_clicked && touch->in_range(v)) {
m_list->OnUpdate(controller, touch, m_entries.size(), [this](auto i) {
if (m_index == i) {
FireAction(Button::A);
} else {
App::PlaySoundEffect(SoundEffect_Focus);
SetIndex(i);
}
return false;
}
return true;
});
}
@@ -399,9 +394,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
}
constexpr float text_xoffset{15.f};
gfx::drawScrollbar(vg, theme, m_index_offset, m_entries.size(), 8);
m_list->Do(vg, theme, m_index_offset, m_entries.size(), [this, text_col](auto* vg, auto* theme, auto v, auto i) {
m_list->Draw(vg, theme, m_entries.size(), [this, text_col](auto* vg, auto* theme, auto v, auto i) {
const auto& [x, y, w, h] = v;
auto& e = m_entries[i];
@@ -417,13 +411,11 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
}
nvgSave(vg);
const auto txt_clip = std::min(GetY() + GetH(), y + h) - y;
nvgScissor(vg, x + text_xoffset, y, w-(x+text_xoffset+50), txt_clip);
nvgIntersectScissor(vg, x + text_xoffset, y, w-(x+text_xoffset+50), h);
gfx::drawTextArgs(vg, x + text_xoffset, y + (h / 2.f), 20.f, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->elements[text_id].colour, "%s By %s", e.repo.c_str(), e.owner.c_str());
nvgRestore(vg);
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f), 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE, theme->elements[text_id].colour, "version: %s", e.tag.c_str());
return true;
});
}
@@ -434,7 +426,7 @@ void Menu::OnFocusGained() {
}
}
void Menu::SetIndex(std::size_t index) {
void Menu::SetIndex(s64 index) {
m_index = index;
if (!m_index) {
m_index_offset = 0;

View File

@@ -43,22 +43,22 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
}
}}),
std::make_pair(Button::DOWN, Action{[this](){
if (ScrollHelperDown(m_index, m_start, 3, 3, 9, m_entries.size())) {
if (m_list->ScrollDown(m_index, 3, m_entries.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
if (ScrollHelperUp(m_index, m_start, 3, 3, 9, m_entries.size())) {
if (m_list->ScrollUp(m_index, 3, m_entries.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::R2, Action{[this](){
if (ScrollHelperDown(m_index, m_start, 9, 3, 9, m_entries.size())) {
if (m_list->ScrollDown(m_index, 9, m_entries.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::L2, Action{[this](){
if (ScrollHelperUp(m_index, m_start, 9, 3, 9, m_entries.size())) {
if (m_list->ScrollUp(m_index, 9, m_entries.size())) {
SetIndex(m_index);
}
}}),
@@ -86,12 +86,12 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
order_items.push_back("Decending"_i18n);
order_items.push_back("Ascending"_i18n);
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
m_sort.Set(index_out);
SortAndFindLastFile();
}, m_sort.Get()));
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](s64& index_out){
m_order.Set(index_out);
SortAndFindLastFile();
}, m_order.Get()));
@@ -144,7 +144,7 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
const Vec4 v{75, 110, 370, 155};
const Vec2 pad{10, 10};
m_list = std::make_unique<List>(m_pos, v, pad);
m_list = std::make_unique<List>(3, 9, m_pos, v, pad);
}
Menu::~Menu() {
@@ -157,42 +157,24 @@ Menu::~Menu() {
void Menu::Update(Controller* controller, TouchInfo* touch) {
MenuBase::Update(controller, touch);
if (touch->is_clicked && touch->in_range(GetPos())) {
m_list->Do(m_start, m_entries.size(), [this, touch](auto* vg, auto* theme, auto v, auto i) {
if (touch->is_clicked && touch->in_range(v)) {
m_list->OnUpdate(controller, touch, m_entries.size(), [this](auto i) {
if (m_index == i) {
FireAction(Button::A);
} else {
App::PlaySoundEffect(SoundEffect_Focus);
SetIndex(i);
}
return false;
}
return true;
});
} else if (touch->is_scroll && touch->in_range(GetPos())) {
y_off = (s32)touch->initial.y - (s32)touch->cur.y;
log_write("y off: %.2f\n", y_off);
} else {
// y_off = 0;
}
}
void Menu::Draw(NVGcontext* vg, Theme* theme) {
MenuBase::Draw(vg, theme);
// only draw scrollbar if needed
gfx::drawScrollbar(vg, theme, m_start, m_entries.size(), 9);
// max images per frame, in order to not hit io / gpu too hard.
const int image_load_max = 2;
int image_load_count = 0;
nvgSave(vg);
nvgScissor(vg, GetX(), GetY(), GetW(), GetH());
m_list->Do(vg, theme, m_start, m_entries.size(), [this, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
m_list->Draw(vg, theme, m_entries.size(), [this, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
const auto& [x, y, w, h] = v;
auto& e = m_entries[pos];
@@ -222,9 +204,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
const float image_size = 115;
gfx::drawImageRounded(vg, x + 20, y + 20, image_size, image_size, e.image ? e.image : App::GetDefaultImage());
const auto clip_y = std::min(GetY() + GetH(), y + h) - y;
nvgSave(vg);
nvgScissor(vg, x, y, w - 30.f, clip_y); // clip
nvgIntersectScissor(vg, x, y, w - 30.f, h); // clip
{
bool has_star = false;
if (IsStarEnabled()) {
@@ -240,10 +221,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
gfx::drawTextArgs(vg, x + 148, y + 115, font_size, NVG_ALIGN_LEFT, theme->elements[text_id].colour, e.GetDisplayVersion());
}
nvgRestore(vg);
return true;
}, y_off);
nvgRestore(vg);
});
}
void Menu::OnFocusGained() {
@@ -253,14 +231,10 @@ void Menu::OnFocusGained() {
}
}
void Menu::SetIndex(std::size_t index) {
void Menu::SetIndex(s64 index) {
m_index = index;
if (!m_index) {
m_start = 0;
}
if (m_index > m_start && m_index - m_start >= 9) {
m_start = m_index/3*3 - 6;
m_list->SetYoff(0);
}
const auto& e = m_entries[m_index];
@@ -450,9 +424,9 @@ void Menu::SortAndFindLastFile() {
if (index >= 0) {
// guesstimate where the position is
if (index >= 9) {
m_start = (index - 9) / 3 * 3 + 3;
m_list->SetYoff((((index - 9) + 3) / 3) * m_list->GetMaxY());
} else {
m_start = 0;
m_list->SetYoff(0);
}
SetIndex(index);
}

View File

@@ -139,44 +139,44 @@ Menu::Menu() : MenuBase{"Irs"_i18n} {
format_str.emplace_back("20x15"_i18n);
}
options->Add(std::make_shared<SidebarEntryArray>("Controller"_i18n, controller_str, [this](std::size_t& index){
options->Add(std::make_shared<SidebarEntryArray>("Controller"_i18n, controller_str, [this](s64& index){
irsStopImageProcessor(m_entries[m_index].m_handle);
m_index = index;
UpdateConfig(&m_config);
}, m_index));
options->Add(std::make_shared<SidebarEntryArray>("Rotation"_i18n, rotation_str, [this](std::size_t& index){
options->Add(std::make_shared<SidebarEntryArray>("Rotation"_i18n, rotation_str, [this](s64& index){
m_rotation = (Rotation)index;
}, m_rotation));
options->Add(std::make_shared<SidebarEntryArray>("Colour"_i18n, colour_str, [this](std::size_t& index){
options->Add(std::make_shared<SidebarEntryArray>("Colour"_i18n, colour_str, [this](s64& index){
m_colour = (Colour)index;
updateColourArray();
}, m_colour));
options->Add(std::make_shared<SidebarEntryArray>("Light Target"_i18n, light_target_str, [this](std::size_t& index){
options->Add(std::make_shared<SidebarEntryArray>("Light Target"_i18n, light_target_str, [this](s64& index){
m_config.light_target = index;
UpdateConfig(&m_config);
}, m_config.light_target));
options->Add(std::make_shared<SidebarEntryArray>("Gain"_i18n, gain_str, [this](std::size_t& index){
options->Add(std::make_shared<SidebarEntryArray>("Gain"_i18n, gain_str, [this](s64& index){
m_config.gain = GAIN_MIN + index;
UpdateConfig(&m_config);
}, m_config.gain - GAIN_MIN));
options->Add(std::make_shared<SidebarEntryArray>("Negative Image"_i18n, is_negative_image_used_str, [this](std::size_t& index){
options->Add(std::make_shared<SidebarEntryArray>("Negative Image"_i18n, is_negative_image_used_str, [this](s64& index){
m_config.is_negative_image_used = index;
UpdateConfig(&m_config);
}, m_config.is_negative_image_used));
options->Add(std::make_shared<SidebarEntryArray>("Format"_i18n, format_str, [this](std::size_t& index){
options->Add(std::make_shared<SidebarEntryArray>("Format"_i18n, format_str, [this](s64& index){
m_config.orig_format = index;
m_config.trimming_format = index;
UpdateConfig(&m_config);
}, m_config.orig_format));
if (hosversionAtLeast(4,0,0)) {
options->Add(std::make_shared<SidebarEntryArray>("Trimming Format"_i18n, format_str, [this](std::size_t& index){
options->Add(std::make_shared<SidebarEntryArray>("Trimming Format"_i18n, format_str, [this](s64& index){
// you cannot set trim a larger region than the source
if (index < m_config.orig_format) {
index = m_config.orig_format;

View File

@@ -115,7 +115,7 @@ auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string v
}
std::vector<char> buf(chunk_size);
u64 offset{};
s64 offset{};
while (offset < info.uncompressed_size) {
const auto bytes_read = unzReadCurrentFile(zfile, buf.data(), buf.size());
if (bytes_read <= 0) {
@@ -205,9 +205,6 @@ MainMenu::MainMenu() {
}
});
AddOnLPress();
AddOnRPress();
this->SetActions(
std::make_pair(Button::START, Action{App::Exit}),
std::make_pair(Button::Y, Action{"Menu"_i18n, [this](){
@@ -239,7 +236,7 @@ MainMenu::MainMenu() {
auto options = std::make_shared<Sidebar>("Theme Options"_i18n, Sidebar::Side::LEFT);
ON_SCOPE_EXIT(App::Push(options));
options->Add(std::make_shared<SidebarEntryArray>("Select Theme"_i18n, theme_items, [this, theme_items](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Select Theme"_i18n, theme_items, [this, theme_items](s64& index_out){
App::SetTheme(index_out);
}, App::GetThemeIndex()));
@@ -292,9 +289,9 @@ MainMenu::MainMenu() {
}
}));
options->Add(std::make_shared<SidebarEntryArray>("Language"_i18n, language_items, [this](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Language"_i18n, language_items, [this](s64& index_out){
App::SetLanguage(index_out);
}, (std::size_t)App::GetLanguage()));
}, (s64)App::GetLanguage()));
options->Add(std::make_shared<SidebarEntryCallback>("Misc"_i18n, [this](){
auto options = std::make_shared<Sidebar>("Misc Options"_i18n, Sidebar::Side::LEFT);
@@ -339,9 +336,9 @@ MainMenu::MainMenu() {
App::SetInstallEnable(enable);
}, "Enabled"_i18n, "Disabled"_i18n));
options->Add(std::make_shared<SidebarEntryArray>("Install location"_i18n, install_items, [this](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Install location"_i18n, install_items, [this](s64& index_out){
App::SetInstallSdEnable(index_out);
}, (std::size_t)App::GetInstallSdEnable()));
}, (s64)App::GetInstallSdEnable()));
options->Add(std::make_shared<SidebarEntryBool>("Show install warning"_i18n, App::GetInstallPrompt(), [this](bool& enable){
App::SetInstallPrompt(enable);
@@ -355,6 +352,8 @@ MainMenu::MainMenu() {
m_app_store_menu = std::make_shared<appstore::Menu>(m_homebrew_menu->GetHomebrewList());
m_current_menu = m_homebrew_menu;
AddOnLRPress();
for (auto [button, action] : m_actions) {
m_current_menu->SetAction(button, action);
}
@@ -387,17 +386,11 @@ void MainMenu::OnLRPress(std::shared_ptr<MenuBase> menu, Button b) {
if (m_current_menu == m_homebrew_menu) {
m_current_menu = menu;
RemoveAction(b);
if (b == Button::L) {
AddOnRPress();
} else {
AddOnLPress();
}
} else {
m_current_menu = m_homebrew_menu;
AddOnRPress();
AddOnLPress();
}
AddOnLRPress();
m_current_menu->OnFocusGained();
for (auto [button, action] : m_actions) {
@@ -405,18 +398,20 @@ void MainMenu::OnLRPress(std::shared_ptr<MenuBase> menu, Button b) {
}
}
void MainMenu::AddOnLPress() {
void MainMenu::AddOnLRPress() {
if (m_current_menu != m_filebrowser_menu) {
const auto label = m_current_menu == m_homebrew_menu ? "Files" : "Apps";
SetAction(Button::L, Action{i18n::get(label), [this]{
OnLRPress(m_filebrowser_menu, Button::L);
}});
}
}
void MainMenu::AddOnRPress() {
if (m_current_menu != m_app_store_menu) {
const auto label = m_current_menu == m_homebrew_menu ? "Store" : "Apps";
SetAction(Button::R, Action{i18n::get(label), [this]{
OnLRPress(m_app_store_menu, Button::R);
}});
}
}
} // namespace sphaira::ui::menu::main

View File

@@ -369,7 +369,7 @@ auto InstallTheme(ProgressBox* pbox, const PackListEntry& entry) -> bool {
}
std::vector<char> buf(chunk_size);
u64 offset{};
s64 offset{};
while (offset < info.uncompressed_size) {
if (pbox->ShouldExit()) {
return false;
@@ -429,25 +429,25 @@ Menu::Menu() : MenuBase{"Themezer"_i18n} {
}}),
std::make_pair(Button::DOWN, Action{[this](){
const auto& page = m_pages[m_page_index];
if (ScrollHelperDown(m_index, m_start, 3, 3, 6, page.m_packList.size())) {
if (m_list->ScrollDown(m_index, 3, page.m_packList.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
const auto& page = m_pages[m_page_index];
if (ScrollHelperUp(m_index, m_start, 3, 3, 6, page.m_packList.size())) {
if (m_list->ScrollUp(m_index, 3, page.m_packList.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::R2, Action{[this](){
const auto& page = m_pages[m_page_index];
if (ScrollHelperDown(m_index, m_start, 6, 3, 6, page.m_packList.size())) {
if (m_list->ScrollDown(m_index, 6, page.m_packList.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::L2, Action{[this](){
const auto& page = m_pages[m_page_index];
if (ScrollHelperUp(m_index, m_start, 6, 3, 6, page.m_packList.size())) {
if (m_list->ScrollUp(m_index, 6, page.m_packList.size())) {
SetIndex(m_index);
}
}}),
@@ -492,14 +492,14 @@ Menu::Menu() : MenuBase{"Themezer"_i18n} {
InvalidateAllPages();
}, "Enabled"_i18n, "Disabled"_i18n));
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
if (m_sort.Get() != index_out) {
m_sort.Set(index_out);
InvalidateAllPages();
}
}, m_sort.Get()));
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](std::size_t& index_out){
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](s64& index_out){
if (m_order.Get() != index_out) {
m_order.Set(index_out);
InvalidateAllPages();
@@ -548,7 +548,7 @@ Menu::Menu() : MenuBase{"Themezer"_i18n} {
const Vec4 v{75, 110, 350, 250};
const Vec2 pad{10, 10};
m_list = std::make_unique<List>(m_pos, v, pad);
m_list = std::make_unique<List>(3, 6, m_pos, v, pad);
}
Menu::~Menu() {
@@ -567,17 +567,13 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
return;
}
m_list->Do(m_start, page.m_packList.size(), [this, touch](auto* vg, auto* theme, auto v, auto i) {
if (touch->is_clicked && touch->in_range(v)) {
m_list->OnUpdate(controller, touch, page.m_packList.size(), [this](auto i) {
if (m_index == i) {
FireAction(Button::A);
} else {
App::PlaySoundEffect(SoundEffect_Focus);
SetIndex(i);
}
return false;
}
return true;
});
}
@@ -605,14 +601,11 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
return;
}
// only draw scrollbar if needed
gfx::drawScrollbar(vg, theme, m_start, page.m_packList.size(), 9);
// max images per frame, in order to not hit io / gpu too hard.
const int image_load_max = 2;
int image_load_count = 0;
m_list->Do(vg, theme, m_start, page.m_packList.size(), [this, &page, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
m_list->Draw(vg, theme, page.m_packList.size(), [this, &page, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
const auto& [x, y, w, h] = v;
auto& e = page.m_packList[pos];
@@ -687,15 +680,13 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
gfx::drawImageRounded(vg, x + xoff, y, 320, 180, image.image ? image.image : App::GetDefaultImage());
}
const auto clip_y = std::min(GetY() + GetH(), y + h) - y;
nvgSave(vg);
nvgScissor(vg, x, y, w - 30.f, clip_y); // clip
nvgIntersectScissor(vg, x, y, w - 30.f, h); // clip
{
gfx::drawTextArgs(vg, x + xoff, y + 180 + 20, 18, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "%s", e.details.name.c_str());
gfx::drawTextArgs(vg, x + xoff, y + 180 + 55, 18, NVG_ALIGN_LEFT, theme->elements[text_id].colour, "%s", e.creator.display_name.c_str());
}
nvgRestore(vg);
return true;
});
}
@@ -719,7 +710,7 @@ void Menu::PackListDownload() {
SetSubHeading(subheading);
m_index = 0;
m_start = 0;
m_list->SetYoff(0);
// already downloaded
if (m_pages[m_page_index].m_ready != PageLoadState::None) {

View File

@@ -77,6 +77,19 @@ inline void drawRectOutlineInternal(NVGcontext* vg, float size, const NVGcolor&
float gradientX, gradientY, color;
getHighlightAnimation(&gradientX, &gradientY, &color);
#if 1
// NVGcolor pulsationColor = nvgRGBAf((color * out_col.r) + (1 - color) * out_col.r,
// (color * out_col.g) + (1 - color) * out_col.g,
// (color * out_col.b) + (1 - color) * out_col.b,
// out_col.a);
NVGcolor pulsationColor = nvgRGBAf((color * out_col.r) + (1 - color) * out_col.r,
(color * out_col.g) + (1 - color) * out_col.g,
(color * out_col.b) + (1 - color) * out_col.b,
out_col.a);
drawRectIntenal(vg, {vec.x-size,vec.y-size,vec.w+(size*2.f),vec.h+(size * 2.f)}, pulsationColor, false);
drawRectIntenal(vg, vec, c, false);
#else
const auto strokeWidth = 5.0;
auto v2 = vec;
v2.x -= strokeWidth / 2.0;
@@ -85,8 +98,8 @@ inline void drawRectOutlineInternal(NVGcontext* vg, float size, const NVGcolor&
v2.h += strokeWidth;
const auto corner_radius = 0.5;
nvgSave(vg);
nvgResetScissor(vg);
// nvgSave(vg);
// nvgResetScissor(vg);
// const auto stroke_width = 5.0f;
// const auto shadow_corner_radius = 6.0f;
@@ -155,7 +168,8 @@ inline void drawRectOutlineInternal(NVGcontext* vg, float size, const NVGcolor&
nvgFillColor(vg, c);
nvgFill(vg);
nvgRestore(vg);
// nvgRestore(vg);
#endif
}
inline void drawRectOutlineInternal(NVGcontext* vg, float size, const NVGcolor& out_col, Vec4 vec, const NVGpaint& p) {
@@ -454,9 +468,9 @@ void drawTextArgs(NVGcontext* vg, float x, float y, float size, int align, Colou
}
void drawScrollbar(NVGcontext* vg, Theme* theme, float x, float y, float h, u32 index_off, u32 count, u32 max_per_page) {
const u64 SCROLL = index_off;
const u64 max_entry_display = max_per_page;
const u64 entry_total = count;
const s64 SCROLL = index_off;
const s64 max_entry_display = max_per_page;
const s64 entry_total = count;
const float scc2 = 8.0;
const float scw = 2.0;
@@ -474,6 +488,28 @@ void drawScrollbar(NVGcontext* vg, Theme* theme, u32 index_off, u32 count, u32 m
drawScrollbar(vg, theme, SCREEN_WIDTH - 50, 100, SCREEN_HEIGHT-200, index_off, count, max_per_page);
}
void drawScrollbar2(NVGcontext* vg, Theme* theme, float x, float y, float h, s64 index_off, s64 count, s64 row, s64 page) {
// round up
if (count % row) {
count = count + (row - count % row);
}
const float scc2 = 8.0;
const float scw = 2.0;
// only draw scrollbar if needed
if (count > page) {
const float sb_h = 1.f / (float)count * h;
const float sb_y = index_off;
gfx::drawRect(vg, x, y, scc2, h, theme->elements[ThemeEntryID_GRID].colour, false);
gfx::drawRect(vg, x + scw, y + scw + sb_h * sb_y, scc2 - scw * 2, sb_h * float(page) - scw * 2, theme->elements[ThemeEntryID_TEXT_SELECTED].colour, false);
}
}
void drawScrollbar2(NVGcontext* vg, Theme* theme, s64 index_off, s64 count, s64 row, s64 page) {
drawScrollbar2(vg, theme, SCREEN_WIDTH - 50, 100, SCREEN_HEIGHT-200, index_off, count, row, page);
}
#define HIGHLIGHT_SPEED 350.0
static double highlightGradientX = 0;

View File

@@ -45,7 +45,7 @@ OptionBox::OptionBox(const std::string& message, const Option& a, const Option&
}
OptionBox::OptionBox(const std::string& message, const Option& a, const Option& b, std::size_t index, Callback cb)
OptionBox::OptionBox(const std::string& message, const Option& a, const Option& b, s64 index, Callback cb)
: m_message{message}
, m_callback{cb} {
@@ -70,7 +70,7 @@ OptionBox::OptionBox(const std::string& message, const Option& a, const Option&
}
OptionBox::OptionBox(const std::string& message, const Option& a, const Option& b, const Option& c, std::size_t index, Callback cb)
OptionBox::OptionBox(const std::string& message, const Option& a, const Option& b, const Option& c, s64 index, Callback cb)
: m_message{message}
, m_callback{cb} {
@@ -80,7 +80,7 @@ auto OptionBox::Update(Controller* controller, TouchInfo* touch) -> void {
Widget::Update(controller, touch);
if (touch->is_clicked) {
for (std::size_t i = 0; i < m_entries.size(); i++) {
for (s64 i = 0; i < m_entries.size(); i++) {
auto& e = m_entries[i];
if (touch->in_range(e.GetPos())) {
SetIndex(i);
@@ -118,8 +118,8 @@ auto OptionBox::OnFocusLost() noexcept -> void {
SetHidden(true);
}
auto OptionBox::Setup(std::size_t index) -> void {
m_index = std::min(m_entries.size() - 1, index);
auto OptionBox::Setup(s64 index) -> void {
m_index = std::min<s64>(m_entries.size() - 1, index);
m_entries[m_index].Selected(true);
m_spacer_line = Vec4{m_pos.x, m_pos.y + 220.f - 2.f, m_pos.w, 2.f};
@@ -145,7 +145,7 @@ auto OptionBox::Setup(std::size_t index) -> void {
);
}
void OptionBox::SetIndex(std::size_t index) {
void OptionBox::SetIndex(s64 index) {
if (m_index != index) {
m_entries[m_index].Selected(false);
m_index = index;

View File

@@ -5,7 +5,7 @@
namespace sphaira::ui {
PopupList::PopupList(std::string title, Items items, std::string& index_str_ref, std::size_t& index_ref)
PopupList::PopupList(std::string title, Items items, std::string& index_str_ref, s64& index_ref)
: PopupList{std::move(title), std::move(items), Callback{}, index_ref} {
m_callback = [&index_str_ref, &index_ref, this](auto op_idx) {
@@ -34,7 +34,7 @@ PopupList::PopupList(std::string title, Items items, std::string& index_ref)
};
}
PopupList::PopupList(std::string title, Items items, std::size_t& index_ref)
PopupList::PopupList(std::string title, Items items, s64& index_ref)
: PopupList{std::move(title), std::move(items), Callback{}, index_ref} {
m_callback = [&index_ref, this](auto op_idx) {
@@ -56,11 +56,32 @@ PopupList::PopupList(std::string title, Items items, Callback cb, std::string in
}
}
PopupList::PopupList(std::string title, Items items, Callback cb, std::size_t index)
PopupList::PopupList(std::string title, Items items, Callback cb, s64 index)
: m_title{std::move(title)}
, m_items{std::move(items)}
, m_callback{cb}
, m_index{index} {
this->SetActions(
std::make_pair(Button::DOWN, Action{[this](){
if (m_list->ScrollDown(m_index, 1, m_items.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
if (m_list->ScrollUp(m_index, 1, m_items.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::A, Action{"Select"_i18n, [this](){
if (m_callback) {
m_callback(m_index);
}
SetPop();
}}),
std::make_pair(Button::B, Action{"Back"_i18n, [this](){
SetPop();
}})
);
m_pos.w = 1280.f;
const float a = std::min(405.f, (60.f * static_cast<float>(m_items.size())));
@@ -75,44 +96,16 @@ PopupList::PopupList(std::string title, Items items, Callback cb, std::size_t in
Vec4 v{m_block};
v.y = m_line_top + 1.f + 42.f;
const Vec4 pos{0, m_line_top, 1280.f, m_line_bottom - m_line_top};
m_list = std::make_unique<List>(pos, v);
this->SetActions(
std::make_pair(Button::DOWN, Action{[this](){
if (ScrollHelperDown(m_index, m_index_offset, 1, 1, 7, m_items.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
if (ScrollHelperUp(m_index, m_index_offset, 1, 1, 7, m_items.size())) {
SetIndex(m_index);
}
}}),
std::make_pair(Button::A, Action{"Select"_i18n, [this](){
if (m_callback) {
m_callback(m_index);
}
SetPop();
}}),
std::make_pair(Button::B, Action{"Back"_i18n, [this](){
SetPop();
}})
);
m_list = std::make_unique<List>(1, 7, pos, v);
m_list->SetScrollBarPos(1250, m_line_top + 20, m_line_bottom - m_line_top - 40);
}
auto PopupList::Update(Controller* controller, TouchInfo* touch) -> void {
Widget::Update(controller, touch);
if (touch->is_clicked && touch->in_range(GetPos())) {
m_list->Do(m_index_offset, m_items.size(), [this, touch](auto* vg, auto* theme, auto v, auto i) {
if (touch->is_clicked && touch->in_range(v)) {
m_list->OnUpdate(controller, touch, m_items.size(), [this](auto i) {
SetIndex(i);
FireAction(Button::A);
return false;
}
return true;
});
}
}
auto PopupList::Draw(NVGcontext* vg, Theme* theme) -> void {
@@ -122,9 +115,7 @@ auto PopupList::Draw(NVGcontext* vg, Theme* theme) -> void {
gfx::drawRect(vg, 30.f, m_line_top, m_line_width, 1.f, theme->elements[ThemeEntryID_TEXT].colour);
gfx::drawRect(vg, 30.f, m_line_bottom, m_line_width, 1.f, theme->elements[ThemeEntryID_TEXT].colour);
gfx::drawScrollbar(vg, theme, 1250, m_line_top + 20, m_line_bottom - m_line_top - 40, m_index_offset, m_items.size(), 7);
m_list->Do(vg, theme, m_index_offset, m_items.size(), [this](auto* vg, auto* theme, auto v, auto i) {
m_list->Draw(vg, theme, m_items.size(), [this](auto* vg, auto* theme, auto v, auto i) {
const auto& [x, y, w, h] = v;
if (m_index == i) {
gfx::drawRect(vg, x - 4.f, y - 4.f, w + 8.f, h + 8.f, theme->elements[ThemeEntryID_SELECTED_OVERLAY].colour);
@@ -135,7 +126,6 @@ auto PopupList::Draw(NVGcontext* vg, Theme* theme) -> void {
gfx::drawRect(vg, x, y + h, w, 1.f, theme->elements[ThemeEntryID_TEXT].colour);
gfx::drawText(vg, x + m_text_xoffset, y + (h / 2.f), 20.f, m_items[i].c_str(), NULL, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->elements[ThemeEntryID_TEXT].colour);
}
return true;
});
Widget::Draw(vg, theme);
@@ -151,7 +141,7 @@ auto PopupList::OnFocusLost() noexcept -> void {
SetHidden(true);
}
void PopupList::SetIndex(std::size_t index) {
void PopupList::SetIndex(s64 index) {
m_index = index;
if (m_index > m_index_offset && m_index - m_index_offset >= 6) {

View File

@@ -119,7 +119,7 @@ auto ProgressBox::NewTransfer(const std::string& transfer) -> ProgressBox& {
return *this;
}
auto ProgressBox::UpdateTransfer(u64 offset, u64 size) -> ProgressBox& {
auto ProgressBox::UpdateTransfer(s64 offset, s64 size) -> ProgressBox& {
mutexLock(&m_mutex);
m_size = size;
m_offset = offset;

View File

@@ -126,7 +126,7 @@ SidebarEntryArray::SidebarEntryArray(std::string title, Items items, Callback cb
}
}
SidebarEntryArray::SidebarEntryArray(std::string title, Items items, Callback cb, std::size_t index)
SidebarEntryArray::SidebarEntryArray(std::string title, Items items, Callback cb, s64 index)
: SidebarEntryBase{std::forward<std::string>(title)}
, m_items{std::move(items)}
, m_callback{cb}
@@ -190,27 +190,12 @@ Sidebar::Sidebar(std::string title, std::string sub, Side side, Items&& items)
m_title_pos = Vec2{m_pos.x + 30.f, m_pos.y + 40.f};
m_base_pos = Vec4{GetX() + 30.f, GetY() + 170.f, m_pos.w - (30.f * 2.f), 70.f};
const Vec4 pos = DistanceBetweenY(m_top_bar, m_bottom_bar);
m_list = std::make_unique<List>(pos, m_base_pos);
// set button positions
SetUiButtonPos({m_pos.x + m_pos.w - 60.f, 675});
this->SetActions(
std::make_pair(Button::DOWN, Action{[this](){
auto index = m_index;
if (ScrollHelperDown(index, m_index_offset, 1, 1, 6, m_items.size())) {
SetIndex(index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
auto index = m_index;
if (ScrollHelperUp(index, m_index_offset, 1, 1, 6, m_items.size())) {
SetIndex(index);
}
}}),
// each item has it's own Action, but we take over B
std::make_pair(Button::B, Action{"Back"_i18n, [this](){
SetPop();
}})
);
const Vec4 pos = DistanceBetweenY(m_top_bar, m_bottom_bar);
m_list = std::make_unique<List>(1, 6, pos, m_base_pos);
m_list->SetScrollBarPos(GetX() + GetW() - 20, m_base_pos.y - 10, pos.h - m_base_pos.y + 48);
}
Sidebar::Sidebar(std::string title, std::string sub, Side side)
@@ -219,36 +204,16 @@ Sidebar::Sidebar(std::string title, std::string sub, Side side)
auto Sidebar::Update(Controller* controller, TouchInfo* touch) -> void {
m_items[m_index]->Update(controller, touch);
const auto old_index = m_index;
Widget::Update(controller, touch);
if (touch->is_clicked && touch->in_range(GetPos())) {
m_list->Do(m_index_offset, m_items.size(), [this, touch](auto* vg, auto* theme, auto v, auto i) {
const auto& [x, y, w, h] = v;
m_items[i]->SetY(y);
if (touch->in_range(m_items[i]->GetPos())) {
// if touched out of bounds, pop the sidebar and all widgets below it.
if (touch->is_clicked && !touch->in_range(GetPos())) {
App::PopToMenu();
} else {
m_list->OnUpdate(controller, touch, m_items.size(), [this](auto i) {
SetIndex(i);
FireAction(Button::A);
m_items[m_index]->FireAction(Button::A);
return false;
}
return true;
});
} else if (touch->is_clicked && !touch->in_range(GetPos())) {
App::PopToMenu();
}
Actions draw_actions{m_actions};
const auto& actions_ref = m_items[m_index]->GetActions();
draw_actions.insert(actions_ref.cbegin(), actions_ref.cend());
const auto d = GetUiButtons(draw_actions, m_pos.x + m_pos.w - 60.f);
for (auto& e : d) {
if (touch->is_clicked && touch->in_range(e.GetPos())) {
FireAction(e.m_button);
m_items[m_index]->FireAction(e.m_button);
}
}
if (m_items[m_index]->ShouldPop()) {
@@ -265,28 +230,13 @@ auto Sidebar::Draw(NVGcontext* vg, Theme* theme) -> void {
gfx::drawRect(vg, m_top_bar, theme->elements[ThemeEntryID_TEXT].colour);
gfx::drawRect(vg, m_bottom_bar, theme->elements[ThemeEntryID_TEXT].colour);
// only draw scrollbar if needed
const auto dist = DistanceBetweenY(m_top_bar, m_bottom_bar);
gfx::drawScrollbar(vg, theme, GetX() + GetW() - 20, m_base_pos.y - 10, dist.h - m_base_pos.y + 48, m_index_offset, m_items.size(), 6);
Widget::Draw(vg, theme);
m_list->Do(vg, theme, m_index_offset, m_items.size(), [this](auto* vg, auto* theme, auto v, auto i) {
m_list->Draw(vg, theme, m_items.size(), [this](auto* vg, auto* theme, auto v, auto i) {
const auto& [x, y, w, h] = v;
m_items[i]->SetY(y);
m_items[i]->Draw(vg, theme);
return true;
});
// draw the buttons. fetch the actions from current item and insert into array.
Actions draw_actions{m_actions};
const auto& actions_ref = m_items[m_index]->GetActions();
draw_actions.insert(actions_ref.cbegin(), actions_ref.cend());
auto d = GetUiButtons(draw_actions, m_pos.x + m_pos.w - 60.f);
for (auto& e : d) {
e.Draw(vg, theme);
}
// gfx::drawButtons(vg, draw_actions, theme->elements[ThemeEntryID_TEXT].colour, m_pos.x + m_pos.w - 60.f);
}
auto Sidebar::OnFocusGained() noexcept -> void {
@@ -304,10 +254,13 @@ void Sidebar::Add(std::shared_ptr<SidebarEntryBase> entry) {
m_items.back()->SetPos(m_base_pos);
// give focus to first entry.
if (m_items.size() == 1) {
m_items[m_index]->OnFocusGained();
SetupButtons();
}
}
void Sidebar::SetIndex(std::size_t index) {
void Sidebar::SetIndex(s64 index) {
// if we moved
if (m_index != index) {
m_items[m_index]->OnFocusLost();
@@ -317,7 +270,38 @@ void Sidebar::SetIndex(std::size_t index) {
if (m_index > m_index_offset && m_index - m_index_offset >= 5) {
m_index_offset = m_index - 5;
}
SetupButtons();
}
}
void Sidebar::SetupButtons() {
RemoveActions();
// add entry actions
for (const auto& [button, action] : m_items[m_index]->GetActions()) {
SetAction(button, action);
}
// add default actions, overriding if needed.
this->SetActions(
std::make_pair(Button::DOWN, Action{[this](){
auto index = m_index;
if (m_list->ScrollDown(index, 1, m_items.size())) {
SetIndex(index);
}
}}),
std::make_pair(Button::UP, Action{[this](){
auto index = m_index;
if (m_list->ScrollUp(index, 1, m_items.size())) {
SetIndex(index);
}
}}),
// each item has it's own Action, but we take over B
std::make_pair(Button::B, Action{"Back"_i18n, [this](){
SetPop();
}})
);
}
} // namespace sphaira::ui

View File

@@ -24,20 +24,24 @@ void Widget::Update(Controller* controller, TouchInfo* touch) {
App::PlaySoundEffect(SoundEffect_Focus);
}
action.Invoke(true);
break;
}
else if ((action.m_type & ActionType::UP) && controller->GotUp(button)) {
action.Invoke(false);
break;
}
else if ((action.m_type & ActionType::HELD) && controller->GotHeld(button)) {
action.Invoke(true);
break;
}
}
auto draw_actions = GetUiButtons();
for (auto& e : draw_actions) {
if (touch->is_clicked && touch->in_range(e.GetPos())) {
FireAction(e.m_button);
log_write("got click: %s\n", e.m_action.m_hint.c_str());
FireAction(e.m_button);
break;
}
}
}
@@ -75,71 +79,15 @@ auto Widget::FireAction(Button b, u8 type) -> bool {
return false;
}
auto Widget::ScrollHelperDown(u64& index, u64& start, u64 step, s64 row, s64 page, u64 size) -> bool {
const auto old_index = index;
if (!size) {
return false;
}
if (index + step < size) {
index += step;
} else {
index = size - 1;
}
if (index != old_index) {
App::PlaySoundEffect(SoundEffect_Scroll);
s64 delta = index - old_index;
if (index - start >= page) {
do {
start += row;
delta -= row;
} while (delta > 0 && start + page < size);
}
return true;
}
return false;
}
auto Widget::ScrollHelperUp(u64& index, u64& start, s64 step, s64 row, s64 page, s64 size) -> bool {
const auto old_index = index;
if (!size) {
return false;
}
if (index >= step) {
index -= step;
} else {
index = 0;
}
if (index != old_index) {
App::PlaySoundEffect(SoundEffect_Scroll);
// if ()
while (index < start) {
// log_write("moved up\n");
start -= row;
}
return true;
}
return false;
}
auto Widget::GetUiButtons(const Actions& actions, float x, float y) -> uiButtons {
auto Widget::GetUiButtons() const -> uiButtons {
auto vg = App::GetVg();
auto [x, y] = m_button_pos;
uiButtons draw_actions;
draw_actions.reserve(actions.size());
draw_actions.reserve(m_actions.size());
// build array
for (const auto& [button, action] : actions) {
for (const auto& [button, action] : m_actions) {
if (action.IsHidden() || action.m_hint.empty()) {
continue;
}
@@ -183,8 +131,4 @@ auto Widget::GetUiButtons(const Actions& actions, float x, float y) -> uiButtons
return draw_actions;
}
auto Widget::GetUiButtons() const -> uiButtons {
return GetUiButtons(m_actions);
}
} // namespace sphaira::ui