Move refocus logic to from TitlesTab to LayeredErrorFrame.

* Refocus logic now also takes care of updating Borealis' internal focus stack if a view has been pushed on top of the root view using a ListItem from LayeredErrorFrame.
* Further modifications to Borealis are used to store a pointer to the corresponding sidebar item in LayeredErrorFrame objects.
* The UI code now always uses C++-style casts.
* Git commit information is now displayed next to the nxdumptool release version.
* The applet mode warning is now displayed centered at the top.
* Title IDs in title lists are now displayed at the right of each entry.
This commit is contained in:
Pablo Curiel 2021-06-25 16:13:39 -04:00
parent c683e77873
commit c50840ec7c
6 changed files with 166 additions and 68 deletions

View File

@ -31,14 +31,24 @@ namespace nxdt::views
/* Extended class to switch between ErrorFrame and List views on demand. */
class LayeredErrorFrame: public brls::LayerView
{
private:
brls::SidebarItem *sidebar_item = nullptr;
protected:
ErrorFrame *error_frame = nullptr;
brls::List *list = nullptr;
void SwitchLayerView(bool use_error_frame);
bool IsListItemFocused(void);
int GetFocusStackViewIndex(void);
bool UpdateFocusStackViewAtIndex(int index, brls::View *view);
void SwitchLayerView(bool use_error_frame, bool update_focused_view = false, bool update_focus_stack = true);
public:
LayeredErrorFrame(std::string msg = "");
void SetParentSidebarItem(brls::SidebarItem *sidebar_item);
};
}

@ -1 +1 @@
Subproject commit 7de16249a693e187e7249404d41e0da8a5572bf6
Subproject commit 7f70ea3dd36dcdc6ca6c01e67632452a4da6d253

View File

@ -34,24 +34,110 @@ namespace nxdt::views
this->addLayer(this->list);
}
void LayeredErrorFrame::SwitchLayerView(bool use_error_frame)
void LayeredErrorFrame::SetParentSidebarItem(brls::SidebarItem *sidebar_item)
{
int index = this->getLayerIndex();
int new_index = (index ^ 1);
brls::View *cur_focus = brls::Application::getCurrentFocus();
if (sidebar_item) this->sidebar_item = sidebar_item;
}
bool LayeredErrorFrame::IsListItemFocused(void)
{
brls::View *cur_view = brls::Application::getCurrentFocus();
size_t cur_list_count = this->list->getViewsCount();
/* Don't proceed if we're already at the desired view layer. */
if (index < 0 || index > 1 || (use_error_frame && index == 0) || (!use_error_frame && index == 1)) return;
/* Focus the sidebar if we're currently focusing an element from our List and we're about to switch to the error frame. */
if (use_error_frame && cur_focus && cur_focus->hasParent())
if (cur_list_count)
{
brls::View *cur_focus_parent = cur_focus->getParent();
if (cur_focus_parent && cur_focus_parent->hasParent() && cur_focus_parent->getParent() == this->list) brls::Application::onGamepadButtonPressed(GLFW_GAMEPAD_BUTTON_DPAD_LEFT, false);
while(cur_view)
{
if (cur_view == this->list) return true;
cur_view = cur_view->getParent();
}
}
/* Change layer view. */
this->changeLayer(new_index);
this->invalidate(true);
return false;
}
int LayeredErrorFrame::GetFocusStackViewIndex(void)
{
size_t cur_list_count = this->list->getViewsCount();
std::vector<brls::View*> *focus_stack = brls::Application::getFocusStack();
if (cur_list_count && focus_stack)
{
size_t focus_stack_size = focus_stack->size();
for(size_t i = 0; i < focus_stack_size; i++)
{
for(size_t j = 0; j < cur_list_count; j++)
{
if (this->list->getChild(j) == focus_stack->at(i)) return static_cast<int>(i);
}
}
}
return -1;
}
bool LayeredErrorFrame::UpdateFocusStackViewAtIndex(int index, brls::View *view)
{
std::vector<brls::View*> *focus_stack = brls::Application::getFocusStack();
if (!focus_stack || index < 0) return false;
size_t focus_stack_size = focus_stack->size();
if (index >= static_cast<int>(focus_stack_size)) return false;
focus_stack->at(index) = view;
brls::Logger::debug("Focus stack updated");
return true;
}
void LayeredErrorFrame::SwitchLayerView(bool use_error_frame, bool update_focused_view, bool update_focus_stack)
{
int cur_index = this->getLayerIndex();
int new_index = (use_error_frame ? 0 : 1);
size_t cur_list_count = this->list->getViewsCount();
brls::View *first_child = nullptr;
int focus_stack_index = this->GetFocusStackViewIndex();
bool focus_stack_updated = false;
if (cur_list_count)
{
/* Get pointer to the first list item. */
first_child = this->list->getChild(0);
/* Update focus stack information, if needed. */
if (update_focus_stack && focus_stack_index > -1) focus_stack_updated = this->UpdateFocusStackViewAtIndex(focus_stack_index, use_error_frame ? this->sidebar_item : first_child);
}
if (!focus_stack_updated)
{
/* Check if the user is currently focusing a list item. */
if (!update_focused_view) update_focused_view = this->IsListItemFocused();
if (update_focused_view)
{
/* Update focused view. */
if (use_error_frame || !cur_list_count)
{
/* Move focus to the sidebar item. */
brls::Application::giveFocus(this->sidebar_item);
} else {
/* Move focus to the first list item. */
brls::Application::giveFocus(first_child);
/* Make sure to call willAppear() on our list to update the scrolling accordingly. */
this->list->willAppear(true);
}
}
}
/* Change layer view only if the new index is different. */
if (cur_index != new_index)
{
this->changeLayer(new_index);
this->invalidate(true);
}
}
}

