Implemented two more eager-construction optimizations:

- Each option now uses one complete XML template containing both its row and control.
  - Those immutable XML templates are parsed once and reused through loadLayoutNodes().
This commit is contained in:
Martín Lucas Golini
2026-08-24 16:29:07 -03:00
parent bc98615590
commit 0d2e09f654
2 changed files with 53 additions and 50 deletions
+52 -49
View File
@@ -4,6 +4,8 @@
#include "uitreeviewfs.hpp" #include "uitreeviewfs.hpp"
#include <deque> #include <deque>
#include <limits> #include <limits>
#define PUGIXML_HEADER_ONLY
#include <pugixml/pugixml.hpp>
using namespace EE::UI::Models; using namespace EE::UI::Models;
@@ -238,17 +240,36 @@ static constexpr const char* SETTINGS_PANEL_LAYOUT = R"xml(
</vbox> </vbox>
)xml"; )xml";
static constexpr const char* SETTINGS_ROW_LAYOUT = R"xml( static std::string settingsRowLayout( std::string_view control ) {
return R"xml(
<vbox lw="mp" lh="wc" class="settings_option"> <vbox lw="mp" lh="wc" class="settings_option">
<hbox lw="mp" lh="wc" class="settings_option_content"> <hbox lw="mp" lh="wc" class="settings_option_content">
<vbox lw="0" lw8="1" lh="wc"> <vbox lw="0" lw8="1" lh="wc">
<TextView id="setting_name" lw="mp" lh="wc" class="settings_option_name" focusable="false" /> <TextView id="setting_name" lw="mp" lh="wc" class="settings_option_name" focusable="false" />
<TextView id="setting_description" lw="mp" lh="wc" class="settings_option_description" focusable="false" /> <TextView id="setting_description" lw="mp" lh="wc" class="settings_option_description" focusable="false" />
</vbox> </vbox>
<hbox id="setting_control" lw="wc" lh="wc" class="settings_option_control" /> <hbox id="setting_control" lw="wc" lh="wc" class="settings_option_control">
)xml" + std::string( control ) +
R"xml(
</hbox>
</hbox> </hbox>
</vbox> </vbox>
)xml"; )xml";
}
class SettingsLayoutTemplate {
public:
explicit SettingsLayoutTemplate( const std::string& layout ) {
[[maybe_unused]] auto result =
mDocument.load_string( layout.c_str(), pugi::parse_default | pugi::parse_ws_pcdata );
eeASSERT( result );
}
pugi::xml_node root() const { return mDocument.first_child(); }
private:
pugi::xml_document mDocument;
};
static constexpr const char* SETTINGS_CATEGORY_HEADING_LAYOUT = R"xml( static constexpr const char* SETTINGS_CATEGORY_HEADING_LAYOUT = R"xml(
<TextView lw="mp" lh="wc" class="settings_category_heading" visible="false" focusable="false" /> <TextView lw="mp" lh="wc" class="settings_category_heading" visible="false" focusable="false" />
@@ -256,16 +277,18 @@ static constexpr const char* SETTINGS_CATEGORY_HEADING_LAYOUT = R"xml(
static constexpr const char* SETTINGS_SUBCATEGORY_HEADING_LAYOUT = R"xml( static constexpr const char* SETTINGS_SUBCATEGORY_HEADING_LAYOUT = R"xml(
<TextView lw="mp" lh="wc" class="settings_subcategory_heading" visible="false" focusable="false" /> <TextView lw="mp" lh="wc" class="settings_subcategory_heading" visible="false" focusable="false" />
)xml"; )xml";
static constexpr const char* SETTINGS_BOOL_LAYOUT = R"xml(<CheckBox class="settings_bool" />)xml"; static const SettingsLayoutTemplate SETTINGS_BOOL_ROW_LAYOUT( settingsRowLayout(
static constexpr const char* SETTINGS_CHOICE_LAYOUT = R"xml(<CheckBox id="setting_control_widget" class="settings_bool" />)xml" ) );
R"xml(<DropDownModelList class="settings_choice" />)xml"; static const SettingsLayoutTemplate SETTINGS_CHOICE_ROW_LAYOUT( settingsRowLayout(
static constexpr const char* SETTINGS_EDITABLE_CHOICE_LAYOUT = R"xml(<DropDownModelList id="setting_control_widget" class="settings_choice" />)xml" ) );
R"xml(<ComboBox class="settings_editable_choice" popup-to-root="true" />)xml"; static const SettingsLayoutTemplate SETTINGS_EDITABLE_CHOICE_ROW_LAYOUT( settingsRowLayout(
static constexpr const char* SETTINGS_INTEGER_LAYOUT = R"xml(<ComboBox id="setting_control_widget" class="settings_editable_choice" popup-to-root="true" />)xml" ) );
R"xml(<SpinBox class="settings_integer" />)xml"; static const SettingsLayoutTemplate SETTINGS_INTEGER_ROW_LAYOUT( settingsRowLayout(
static constexpr const char* SETTINGS_TEXT_LAYOUT = R"xml(<TextInput class="settings_text" />)xml"; R"xml(<SpinBox id="setting_control_widget" class="settings_integer" />)xml" ) );
static constexpr const char* SETTINGS_ACTION_LAYOUT = static const SettingsLayoutTemplate SETTINGS_TEXT_ROW_LAYOUT( settingsRowLayout(
R"xml(<PushButton class="settings_action" />)xml"; R"xml(<TextInput id="setting_control_widget" class="settings_text" />)xml" ) );
static const SettingsLayoutTemplate SETTINGS_ACTION_ROW_LAYOUT( settingsRowLayout(
R"xml(<PushButton id="setting_control_widget" class="settings_action" />)xml" ) );
static void disableTabFocusTree( Node* node ) { static void disableTabFocusTree( Node* node ) {
if ( node->isWidget() ) if ( node->isWidget() )
@@ -485,8 +508,9 @@ void SettingsPanel::setupCategories( PanelState& panel ) {
} ); } );
} }
UIWidget* SettingsPanel::createRow( PanelState& panel, SettingBinding& binding ) { UIWidget* SettingsPanel::createRow( PanelState& panel, SettingBinding& binding,
auto* row = mApp->getUISceneNode()->loadLayoutFromString( SETTINGS_ROW_LAYOUT, panel.settings ); pugi::xml_node layout ) {
auto* row = mApp->getUISceneNode()->loadLayoutNodes( layout, panel.settings, 0 );
row->setId( "setting_" + binding.id ); row->setId( "setting_" + binding.id );
row->find<UITextView>( "setting_name" )->setText( binding.name ); row->find<UITextView>( "setting_name" )->setText( binding.name );
auto* description = row->find<UITextView>( "setting_description" ); auto* description = row->find<UITextView>( "setting_description" );
@@ -517,12 +541,9 @@ void SettingsPanel::addBool( PanelState& panel, SettingBinding binding, std::fun
} }
UICheckBox* SettingsPanel::createBoolControl( PanelState& panel, SettingBinding& binding ) { UICheckBox* SettingsPanel::createBoolControl( PanelState& panel, SettingBinding& binding ) {
auto* row = createRow( panel, binding ); auto* row = createRow( panel, binding, SETTINGS_BOOL_ROW_LAYOUT.root() );
row->addClass( "settings_boolean_option" ); row->addClass( "settings_boolean_option" );
auto* check = mApp->getUISceneNode() auto* check = row->find<UICheckBox>( "setting_control_widget" );
->loadLayoutFromString( SETTINGS_BOOL_LAYOUT,
row->find<UILinearLayout>( "setting_control" ) )
->asType<UICheckBox>();
auto toggle = [check]( const Event* ) { check->setChecked( !check->isChecked() ); }; auto toggle = [check]( const Event* ) { check->setChecked( !check->isChecked() ); };
panel.connections += panel.connections +=
row->find<UITextView>( "setting_name" )->connect( Event::MouseClick, toggle ); row->find<UITextView>( "setting_name" )->connect( Event::MouseClick, toggle );
@@ -535,11 +556,8 @@ void SettingsPanel::addChoice( PanelState& panel, SettingBinding binding,
const std::vector<String>& choices, std::function<size_t()> get, const std::vector<String>& choices, std::function<size_t()> get,
std::function<void( size_t )> set, std::function<void( size_t )> set,
std::vector<String> choiceDescriptions ) { std::vector<String> choiceDescriptions ) {
auto* row = createRow( panel, binding ); auto* row = createRow( panel, binding, SETTINGS_CHOICE_ROW_LAYOUT.root() );
auto* dropDown = mApp->getUISceneNode() auto* dropDown = row->find<UIDropDownModelList>( "setting_control_widget" );
->loadLayoutFromString( SETTINGS_CHOICE_LAYOUT,
row->find<UILinearLayout>( "setting_control" ) )
->asType<UIDropDownModelList>();
auto model = ItemListOwnerModel<String>::create( choices ); auto model = ItemListOwnerModel<String>::create( choices );
dropDown->setModel( model ); dropDown->setModel( model );
const size_t selected = get(); const size_t selected = get();
@@ -566,11 +584,8 @@ void SettingsPanel::addEditableChoice( PanelState& panel, SettingBinding binding
const std::vector<String>& choices, const std::vector<String>& choices,
std::function<String()> get, std::function<String()> get,
std::function<bool( const String& )> set ) { std::function<bool( const String& )> set ) {
auto* row = createRow( panel, binding ); auto* row = createRow( panel, binding, SETTINGS_EDITABLE_CHOICE_ROW_LAYOUT.root() );
auto* combo = mApp->getUISceneNode() auto* combo = row->find<UIComboBox>( "setting_control_widget" );
->loadLayoutFromString( SETTINGS_EDITABLE_CHOICE_LAYOUT,
row->find<UILinearLayout>( "setting_control" ) )
->asType<UIComboBox>();
for ( const auto& choice : choices ) for ( const auto& choice : choices )
combo->getListBox()->addListBoxItem( choice ); combo->getListBox()->addListBoxItem( choice );
combo->setText( get() ); combo->setText( get() );
@@ -589,11 +604,8 @@ void SettingsPanel::addEditableChoice( PanelState& panel, SettingBinding binding
void SettingsPanel::addInteger( PanelState& panel, SettingBinding binding, int min, int max, void SettingsPanel::addInteger( PanelState& panel, SettingBinding binding, int min, int max,
std::function<int()> get, std::function<void( int )> set ) { std::function<int()> get, std::function<void( int )> set ) {
auto* row = createRow( panel, binding ); auto* row = createRow( panel, binding, SETTINGS_INTEGER_ROW_LAYOUT.root() );
auto* spin = mApp->getUISceneNode() auto* spin = row->find<UISpinBox>( "setting_control_widget" );
->loadLayoutFromString( SETTINGS_INTEGER_LAYOUT,
row->find<UILinearLayout>( "setting_control" ) )
->asType<UISpinBox>();
spin->setMinValue( min )->setMaxValue( max ); spin->setMinValue( min )->setMaxValue( max );
spin->unsetTabFocusable(); spin->unsetTabFocusable();
spin->getButtonPushUp()->asType<UIWidget>()->unsetTabFocusable(); spin->getButtonPushUp()->asType<UIWidget>()->unsetTabFocusable();
@@ -610,11 +622,8 @@ void SettingsPanel::addText( PanelState& panel, SettingBinding binding,
std::function<std::string()> get, std::function<std::string()> get,
std::function<bool( const std::string& )> set, std::function<bool( const std::string& )> set,
bool commitOnFocusLoss ) { bool commitOnFocusLoss ) {
auto* row = createRow( panel, binding ); auto* row = createRow( panel, binding, SETTINGS_TEXT_ROW_LAYOUT.root() );
auto* input = mApp->getUISceneNode() auto* input = row->find<UITextInput>( "setting_control_widget" );
->loadLayoutFromString( SETTINGS_TEXT_LAYOUT,
row->find<UILinearLayout>( "setting_control" ) )
->asType<UITextInput>();
input->setText( String::fromUtf8( get() ) ); input->setText( String::fromUtf8( get() ) );
auto commit = [input, set = std::move( set )]( const Event* ) { auto commit = [input, set = std::move( set )]( const Event* ) {
std::string text = input->getText().toUtf8(); std::string text = input->getText().toUtf8();
@@ -636,11 +645,8 @@ void SettingsPanel::addText( PanelState& panel, SettingBinding binding,
void SettingsPanel::addFloat( PanelState& panel, SettingBinding binding, double min, double max, void SettingsPanel::addFloat( PanelState& panel, SettingBinding binding, double min, double max,
double step, std::function<double()> get, double step, std::function<double()> get,
std::function<void( double )> set ) { std::function<void( double )> set ) {
auto* row = createRow( panel, binding ); auto* row = createRow( panel, binding, SETTINGS_INTEGER_ROW_LAYOUT.root() );
auto* spin = mApp->getUISceneNode() auto* spin = row->find<UISpinBox>( "setting_control_widget" );
->loadLayoutFromString( SETTINGS_INTEGER_LAYOUT,
row->find<UILinearLayout>( "setting_control" ) )
->asType<UISpinBox>();
spin->setMinValue( min )->setMaxValue( max )->setClickStep( step ); spin->setMinValue( min )->setMaxValue( max )->setClickStep( step );
spin->allowFloatingPoint( true )->setValue( get() ); spin->allowFloatingPoint( true )->setValue( get() );
spin->unsetTabFocusable(); spin->unsetTabFocusable();
@@ -655,11 +661,8 @@ void SettingsPanel::addFloat( PanelState& panel, SettingBinding binding, double
void SettingsPanel::addAction( PanelState& panel, SettingBinding binding, const String& buttonText, void SettingsPanel::addAction( PanelState& panel, SettingBinding binding, const String& buttonText,
std::function<void()> action ) { std::function<void()> action ) {
auto* row = createRow( panel, binding ); auto* row = createRow( panel, binding, SETTINGS_ACTION_ROW_LAYOUT.root() );
auto* button = mApp->getUISceneNode() auto* button = row->find<UIPushButton>( "setting_control_widget" );
->loadLayoutFromString( SETTINGS_ACTION_LAYOUT,
row->find<UILinearLayout>( "setting_control" ) )
->asType<UIPushButton>();
button->setText( buttonText ); button->setText( buttonText );
panel.connections += button->connect( panel.connections += button->connect(
Event::MouseClick, [action = std::move( action )]( const Event* ) { action(); } ); Event::MouseClick, [action = std::move( action )]( const Event* ) { action(); } );
+1 -1
View File
@@ -88,7 +88,7 @@ class SettingsPanel {
std::function<double()> get, std::function<void( double )> set ); std::function<double()> get, std::function<void( double )> set );
void addAction( PanelState& state, SettingBinding binding, const String& buttonText, void addAction( PanelState& state, SettingBinding binding, const String& buttonText,
std::function<void()> action ); std::function<void()> action );
UIWidget* createRow( PanelState& state, SettingBinding& binding ); UIWidget* createRow( PanelState& state, SettingBinding& binding, pugi::xml_node layout );
UICheckBox* createBoolControl( PanelState& state, SettingBinding& binding ); UICheckBox* createBoolControl( PanelState& state, SettingBinding& binding );
void setCategoryEnabled( PanelState& state, const std::string& category, bool enabled, void setCategoryEnabled( PanelState& state, const std::string& category, bool enabled,
const std::string& excludedSetting = {} ); const std::string& excludedSetting = {} );