From 54c60d9a18d103011a12939c5029dd35a8e9e200 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 22 Aug 2024 21:44:06 +0200 Subject: Start on file chooser, page stack --- src/gui/Button.cpp | 4 ++ src/gui/ComboBox.cpp | 4 +- src/gui/FileChooser.cpp | 174 +++++++++++++++++++++++++++++++++++++++++++++ src/gui/GsrPage.cpp | 126 ++++++++++++++++++++++++++++++++ src/gui/List.cpp | 63 +++------------- src/gui/PageStack.cpp | 58 +++++++++++++++ src/gui/RadioButton.cpp | 4 +- src/gui/ScrollablePage.cpp | 44 ++---------- src/gui/SettingsPage.cpp | 82 ++++++++------------- 9 files changed, 410 insertions(+), 149 deletions(-) create mode 100644 src/gui/FileChooser.cpp create mode 100644 src/gui/GsrPage.cpp create mode 100644 src/gui/PageStack.cpp (limited to 'src/gui') diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp index e36d07b..20a771e 100644 --- a/src/gui/Button.cpp +++ b/src/gui/Button.cpp @@ -70,4 +70,8 @@ namespace gsr { void Button::set_border_scale(float scale) { border_scale = scale; } + + const std::string& Button::get_text() const { + return text.get_string(); + } } \ No newline at end of file diff --git a/src/gui/ComboBox.cpp b/src/gui/ComboBox.cpp index f23a323..b344c4e 100644 --- a/src/gui/ComboBox.cpp +++ b/src/gui/ComboBox.cpp @@ -146,14 +146,14 @@ namespace gsr { dirty = true; } - void ComboBox::set_selected_item(const std::string &id, bool trigger_event) { + void ComboBox::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) { for(size_t i = 0; i < items.size(); ++i) { auto &item = items[i]; if(item.id == id) { const size_t prev_selected_item = selected_item; selected_item = i; - if(trigger_event && selected_item != prev_selected_item && on_selection_changed) + if(trigger_event && (trigger_event_even_if_selection_not_changed || selected_item != prev_selected_item) && on_selection_changed) on_selection_changed(item.text.get_string(), item.id); break; diff --git a/src/gui/FileChooser.cpp b/src/gui/FileChooser.cpp new file mode 100644 index 0000000..8cc5d74 --- /dev/null +++ b/src/gui/FileChooser.cpp @@ -0,0 +1,174 @@ +#include "../../include/gui/FileChooser.hpp" +#include "../../include/gui/Utils.hpp" +#include "../../include/Theme.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gsr { + static const float current_directory_padding_top_scale = 0.004629f; + static const float current_directory_padding_bottom_scale = 0.004629f; + static const float current_directory_padding_left_scale = 0.004629f; + static const float current_directory_padding_right_scale = 0.004629f; + static const float spacing_between_current_directory_and_content = 0.015f; + static const int num_columns = 6; + static const float content_padding_top_scale = 0.015f; + static const float content_padding_bottom_scale = 0.015f; + static const float content_padding_left_scale = 0.015f; + static const float content_padding_right_scale = 0.015f; + + FileChooser::FileChooser(const char *start_directory, mgl::vec2f content_size) : + content_size(content_size), current_directory_text(start_directory, get_theme().body_font) + { + set_current_directory(start_directory); + } + + bool FileChooser::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) { + if(!visible) + return true; + + if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { + if(double_click_timer.get_elapsed_time_seconds() <= get_theme().double_click_timeout_seconds) { + ++times_clicked_within_timer; + } else { + times_clicked_within_timer = 1; + } + double_click_timer.restart(); + + selected_item = mouse_over_item; + + if(selected_item != -1 && times_clicked_within_timer > 0 && times_clicked_within_timer % 2 == 0) { + char new_directory[PATH_MAX]; + snprintf(new_directory, sizeof(new_directory), "%s/%s", current_directory_text.get_string().c_str(), folders[selected_item].get_string().c_str()); + set_current_directory(new_directory); + } + } + return true; + } + + void FileChooser::draw(mgl::Window &window, mgl::vec2f offset) { + mouse_over_item = -1; + + if(!visible) + return; + + const mgl::vec2f draw_pos = position + offset; + const mgl::vec2f current_directory_padding( + current_directory_padding_left_scale * get_theme().window_height + current_directory_padding_right_scale * get_theme().window_height, + current_directory_padding_top_scale * get_theme().window_height + current_directory_padding_bottom_scale * get_theme().window_height + ); + + mgl::Rectangle current_directory_background(mgl::vec2f(content_size.x, current_directory_text.get_bounds().size.y + current_directory_padding.y).floor()); + current_directory_background.set_color(mgl::Color(0, 0, 0, 120)); + current_directory_background.set_position(draw_pos.floor()); + window.draw(current_directory_background); + + current_directory_text.set_color(get_theme().text_color); + current_directory_text.set_position((draw_pos + mgl::vec2f(current_directory_padding.x, current_directory_background.get_size().y * 0.5f - current_directory_text.get_bounds().size.y * 0.5f)).floor()); + window.draw(current_directory_text); + + mgl::Rectangle content_background(content_size.floor()); + content_background.set_color(mgl::Color(0, 0, 0, 120)); + content_background.set_position((draw_pos + mgl::vec2f(0.0f, current_directory_background.get_size().y + spacing_between_current_directory_and_content * get_theme().window_height)).floor()); + window.draw(content_background); + + const mgl::vec2f mouse_pos = window.get_mouse_position().to_vec2f(); + + const int content_padding_top = content_padding_top_scale * get_theme().window_height; + const int content_padding_bottom = content_padding_bottom_scale * get_theme().window_height; + const int content_padding_left = content_padding_left_scale * get_theme().window_height; + const int content_padding_right = content_padding_right_scale * get_theme().window_height; + + const float folder_width = (int)((content_size.x - (content_padding_left + content_padding_right) * num_columns) / num_columns); + const float width_per_item_after = content_padding_right + folder_width + content_padding_left; + mgl::vec2f folder_pos = content_background.get_position() + mgl::vec2f(content_padding_left, content_padding_top); + for(int i = 0; i < (int)folders.size(); ++i) { + auto &folder_text = folders[i]; + + mgl::Sprite folder_sprite(&get_theme().folder_texture); + folder_sprite.set_position(folder_pos.floor()); + folder_sprite.set_width((int)folder_width); + + const mgl::vec2f item_pos = folder_pos - mgl::vec2f(content_padding_left, content_padding_top); + const mgl::vec2f item_size = folder_sprite.get_size() + mgl::vec2f(content_padding_left + content_padding_right, content_padding_top + content_padding_bottom); + if(i == selected_item) { + mgl::Rectangle selected_item_background(item_size.floor()); + selected_item_background.set_position(item_pos.floor()); + selected_item_background.set_color(get_theme().tint_color); + window.draw(selected_item_background); + } + if(!has_parent_with_selected_child_widget() && mouse_over_item == -1 && mgl::FloatRect(item_pos, item_size).contains(mouse_pos)) { + // mgl::Rectangle selected_item_background(item_size.floor()); + // selected_item_background.set_position(item_pos.floor()); + // selected_item_background.set_color(mgl::Color(20, 20, 20, 150)); + // window.draw(selected_item_background); + const float border_scale = 0.0015f; + draw_rectangle_outline(window, item_pos.floor(), item_size.floor(), get_theme().tint_color, border_scale * get_theme().window_height); + mouse_over_item = i; + } + + window.draw(folder_sprite); + + // TODO: Dont allow text to go further left/right than item_pos (on the left side) and item_pos + item_size (on the right side). + folder_text.set_position(folder_sprite.get_position() + mgl::vec2f(folder_sprite.get_size().x * 0.5f - folder_text.get_bounds().size.x * 0.5f, folder_sprite.get_size().y)); + window.draw(folder_text); + + folder_pos.x += width_per_item_after; + if(folder_pos.x + folder_width > content_background.get_position().x + content_size.x) { + folder_pos.x = content_background.get_position().x + content_padding_left; + folder_pos.y += content_padding_bottom + folder_sprite.get_size().y + content_padding_top; + } + } + } + + mgl::vec2f FileChooser::get_size() { + if(!visible) + return {0.0f, 0.0f}; + + const mgl::vec2f current_directory_padding( + current_directory_padding_left_scale * get_theme().window_height + current_directory_padding_right_scale * get_theme().window_height, + current_directory_padding_top_scale * get_theme().window_height + current_directory_padding_bottom_scale * get_theme().window_height + ); + return {content_size.x, current_directory_text.get_bounds().size.y + current_directory_padding.y + spacing_between_current_directory_and_content * get_theme().window_height + content_size.y}; + } + + static bool is_dir(struct dirent *dir, const char *parent_directory) { + if(dir->d_type == DT_DIR) + return true; + + char filepath[PATH_MAX]; + snprintf(filepath, sizeof(filepath), "%s/%s", parent_directory, dir->d_name); + + struct stat st; + return stat(filepath, &st) != -1 && S_ISDIR(st.st_mode); + } + + void FileChooser::set_current_directory(const char *directory) { + folders.clear(); + selected_item = -1; + mouse_over_item = -1; + current_directory_text.set_string(directory); + + DIR *d = opendir(directory); + if(!d) { + fprintf(stderr, "gsr-overlay error: failed to open directory: %s, error: %s\n", directory, strerror(errno)); + return; + } + + struct dirent *dir = NULL; + while((dir = readdir(d)) != NULL) { + /* Ignore hidden files */ + if(dir->d_name[0] == '.' || !is_dir(dir, directory)) + continue; + folders.push_back(mgl::Text(dir->d_name, get_theme().body_font)); + } + + closedir(d); + } +} \ No newline at end of file diff --git a/src/gui/GsrPage.cpp b/src/gui/GsrPage.cpp new file mode 100644 index 0000000..db49ced --- /dev/null +++ b/src/gui/GsrPage.cpp @@ -0,0 +1,126 @@ +#include "../../include/gui/GsrPage.hpp" +#include "../../include/Theme.hpp" + +#include +#include +#include + +namespace gsr { + GsrPage::GsrPage() : + scrollable_body(mgl::vec2f(0.0f, 0.0f)), + back_button(&get_theme().title_font, "Back", + mgl::vec2f(get_theme().window_width / 10, get_theme().window_height / 15).floor(), get_theme().scrollable_page_bg_color), + label_text("Settings", get_theme().title_font) + { + //set_position(content_page_position); + const float margin = 0.02f; + set_margins(margin, margin, margin, margin); + back_button.set_border_scale(0.003f); + } + + bool GsrPage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f) { + if(!visible) + return true; + + if(!scrollable_body.on_event(event, window, mgl::vec2f(0.0f, 0.0f))) + return false; + + if(!back_button.on_event(event, window, mgl::vec2f(0.0f, 0.0f))) + return false; + + return true; + } + + void GsrPage::draw(mgl::Window &window, mgl::vec2f offset) { + if(!visible) + return; + + const int margin_top = margin_top_scale * get_theme().window_height; + const int margin_left = margin_left_scale * get_theme().window_height; + + const mgl::vec2f window_size = mgl::vec2f(get_theme().window_width, get_theme().window_height).floor(); + const mgl::vec2f content_page_size = get_size(); + const mgl::vec2f content_page_position = mgl::vec2f(window_size * 0.5f - content_page_size * 0.5f).floor(); + offset = content_page_position + mgl::vec2f(margin_left, get_border_size() + margin_top).floor(); + + mgl::Rectangle background(content_page_size); + background.set_position(content_page_position); + background.set_color(get_theme().scrollable_page_bg_color); + window.draw(background); + + mgl::Rectangle border(mgl::vec2f(content_page_size.x, get_border_size()).floor()); + border.set_position(content_page_position); + border.set_color(get_theme().tint_color); + window.draw(border); + + scrollable_body.set_position(offset); + scrollable_body.draw(window, mgl::vec2f(0.0f, 0.0f)); + + draw_page_label(window, content_page_position); + + back_button.set_position(content_page_position + mgl::vec2f(content_page_size.x + get_horizontal_spacing(), 0.0f).floor()); + back_button.draw(window, mgl::vec2f(0.0f, 0.0f)); + } + + void GsrPage::draw_page_label(mgl::Window &window, mgl::vec2f body_pos) { + const mgl::vec2f window_size = mgl::vec2f(get_theme().window_width, get_theme().window_height).floor(); + + mgl::Rectangle background(mgl::vec2f(window_size.x / 10, window_size.x / 10).floor()); + background.set_position(body_pos.floor() - mgl::vec2f(background.get_size().x + get_horizontal_spacing(), 0.0f).floor()); + background.set_color(mgl::Color(0, 0, 0, 255)); + window.draw(background); + + const int text_margin = background.get_size().y * 0.085; + label_text.set_position((background.get_position() + mgl::vec2f(background.get_size().x * 0.5f - label_text.get_bounds().size.x * 0.5f, text_margin)).floor()); + window.draw(label_text); + + mgl::Sprite icon(&get_theme().settings_texture); + icon.set_height((int)(background.get_size().y * 0.5f)); + icon.set_position((background.get_position() + background.get_size() * 0.5f - icon.get_size() * 0.5f).floor()); + window.draw(icon); + } + + mgl::vec2f GsrPage::get_size() { + if(!visible) + return {0.0f, 0.0f}; + + const mgl::vec2f window_size = mgl::vec2f(get_theme().window_width, get_theme().window_height).floor(); + const mgl::vec2f content_page_size = (window_size * mgl::vec2f(0.3333f, 0.7f)).floor(); + return content_page_size; + } + + mgl::vec2f GsrPage::get_inner_size() { + if(!visible) + return {0.0f, 0.0f}; + + const int margin_top = margin_top_scale * get_theme().window_height; + const int margin_bottom = margin_bottom_scale * get_theme().window_height; + const int margin_left = margin_left_scale * get_theme().window_height; + const int margin_right = margin_right_scale * get_theme().window_height; + return get_size() - mgl::vec2f(margin_left + margin_right, margin_top + margin_bottom + get_border_size()); + } + + void GsrPage::add_widget(std::unique_ptr widget) { + scrollable_body.add_widget(std::move(widget)); + } + + void GsrPage::set_margins(float top, float bottom, float left, float right) { + margin_top_scale = top; + margin_bottom_scale = bottom; + margin_left_scale = left; + margin_right_scale = right; + scrollable_body.set_size(get_inner_size()); + } + + void GsrPage::set_on_back_button_click(std::function on_click_handler) { + back_button.on_click = std::move(on_click_handler); + } + + float GsrPage::get_border_size() const { + return 0.004f * get_theme().window_height; + } + + float GsrPage::get_horizontal_spacing() const { + return get_theme().window_width / 50; + } +} \ No newline at end of file diff --git a/src/gui/List.cpp b/src/gui/List.cpp index acdb695..849329f 100644 --- a/src/gui/List.cpp +++ b/src/gui/List.cpp @@ -14,55 +14,25 @@ namespace gsr { if(!visible) return true; - inside_event_handler = true; // We want to store the selected child widget since it can change in the event loop below Widget *selected_widget = selected_child_widget; if(selected_widget) { - if(!selected_widget->on_event(event, window, mgl::vec2f(0.0f, 0.0f))) { - inside_event_handler = false; + if(!selected_widget->on_event(event, window, mgl::vec2f(0.0f, 0.0f))) return false; - } } // Process widgets by visibility (backwards) - for(auto it = widgets.rbegin(), end = widgets.rend(); it != end; ++it) { + return widgets.for_each_reverse([selected_widget, &event, &window](std::unique_ptr &widget) { // Ignore offset because widgets are positioned with offset in ::draw, this solution is simpler - if(it->get() != selected_widget) { - if(!(*it)->on_event(event, window, mgl::vec2f(0.0f, 0.0f))) { - inside_event_handler = false; + if(widget.get() != selected_widget) { + if(!widget->on_event(event, window, mgl::vec2f(0.0f, 0.0f))) return false; - } } - } - - inside_event_handler = false; - return true; - } - - // TODO: Maybe call this from main on all widgets instead of from draw - void List::update() { - //widgets.insert(widgets.back(), std::make_move_iterator(add_queue.begin()), std::make_move_iterator(add_queue.end())); - std::move(add_queue.begin(), add_queue.end(), std::back_inserter(widgets)); - add_queue.clear(); - - for(Widget *widget_to_remove : remove_queue) { - remove_widget_immediate(widget_to_remove); - } - remove_queue.clear(); - } - - void List::remove_widget_immediate(Widget *widget) { - for(auto it = widgets.begin(), end = widgets.end(); it != end; ++it) { - if(it->get() == widget) { - widgets.erase(it); - return; - } - } + return true; + }); } void List::draw(mgl::Window &window, mgl::vec2f offset) { - update(); - if(!visible) return; @@ -140,28 +110,15 @@ namespace gsr { void List::add_widget(std::unique_ptr widget) { widget->parent_widget = this; - if(inside_event_handler) - add_queue.push_back(std::move(widget)); - else - widgets.push_back(std::move(widget)); + widgets.push_back(std::move(widget)); } void List::remove_widget(Widget *widget) { - for(auto it = add_queue.begin(), end = add_queue.end(); it != end; ++it) { - if(it->get() == widget) { - add_queue.erase(it); - return; - } - } - - if(inside_event_handler) - remove_queue.push_back(widget); - else - remove_widget_immediate(widget); + widgets.remove(widget); } - const std::vector>& List::get_child_widgets() const { - return widgets; + void List::for_each_child_widget(std::function &widget)> callback) { + widgets.for_each(callback); } Widget* List::get_child_widget_by_index(size_t index) const { diff --git a/src/gui/PageStack.cpp b/src/gui/PageStack.cpp new file mode 100644 index 0000000..94196cd --- /dev/null +++ b/src/gui/PageStack.cpp @@ -0,0 +1,58 @@ +#include "../../include/gui/PageStack.hpp" + +namespace gsr { + PageStack::PageStack() {} + + bool PageStack::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) { + if(!visible) + return true; + + offset = position + offset; + std::unique_ptr *top_page = widget_stack.back(); + if(top_page) { + if(!(*top_page)->on_event(event, window, offset)) + return false; + } + + return true; + } + + void PageStack::draw(mgl::Window &window, mgl::vec2f offset) { + if(!visible) + return; + + offset = position + offset; + std::unique_ptr *top_page = widget_stack.back(); + if(top_page) + (*top_page)->draw(window, offset); + } + + mgl::vec2f PageStack::get_size() { + return {0.0f, 0.0f}; + } + + void PageStack::push(std::unique_ptr widget) { + widget->on_navigate_to_page(); + widget_stack.push_back(std::move(widget)); + } + + void PageStack::pop() { + std::unique_ptr *top_page = widget_stack.back(); + if(top_page) { + (*top_page)->on_navigate_away_from_page(); + widget_stack.pop_back(); + } + } + + Page* PageStack::top() { + std::unique_ptr *top_page = widget_stack.back(); + if(top_page) + return top_page->get(); + else + return nullptr; + } + + bool PageStack::empty() const { + return widget_stack.empty(); + } +} \ No newline at end of file diff --git a/src/gui/RadioButton.cpp b/src/gui/RadioButton.cpp index 87428bf..66f3b69 100644 --- a/src/gui/RadioButton.cpp +++ b/src/gui/RadioButton.cpp @@ -121,14 +121,14 @@ namespace gsr { dirty = true; } - void RadioButton::set_selected_item(const std::string &id, bool trigger_event) { + void RadioButton::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) { for(size_t i = 0; i < items.size(); ++i) { auto &item = items[i]; if(item.id == id) { const size_t prev_selected_item = selected_item; selected_item = i; - if(trigger_event && selected_item != prev_selected_item && on_selection_changed) + if(trigger_event && (trigger_event_even_if_selection_not_changed || selected_item != prev_selected_item) && on_selection_changed) on_selection_changed(item.text.get_string(), item.id); break; diff --git a/src/gui/ScrollablePage.cpp b/src/gui/ScrollablePage.cpp index a0977ce..a2b6c8f 100644 --- a/src/gui/ScrollablePage.cpp +++ b/src/gui/ScrollablePage.cpp @@ -1,7 +1,5 @@ #include "../../include/gui/ScrollablePage.hpp" -#include "../../include/Theme.hpp" -#include #include namespace gsr { @@ -11,11 +9,8 @@ namespace gsr { if(!visible) return true; - const int margin_top = margin_top_scale * get_theme().window_height; - const int margin_left = margin_left_scale * get_theme().window_height; - const mgl::vec2f draw_pos = position + offset; - offset = draw_pos + mgl::vec2f(margin_left, get_border_size() + margin_top).floor(); + offset = draw_pos; Widget *selected_widget = selected_child_widget; if(selected_widget) { @@ -38,21 +33,8 @@ namespace gsr { if(!visible) return; - const int margin_top = margin_top_scale * get_theme().window_height; - const int margin_left = margin_left_scale * get_theme().window_height; - const mgl::vec2f draw_pos = position + offset; - offset = draw_pos + mgl::vec2f(margin_left, get_border_size() + margin_top).floor(); - - mgl::Rectangle background(size.floor()); - background.set_position(draw_pos); - background.set_color(get_theme().scrollable_page_bg_color); - window.draw(background); - - mgl::Rectangle border(mgl::vec2f(size.x, get_border_size()).floor()); - border.set_position(draw_pos); - border.set_color(get_theme().tint_color); - window.draw(border); + offset = draw_pos; mgl_scissor prev_scissor; mgl_window_get_scissor(window.internal_window(), &prev_scissor); @@ -84,25 +66,11 @@ namespace gsr { return size; } - mgl::vec2f ScrollablePage::get_inner_size() { - if(!visible) - return {0.0f, 0.0f}; - - const int margin_top = margin_top_scale * get_theme().window_height; - const int margin_bottom = margin_bottom_scale * get_theme().window_height; - const int margin_left = margin_left_scale * get_theme().window_height; - const int margin_right = margin_right_scale * get_theme().window_height; - return size - mgl::vec2f(margin_left + margin_right, margin_top + margin_bottom + get_border_size()); - } - - void ScrollablePage::set_margins(float top, float bottom, float left, float right) { - margin_top_scale = top; - margin_bottom_scale = bottom; - margin_left_scale = left; - margin_right_scale = right; + void ScrollablePage::set_size(mgl::vec2f size) { + this->size = size; } - float ScrollablePage::get_border_size() const { - return 0.004f * get_theme().window_height; + void ScrollablePage::add_widget(std::unique_ptr widget) { + widgets.push_back(std::move(widget)); } } \ No newline at end of file diff --git a/src/gui/SettingsPage.cpp b/src/gui/SettingsPage.cpp index 9bf6a12..d6022ab 100644 --- a/src/gui/SettingsPage.cpp +++ b/src/gui/SettingsPage.cpp @@ -1,6 +1,8 @@ #include "../../include/gui/SettingsPage.hpp" -#include "../../include/gui/ScrollablePage.hpp" +#include "../../include/gui/GsrPage.hpp" #include "../../include/gui/Label.hpp" +#include "../../include/gui/PageStack.hpp" +#include "../../include/gui/FileChooser.hpp" #include "../../include/Theme.hpp" #include "../../include/GsrInfo.hpp" @@ -10,20 +12,17 @@ #include namespace gsr { - SettingsPage::SettingsPage(Type type, const GsrInfo &gsr_info, const std::vector &audio_devices, std::optional &config) : + SettingsPage::SettingsPage(Type type, const GsrInfo &gsr_info, const std::vector &audio_devices, std::optional &config, PageStack *page_stack) : StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()), type(type), config(config), + page_stack(page_stack), settings_title_text("Settings", get_theme().title_font) { - const mgl::vec2f window_size = mgl::vec2f(get_theme().window_width, get_theme().window_height).floor(); - const mgl::vec2f content_page_size = (window_size * mgl::vec2f(0.3333f, 0.7f)).floor(); - const mgl::vec2f content_page_position = mgl::vec2f(window_size * 0.5f - content_page_size * 0.5f).floor(); - const float settings_body_margin = 0.02f; - - auto content_page = std::make_unique(content_page_size); - content_page->set_position(content_page_position); - content_page->set_margins(settings_body_margin, settings_body_margin, settings_body_margin, settings_body_margin); + auto content_page = std::make_unique(); + content_page->set_on_back_button_click([page_stack]() { + page_stack->pop(); + }); content_page_ptr = content_page.get(); add_widget(std::move(content_page)); @@ -31,40 +30,6 @@ namespace gsr { add_page_specific_widgets(); } - std::unique_ptr