View File

@ -33,10 +33,12 @@ namespace nxdt::views
{
RootView::RootView(void) : brls::TabFrame()
{
int material = brls::Application::getFontStash()->material;
/* Set UI properties. */
this->setTitle(APP_TITLE);
this->setIcon(BOREALIS_ASSET("icon/" APP_TITLE ".jpg"));
this->setFooterText("v" APP_VERSION);
this->setFooterText("v" APP_VERSION " (" GIT_REV ")");
/* Check if we're running under applet mode. */
this->applet_mode = utilsAppletModeCheck();
@ -44,20 +46,21 @@ namespace nxdt::views
/* Create labels. */
this->applet_mode_lbl = new brls::Label(brls::LabelStyle::HINT, "root_view/applet_mode"_i18n);
this->applet_mode_lbl->setColor(nvgRGB(255, 0, 0));
this->applet_mode_lbl->setFontSize(brls::Application::getStyle()->AppletFrame.titleSize);
this->applet_mode_lbl->setParent(this);
this->time_lbl = new brls::Label(brls::LabelStyle::SMALL, "");
this->time_lbl->setParent(this);
this->battery_icon = new brls::Label(brls::LabelStyle::SMALL, "");
this->battery_icon->setFont(brls::Application::getFontStash()->material);
this->battery_icon->setFont(material);
this->battery_icon->setParent(this);
this->battery_percentage = new brls::Label(brls::LabelStyle::SMALL, "");
this->battery_percentage->setParent(this);
this->connection_icon = new brls::Label(brls::LabelStyle::SMALL, "");
this->connection_icon->setFont(brls::Application::getFontStash()->material);
this->connection_icon->setFont(material);
this->connection_icon->setParent(this);
this->connection_status_lbl = new brls::Label(brls::LabelStyle::SMALL, "");
@ -71,21 +74,40 @@ namespace nxdt::views
this->usb_host_task = new nxdt::tasks::UsbHostTask();
/* Add tabs. */
this->addTab("root_view/tabs/gamecard"_i18n, new GameCardTab(this->gc_status_task));
GameCardTab *gamecard_tab = new GameCardTab(this->gc_status_task);
this->addTab("root_view/tabs/gamecard"_i18n, gamecard_tab);
gamecard_tab->SetParentSidebarItem(static_cast<brls::SidebarItem*>(this->sidebar->getChild(this->sidebar->getViewsCount() - 1)));
this->addSeparator();
this->addTab("root_view/tabs/user_titles"_i18n, new TitlesTab(this->title_task, false));
this->addTab("root_view/tabs/system_titles"_i18n, new TitlesTab(this->title_task, true));
TitlesTab *user_titles_tab = new TitlesTab(this->title_task, false);
this->addTab("root_view/tabs/user_titles"_i18n, user_titles_tab);
user_titles_tab->SetParentSidebarItem(static_cast<brls::SidebarItem*>(this->sidebar->getChild(this->sidebar->getViewsCount() - 1)));
TitlesTab *system_titles_tab = new TitlesTab(this->title_task, true);
this->addTab("root_view/tabs/system_titles"_i18n, system_titles_tab);
system_titles_tab->SetParentSidebarItem(static_cast<brls::SidebarItem*>(this->sidebar->getChild(this->sidebar->getViewsCount() - 1)));
this->addSeparator();
this->addTab("root_view/tabs/options"_i18n, new brls::Rectangle(nvgRGB(255, 255, 0)));
this->addSeparator();
this->addTab("root_view/tabs/about"_i18n, new AboutTab());
/* Subscribe to status info event. */
this->status_info_task_sub = this->status_info_task->RegisterListener([this](const nxdt::tasks::StatusInfoData *status_info_data) {
/* Update time label. */
bool is_am = true;
struct tm *timeinfo = status_info_data->timeinfo;
u32 charge_percentage = status_info_data->charge_percentage;
PsmChargerType charger_type = status_info_data->charger_type;
NifmInternetConnectionType connection_type = status_info_data->connection_type;
char *ip_addr = status_info_data->ip_addr;
/* Update time label. */
timeinfo->tm_mon++;
timeinfo->tm_year += 1900;
@ -107,18 +129,12 @@ namespace nxdt::views
is_am ? "AM" : "PM"));
/* Update battery labels. */
u32 charge_percentage = status_info_data->charge_percentage;
PsmChargerType charger_type = status_info_data->charger_type;
this->battery_icon->setText(charger_type != PsmChargerType_Unconnected ? "\uE1A3" : (charge_percentage <= 15 ? "\uE19C" : "\uE1A4"));
this->battery_icon->setColor(charger_type != PsmChargerType_Unconnected ? nvgRGB(0, 255, 0) : (charge_percentage <= 15 ? nvgRGB(255, 0, 0) : brls::Application::getTheme()->textColor));
this->battery_percentage->setText(fmt::format("{}%", charge_percentage));
/* Update network label. */
NifmInternetConnectionType connection_type = status_info_data->connection_type;
char *ip_addr = status_info_data->ip_addr;
/* Update network labels. */
this->connection_icon->setText(!connection_type ? "\uE195" : (connection_type == NifmInternetConnectionType_WiFi ? "\uE63E" : "\uE8BE"));
this->connection_status_lbl->setText(ip_addr ? std::string(ip_addr) : "root_view/not_connected"_i18n);
@ -173,8 +189,8 @@ namespace nxdt::views
/* Applet mode label. */
this->applet_mode_lbl->invalidate(true);
this->applet_mode_lbl->setBoundaries(
this->x + (this->width / 4) - (this->applet_mode_lbl->getWidth() / 2),
this->y + this->height - (style->AppletFrame.footerHeight / 2),
this->x + (this->width - this->applet_mode_lbl->getWidth()) / 2,
this->y + (style->AppletFrame.headerHeightRegular / 2) + style->AppletFrame.titleOffset,
this->applet_mode_lbl->getWidth(),
this->applet_mode_lbl->getHeight());
}

View File

@ -56,7 +56,7 @@ namespace nxdt::tasks
/* Get network connection status. */
u32 signal_strength = 0;
NifmInternetConnectionStatus connection_status = (NifmInternetConnectionStatus)0;
NifmInternetConnectionStatus connection_status = static_cast<NifmInternetConnectionStatus>(0);
Result rc = nifmGetInternetConnectionStatus(&(status_info_data->connection_type), &signal_strength, &connection_status);
if (R_SUCCEEDED(rc))
@ -70,7 +70,7 @@ namespace nxdt::tasks
status_info_data->ip_addr = NULL;
}
} else {
status_info_data->connection_type = (NifmInternetConnectionType)0;
status_info_data->connection_type = static_cast<NifmInternetConnectionType>(0);
status_info_data->ip_addr = NULL;
}

View File

@ -65,14 +65,18 @@ namespace nxdt::views
app_metadata(app_metadata),
is_system(is_system)
{
brls::Style* style = brls::Application::getStyle();
/* Set sublabel. */
this->subLabel = (!this->is_system ? std::string(app_metadata->lang_entry.author) : fmt::format("{:016X}", this->app_metadata->title_id));
this->setHeight(style->List.Item.heightWithSubLabel);
if (!this->is_system)
{
this->subLabel = std::string(app_metadata->lang_entry.author);
this->setHeight(brls::Application::getStyle()->List.Item.heightWithSubLabel);
}
/* Set thumbnail (if needed). */
if (app_metadata->icon && app_metadata->icon_size) this->setThumbnail(app_metadata->icon, app_metadata->icon_size);
/* Set value. */
this->setValue(fmt::format("{:016X}", this->app_metadata->title_id), false, false);
}
TitlesTab::TitlesTab(nxdt::tasks::TitleTask *title_task, bool is_system) : LayeredErrorFrame("titles_tab/no_titles_available"_i18n), title_task(title_task), is_system(is_system)
@ -100,39 +104,25 @@ namespace nxdt::views
void TitlesTab::PopulateList(const nxdt::tasks::TitleApplicationMetadataVector* app_metadata)
{
if (!app_metadata) return;
/* Block inputs while we're doing our thing. */
brls::Application::blockInputs();
ON_SCOPE_EXIT { brls::Application::unblockInputs(); };
if (!app_metadata) return;
bool refocus = false;
/* Populate variables. */
size_t app_metadata_count = app_metadata->size();
bool update_focused_view = this->IsListItemFocused();
int focus_stack_index = this->GetFocusStackViewIndex();
if (app_metadata_count)
{
/* Determine if we need to refocus after updating the list. */
brls::View *cur_view = brls::Application::getCurrentFocus();
while(cur_view)
{
if (cur_view == this->list)
{
refocus = true;
break;
}
cur_view = cur_view->getParent();
}
} else {
/* If we need to, switch to the error frame *before* cleaning up our list. */
this->SwitchLayerView(true);
}
/* If needed, switch to the error frame *before* cleaning up our list. */
if (!app_metadata_count) this->SwitchLayerView(true);
/* Clear list. */
this->list->clear();
this->list->invalidate(true);
/* Immediately return if we have no user application metadata. */
/* Return immediately if we have no user application metadata. */
if (!app_metadata_count) return;
/* Populate list. */
@ -176,15 +166,11 @@ namespace nxdt::views
this->list->addView(title);
}
/* Update focus stack, if needed. */
if (focus_stack_index > -1) this->UpdateFocusStackViewAtIndex(focus_stack_index, this->list->getChild(0));
/* Switch to the list. */
this->list->invalidate(true);
this->SwitchLayerView(false);
/* Refocus, if needed. */
if (refocus)
{
brls::Application::giveFocus(this->list->getChild(0));
this->list->willAppear(true);
}
this->SwitchLayerView(false, update_focused_view, focus_stack_index < 0);
}
}