diff options
m--------- | depends/mglpp | 0 | ||||
-rw-r--r-- | images/stream.png | bin | 5235 -> 7286 bytes | |||
-rw-r--r-- | images/up_arrow.png | bin | 0 -> 1426 bytes | |||
-rw-r--r-- | include/Theme.hpp | 1 | ||||
-rw-r--r-- | include/Utils.hpp | 4 | ||||
-rw-r--r-- | include/gui/Button.hpp | 1 | ||||
-rw-r--r-- | include/gui/FileChooser.hpp | 46 | ||||
-rw-r--r-- | include/gui/GsrPage.hpp | 13 | ||||
-rw-r--r-- | include/gui/ScrollablePage.hpp | 4 | ||||
-rw-r--r-- | include/gui/Utils.hpp | 2 | ||||
-rw-r--r-- | src/Theme.cpp | 3 | ||||
-rw-r--r-- | src/Utils.cpp | 18 | ||||
-rw-r--r-- | src/gui/Button.cpp | 9 | ||||
-rw-r--r-- | src/gui/FileChooser.cpp | 191 | ||||
-rw-r--r-- | src/gui/GsrPage.cpp | 40 | ||||
-rw-r--r-- | src/gui/ScrollablePage.cpp | 90 | ||||
-rw-r--r-- | src/gui/SettingsPage.cpp | 26 | ||||
-rw-r--r-- | src/gui/Utils.cpp | 10 | ||||
-rw-r--r-- | src/main.cpp | 5 |
19 files changed, 379 insertions, 84 deletions
diff --git a/depends/mglpp b/depends/mglpp -Subproject 61b2725ef174e4cec734e29bce4f69d6fcd2a81 +Subproject 685cd2d470b399a7aa82ae6a2942ffb0afefab9 diff --git a/images/stream.png b/images/stream.png Binary files differindex 970a673..198a06e 100644 --- a/images/stream.png +++ b/images/stream.png diff --git a/images/up_arrow.png b/images/up_arrow.png Binary files differnew file mode 100644 index 0000000..1be9fdc --- /dev/null +++ b/images/up_arrow.png diff --git a/include/Theme.hpp b/include/Theme.hpp index 7406c67..c70167b 100644 --- a/include/Theme.hpp +++ b/include/Theme.hpp @@ -31,6 +31,7 @@ namespace gsr { mgl::Texture combobox_arrow_texture; mgl::Texture settings_texture; mgl::Texture folder_texture; + mgl::Texture up_arrow_texture; double double_click_timeout_seconds = 0.4; }; diff --git a/include/Utils.hpp b/include/Utils.hpp index 6943e5e..b18ab8e 100644 --- a/include/Utils.hpp +++ b/include/Utils.hpp @@ -25,4 +25,8 @@ namespace gsr { std::string get_videos_dir(); int create_directory_recursive(char *path); bool file_get_content(const char *filepath, std::string &file_content); + + // Returns the path to the parent directory (ignoring trailing /) + // of "." if there is no parent directory and the directory path is relative + std::string get_parent_directory(std::string_view directory); }
\ No newline at end of file diff --git a/include/gui/Button.hpp b/include/gui/Button.hpp index a4dcca1..bc1dd94 100644 --- a/include/gui/Button.hpp +++ b/include/gui/Button.hpp @@ -22,6 +22,7 @@ namespace gsr { void set_border_scale(float scale); const std::string& get_text() const; + void set_text(std::string str); std::function<void()> on_click; private: diff --git a/include/gui/FileChooser.hpp b/include/gui/FileChooser.hpp index 309ff56..36dac2d 100644 --- a/include/gui/FileChooser.hpp +++ b/include/gui/FileChooser.hpp @@ -1,16 +1,51 @@ #pragma once #include "Widget.hpp" +#include "ScrollablePage.hpp" #include <functional> #include <vector> #include <mglpp/graphics/Text.hpp> +#include <mglpp/graphics/Sprite.hpp> #include <mglpp/system/Clock.hpp> // This currently only supports displaying folders // TODO: Support files as well namespace gsr { + class FileChooser; + + class FileChooserBody : public Widget { + friend class FileChooser; + public: + FileChooserBody(FileChooser *file_chooser, mgl::vec2f size); + FileChooserBody(const FileChooserBody&) = delete; + FileChooserBody& operator=(const FileChooserBody&) = delete; + + bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override; + void draw(mgl::Window &window, mgl::vec2f offset) override; + + mgl::vec2f get_size() override; + mgl::vec2f get_inner_size() override; + void set_size(mgl::vec2f size); + private: + void set_current_directory(const char *directory); + private: + struct Folder { + mgl::Text text; + time_t last_modified_seconds = 0; + }; + + FileChooser *file_chooser = nullptr; + mgl::vec2f size; + mgl::vec2f inner_size; + std::vector<Folder> folders; + int mouse_over_item = -1; + int selected_item = -1; + mgl::Clock double_click_timer; + int times_clicked_within_timer = 0; + }; + class FileChooser : public Widget { public: FileChooser(const char *start_directory, mgl::vec2f size); @@ -23,6 +58,9 @@ namespace gsr { mgl::vec2f get_size() override; void set_current_directory(const char *directory); + void open_subdirectory(const char *name); + void open_parent_directory(); + const std::string& get_current_directory() const; private: struct Folder { mgl::Text text; @@ -31,10 +69,8 @@ namespace gsr { mgl::vec2f size; mgl::Text current_directory_text; - int mouse_over_item = -1; - int selected_item = -1; - std::vector<Folder> folders; - mgl::Clock double_click_timer; - int times_clicked_within_timer = 0; + mgl::Sprite up_arrow_sprite; + ScrollablePage scrollable_page; + FileChooserBody *file_chooser_body_ptr = nullptr; }; }
\ No newline at end of file diff --git a/include/gui/GsrPage.hpp b/include/gui/GsrPage.hpp index 916055a..1d298f4 100644 --- a/include/gui/GsrPage.hpp +++ b/include/gui/GsrPage.hpp @@ -3,6 +3,7 @@ #include "Page.hpp" #include "Button.hpp" +#include <functional> #include <mglpp/graphics/Text.hpp> namespace gsr { @@ -19,9 +20,12 @@ namespace gsr { mgl::vec2f get_inner_size() override; void set_margins(float top, float bottom, float left, float right); - void set_on_back_button_click(std::function<void()> on_click_handler); + void add_button(const std::string &text, const std::string &id, mgl::Color color); + + std::function<void(const std::string &id)> on_click; private: void draw_page_label(mgl::Window &window, mgl::vec2f body_pos); + void draw_buttons(mgl::Window &window, mgl::vec2f body_pos, mgl::vec2f body_size); void draw_children(mgl::Window &window, mgl::vec2f position); float get_border_size() const; @@ -29,11 +33,16 @@ namespace gsr { mgl::vec2f get_content_position(); mgl::vec2f get_content_position_with_margin(); private: + struct ButtonItem { + std::unique_ptr<Button> button; + std::string id; + }; + float margin_top_scale = 0.0f; float margin_bottom_scale = 0.0f; float margin_left_scale = 0.0f; float margin_right_scale = 0.0f; - Button back_button; mgl::Text label_text; + std::vector<ButtonItem> buttons; }; }
\ No newline at end of file diff --git a/include/gui/ScrollablePage.hpp b/include/gui/ScrollablePage.hpp index 09c618f..5a31b92 100644 --- a/include/gui/ScrollablePage.hpp +++ b/include/gui/ScrollablePage.hpp @@ -18,8 +18,12 @@ namespace gsr { void set_size(mgl::vec2f size); void add_widget(std::unique_ptr<Widget> widget); + + void reset_scroll(); private: mgl::vec2f size; SafeVector<std::unique_ptr<Widget>> widgets; + int scroll_target_y = 0; + double scroll_y = 0.0; }; }
\ No newline at end of file diff --git a/include/gui/Utils.hpp b/include/gui/Utils.hpp index 885bf3e..6963bc5 100644 --- a/include/gui/Utils.hpp +++ b/include/gui/Utils.hpp @@ -13,4 +13,6 @@ namespace mgl { namespace gsr { // Inner border void draw_rectangle_outline(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::Color color, float border_size); + double get_frame_delta_seconds(); + void set_frame_delta_seconds(double frame_delta); }
\ No newline at end of file diff --git a/src/Theme.cpp b/src/Theme.cpp index 9fcf057..d498ef3 100644 --- a/src/Theme.cpp +++ b/src/Theme.cpp @@ -56,6 +56,9 @@ namespace gsr { if(!theme->folder_texture.load_from_file((resources_path + "images/folder.png").c_str(), {false, false, false})) goto error; + if(!theme->up_arrow_texture.load_from_file((resources_path + "images/up_arrow.png").c_str(), {false, false, false})) + goto error; + return true; error: diff --git a/src/Utils.cpp b/src/Utils.cpp index 55fc57e..ae24f7b 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -166,4 +166,22 @@ namespace gsr { fclose(file); return success; } + + std::string get_parent_directory(std::string_view directory) { + std::string result; + + while(directory.size() > 1 && directory.back() == '/') { + directory.remove_suffix(1); + } + + const size_t prev_slash_index = directory.rfind('/'); + if(prev_slash_index == 0) { + result = "/"; + } else if(prev_slash_index == std::string_view::npos) { + result = "."; + } else { + result = directory.substr(0, prev_slash_index); + } + return result; + } }
\ No newline at end of file diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp index 6c475bd..0cf5bee 100644 --- a/src/gui/Button.cpp +++ b/src/gui/Button.cpp @@ -23,8 +23,9 @@ namespace gsr { const mgl::vec2f item_size = get_size().floor(); if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { const bool clicked_inside = mgl::FloatRect(position + offset, item_size).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y }); - if(clicked_inside && on_click) { - on_click(); + if(clicked_inside) { + if(on_click) + on_click(); return false; } } @@ -76,4 +77,8 @@ namespace gsr { const std::string& Button::get_text() const { return text.get_string(); } + + void Button::set_text(std::string str) { + text.set_string(std::move(str)); + } }
\ No newline at end of file diff --git a/src/gui/FileChooser.cpp b/src/gui/FileChooser.cpp index b63af24..b7321cd 100644 --- a/src/gui/FileChooser.cpp +++ b/src/gui/FileChooser.cpp @@ -1,5 +1,6 @@ #include "../../include/gui/FileChooser.hpp" #include "../../include/gui/Utils.hpp" +#include "../../include/Utils.hpp" #include "../../include/Theme.hpp" #include <mglpp/graphics/Rectangle.hpp> @@ -25,14 +26,12 @@ namespace gsr { static const float content_padding_bottom_scale = 0.03f; static const float content_padding_left_scale = 0.03f; static const float content_padding_right_scale = 0.03f; + static const float up_button_spacing_scale = 0.01f; - FileChooser::FileChooser(const char *start_directory, mgl::vec2f size) : - size(size), current_directory_text(start_directory, get_theme().body_font) - { - set_current_directory(start_directory); - } + FileChooserBody::FileChooserBody(FileChooser *file_chooser, mgl::vec2f size) : + file_chooser(file_chooser), size(size), inner_size(size) {} - bool FileChooser::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) { + bool FileChooserBody::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f) { if(!visible) return true; @@ -44,45 +43,29 @@ namespace gsr { } double_click_timer.restart(); + const int prev_selected_item = selected_item; selected_item = mouse_over_item; + const bool item_changed = selected_item != prev_selected_item; + if(item_changed) + times_clicked_within_timer = 1; 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].text.get_string().c_str()); - set_current_directory(new_directory); + file_chooser->open_subdirectory(folders[selected_item].text.get_string().c_str()); } } return true; } - void FileChooser::draw(mgl::Window &window, mgl::vec2f offset) { + void FileChooserBody::draw(mgl::Window &window, mgl::vec2f offset) { mouse_over_item = -1; if(!visible) return; - const mgl::vec2f draw_pos_start = position + offset; - mgl::vec2f draw_pos = draw_pos_start; - 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(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); - - draw_pos += mgl::vec2f(0.0f, current_directory_background.get_size().y + spacing_between_current_directory_and_content * get_theme().window_height); - mgl::Rectangle content_background(mgl::vec2f(size.x, size.y - (draw_pos.y - draw_pos_start.y)).floor()); - content_background.set_color(mgl::Color(0, 0, 0, 120)); - content_background.set_position(draw_pos.floor()); - window.draw(content_background); + mgl_scissor scissor; + mgl_window_get_scissor(window.internal_window(), &scissor); + const mgl::vec2f draw_pos = position + offset; const mgl::vec2f mouse_pos = window.get_mouse_position().to_vec2f(); const int content_padding_top = content_padding_top_scale * get_theme().window_height; @@ -90,9 +73,11 @@ namespace gsr { 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_background.get_size().x - (content_padding_left + content_padding_right) * num_columns) / num_columns); + const float folder_width = (int)((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); + mgl::vec2f folder_pos = draw_pos + mgl::vec2f(content_padding_left, content_padding_top); + bool end_is_newline = false; + for(int i = 0; i < (int)folders.size(); ++i) { auto &folder = folders[i]; @@ -118,32 +103,33 @@ namespace gsr { mouse_over_item = i; } - window.draw(folder_sprite); + if(item_pos.y + item_size.y >= draw_pos.y && item_pos.y < scissor.position.y + scissor.size.y) { + 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); + // 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_background.get_size().x) { - folder_pos.x = content_background.get_position().x + content_padding_left; + if(folder_pos.x + folder_width > draw_pos.x + size.x) { + folder_pos.x = draw_pos.x + content_padding_left; folder_pos.y += content_padding_bottom + folder_sprite.get_size().y + content_padding_top; + if(i == (int)folders.size() - 1) + end_is_newline = true; } } - } - mgl::vec2f FileChooser::get_size() { - if(!visible) - return {0.0f, 0.0f}; + if(!end_is_newline) + folder_pos.y += content_padding_bottom + folder_width; - return size; + inner_size = mgl::vec2f(size.x, folder_pos.y - draw_pos.y); } - void FileChooser::set_current_directory(const char *directory) { + void FileChooserBody::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) { @@ -176,4 +162,117 @@ namespace gsr { return folder_a.last_modified_seconds > folder_b.last_modified_seconds; }); } + + mgl::vec2f FileChooserBody::get_size() { + if(!visible) + return {0.0f, 0.0f}; + + return size; + } + + mgl::vec2f FileChooserBody::get_inner_size() { + if(!visible) + return {0.0f, 0.0f}; + + return inner_size; + } + + void FileChooserBody::set_size(mgl::vec2f size) { + this->size = size; + } + + FileChooser::FileChooser(const char *start_directory, mgl::vec2f size) : + size(size), + current_directory_text(start_directory, get_theme().body_font), + up_arrow_sprite(&get_theme().up_arrow_texture), + scrollable_page(size) + { + auto file_chooser_body = std::make_unique<FileChooserBody>(this, size); + file_chooser_body_ptr = file_chooser_body.get(); + scrollable_page.add_widget(std::move(file_chooser_body)); + set_current_directory(start_directory); + } + + bool FileChooser::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) { + if(!visible) + return true; + + if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { + if(mgl::FloatRect(up_arrow_sprite.get_position(), up_arrow_sprite.get_size()).contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) { + open_parent_directory(); + return false; + } + } + + return scrollable_page.on_event(event, window, offset); + } + + void FileChooser::draw(mgl::Window &window, mgl::vec2f offset) { + if(!visible) + return; + + const mgl::vec2f draw_pos_start = position + offset; + mgl::vec2f draw_pos = draw_pos_start; + 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::vec2f current_directory_background_size = mgl::vec2f(size.x, current_directory_text.get_bounds().size.y + current_directory_padding.y).floor(); + + up_arrow_sprite.set_height((int)(current_directory_background_size.y * 0.8f)); + up_arrow_sprite.set_position((draw_pos + mgl::vec2f(size.x - up_arrow_sprite.get_size().x, current_directory_background_size.y * 0.5f - up_arrow_sprite.get_size().y * 0.5f)).floor()); + const bool mouse_inside_up_arrow = mgl::FloatRect(up_arrow_sprite.get_position(), up_arrow_sprite.get_size()).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget(); + up_arrow_sprite.set_color(mouse_inside_up_arrow ? get_theme().tint_color : mgl::Color(255, 255, 255)); + window.draw(up_arrow_sprite); + + current_directory_background_size.x -= (up_arrow_sprite.get_size().x + up_button_spacing_scale * get_theme().window_height); + mgl::Rectangle current_directory_background(current_directory_background_size.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_size.y * 0.5f - current_directory_text.get_bounds().size.y * 0.5f)).floor()); + window.draw(current_directory_text); + + draw_pos += mgl::vec2f(0.0f, current_directory_background_size.y + spacing_between_current_directory_and_content * get_theme().window_height); + const mgl::vec2f body_size = mgl::vec2f(size.x, size.y - (draw_pos.y - draw_pos_start.y)).floor(); + scrollable_page.set_size(body_size); + file_chooser_body_ptr->set_size(body_size); + + mgl::Rectangle content_background(size.floor()); + content_background.set_position(draw_pos.floor()); + content_background.set_color(mgl::Color(0, 0, 0, 120)); + window.draw(content_background); + + scrollable_page.draw(window, draw_pos.floor()); + } + + mgl::vec2f FileChooser::get_size() { + if(!visible) + return {0.0f, 0.0f}; + + return size; + } + + void FileChooser::set_current_directory(const char *directory) { + current_directory_text.set_string(directory); + file_chooser_body_ptr->set_current_directory(directory); + scrollable_page.reset_scroll(); + } + + void FileChooser::open_subdirectory(const char *name) { + char filepath[PATH_MAX]; + snprintf(filepath, sizeof(filepath), "%s/%s", current_directory_text.get_string().c_str(), name); + set_current_directory(filepath); + } + + void FileChooser::open_parent_directory() { + set_current_directory(get_parent_directory(current_directory_text.get_string()).c_str()); + } + + const std::string& FileChooser::get_current_directory() const { + return current_directory_text.get_string(); + } }
\ No newline at end of file diff --git a/src/gui/GsrPage.cpp b/src/gui/GsrPage.cpp index df997e9..7a5615c 100644 --- a/src/gui/GsrPage.cpp +++ b/src/gui/GsrPage.cpp @@ -6,23 +6,24 @@ #include <mglpp/window/Window.hpp> namespace gsr { + static const float button_spacing_scale = 0.015f; + GsrPage::GsrPage() : - back_button(&get_theme().title_font, "Back", - mgl::vec2f(get_theme().window_width / 10, get_theme().window_height / 15).floor(), get_theme().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(!back_button.on_event(event, window, mgl::vec2f(0.0f, 0.0f))) - return false; + for(size_t i = 0; i < buttons.size(); ++i) { + ButtonItem &button_item = buttons[i]; + if(!button_item.button->on_event(event, window, mgl::vec2f(0.0f, 0.0f))) + return false; + } const int margin_top = margin_top_scale * get_theme().window_height; const int margin_left = margin_left_scale * get_theme().window_height; @@ -49,8 +50,8 @@ namespace gsr { if(!visible) return; - const mgl::vec2f content_page_size = get_size(); const mgl::vec2f content_page_position = get_content_position(); + const mgl::vec2f content_page_size = get_size(); mgl::Rectangle background(content_page_size); background.set_position(content_page_position); @@ -63,9 +64,7 @@ namespace gsr { window.draw(border); 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)); + draw_buttons(window, content_page_position, content_page_size); const int margin_top = margin_top_scale * get_theme().window_height; const int margin_left = margin_left_scale * get_theme().window_height; @@ -90,6 +89,16 @@ namespace gsr { window.draw(icon); } + void GsrPage::draw_buttons(mgl::Window &window, mgl::vec2f body_pos, mgl::vec2f body_size) { + float offset_y = 0.0f; + for(size_t i = 0; i < buttons.size(); ++i) { + ButtonItem &button_item = buttons[i]; + button_item.button->set_position(body_pos + mgl::vec2f(body_size.x + get_horizontal_spacing(), offset_y).floor()); + button_item.button->draw(window, mgl::vec2f(0.0f, 0.0f)); + offset_y += button_item.button->get_size().y + (button_spacing_scale * get_theme().window_height); + } + } + void GsrPage::draw_children(mgl::Window &window, mgl::vec2f position) { Widget *selected_widget = selected_child_widget; @@ -142,8 +151,15 @@ namespace gsr { margin_right_scale = right; } - void GsrPage::set_on_back_button_click(std::function<void()> on_click_handler) { - back_button.on_click = std::move(on_click_handler); + void GsrPage::add_button(const std::string &text, const std::string &id, mgl::Color color) { + auto button = std::make_unique<Button>(&get_theme().title_font, text.c_str(), + mgl::vec2f(get_theme().window_width / 10, get_theme().window_height / 15).floor(), color); + button->set_border_scale(0.003f); + button->on_click = [this, id]() { + if(on_click) + on_click(id); + }; + buttons.push_back({ std::move(button), id }); } float GsrPage::get_border_size() const { diff --git a/src/gui/ScrollablePage.cpp b/src/gui/ScrollablePage.cpp index 41ede41..a791d75 100644 --- a/src/gui/ScrollablePage.cpp +++ b/src/gui/ScrollablePage.cpp @@ -1,16 +1,21 @@ #include "../../include/gui/ScrollablePage.hpp" +#include "../../include/gui/Utils.hpp" #include <mglpp/window/Window.hpp> +#include <mglpp/window/Event.hpp> +//#include <mglpp/graphics/Rectangle.hpp> namespace gsr { + static const int scroll_speed = 80; + static const double scroll_update_speed = 10.0; + ScrollablePage::ScrollablePage(mgl::vec2f size) : size(size) {} bool ScrollablePage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) { if(!visible) return true; - const mgl::vec2f draw_pos = position + offset; - offset = draw_pos; + offset = position + offset + mgl::vec2f(0.0f, scroll_y); Widget *selected_widget = selected_child_widget; if(selected_widget) { @@ -19,21 +24,34 @@ namespace gsr { } // Process widgets by visibility (backwards) - return widgets.for_each_reverse([selected_widget, &window, &event, offset](std::unique_ptr<Widget> &widget) { + const bool continue_events = widgets.for_each_reverse([selected_widget, &window, &event, offset](std::unique_ptr<Widget> &widget) { if(widget.get() != selected_widget) { if(!widget->on_event(event, window, offset)) return false; } return true; }); + + if(!continue_events) + return false; + + if(event.type == mgl::Event::MouseWheelScrolled) { + const double scroll = event.mouse_wheel_scroll.delta * scroll_speed; + scroll_target_y += scroll; + return false; + } + + return true; } void ScrollablePage::draw(mgl::Window &window, mgl::vec2f offset) { - if(!visible) + if(!visible || widgets.empty()) { + reset_scroll(); return; + } - const mgl::vec2f draw_pos = position + offset; - offset = draw_pos; + offset = position + offset; + const mgl::vec2f page_scroll_start = offset; mgl_scissor prev_scissor; mgl_window_get_scissor(window.internal_window(), &prev_scissor); @@ -46,16 +64,67 @@ namespace gsr { mgl_window_set_scissor(window.internal_window(), &new_scissor); Widget *selected_widget = selected_child_widget; + offset.y += scroll_y; + double child_top = 999999.0; + double child_bottom = 0.0; for(size_t i = 0; i < widgets.size(); ++i) { auto &widget = widgets[i]; - if(widget.get() != selected_widget) + if(widget.get() != selected_widget) { widget->draw(window, offset); + + // TODO: Create a widget function to get the render area instead, which each widget should set (position + offset as start, and position + offset + size as end), this has to be done in the widgets to ensure that recursive rendering has correct position. + // TODO: Do these calls before drawing, so that scroll limits can be set before drawing to prevent scrolling from overflowing for 1 frame. + // To make that possible move inner_size calculation in FileChooserBody to the get_inner_size function. + // That calculation can be done without looping folders. + const mgl::vec2f widget_pos = widget->get_position() + offset; + const mgl::vec2f widget_inner_size = widget->get_inner_size(); + + child_top = std::min(child_top, (double)widget_pos.y); + child_bottom = std::max(child_bottom, (double)widget_pos.y + (double)widget_inner_size.y); + } } - if(selected_widget) + if(selected_widget) { selected_widget->draw(window, offset); + const mgl::vec2f widget_pos = selected_widget->get_position() + offset; + const mgl::vec2f widget_inner_size = selected_widget->get_inner_size(); + + child_top = std::min(child_top, (double)widget_pos.y); + child_bottom = std::max(child_bottom, (double)widget_pos.y + (double)widget_inner_size.y); + } + + const double child_height = child_bottom - child_top; + + // Debug output + // mgl::Rectangle bottom(mgl::vec2f(size.x, 5.0f)); + // bottom.set_color(mgl::Color(255, 0, 0, 255)); + // bottom.set_position(mgl::vec2f(offset.x, child_bottom)); + // window.draw(bottom); + + // mgl::Rectangle bottom_d(mgl::vec2f(size.x, 5.0f)); + // bottom_d.set_color(mgl::Color(0, 255, 0, 255)); + // bottom_d.set_position(mgl::vec2f(offset.x, page_scroll_start.y + size.y - 5)); + // window.draw(bottom_d); + + // Scroll animation + const double scroll_diff = scroll_target_y - scroll_y; + if(std::abs(scroll_diff) < 0.1) { + scroll_y = scroll_target_y; + } else { + const double frame_scroll_speed = std::min(1.0, get_frame_delta_seconds() * scroll_update_speed); + scroll_y += (scroll_diff * frame_scroll_speed); + } + + // Top and bottom limit + const double child_draw_overflow = (page_scroll_start.y + size.y) - child_bottom; + if(scroll_y > 0.001 || child_height < size.y) { + scroll_target_y = 0; + } else if(child_draw_overflow > 0.001) { + scroll_target_y = scroll_y + child_draw_overflow; + } + mgl_window_set_scissor(window.internal_window(), &prev_scissor); } @@ -73,4 +142,9 @@ namespace gsr { void ScrollablePage::add_widget(std::unique_ptr<Widget> widget) { widgets.push_back(std::move(widget)); } + + void ScrollablePage::reset_scroll() { + scroll_y = 0; + scroll_target_y = 0; + } }
\ No newline at end of file diff --git a/src/gui/SettingsPage.cpp b/src/gui/SettingsPage.cpp index 94e51a2..259df6c 100644 --- a/src/gui/SettingsPage.cpp +++ b/src/gui/SettingsPage.cpp @@ -20,9 +20,11 @@ namespace gsr { settings_title_text("Settings", get_theme().title_font) { auto content_page = std::make_unique<GsrPage>(); - content_page->set_on_back_button_click([page_stack]() { - page_stack->pop(); - }); + content_page->add_button("Back", "back", get_theme().page_bg_color); + content_page->on_click = [page_stack](const std::string &id) { + if(id == "back") + page_stack->pop(); + }; content_page_ptr = content_page.get(); add_widget(std::move(content_page)); @@ -363,19 +365,25 @@ namespace gsr { std::unique_ptr<List> SettingsPage::create_save_directory(const char *label) { auto save_directory_list = std::make_unique<List>(List::Orientation::VERTICAL); save_directory_list->add_widget(std::make_unique<Label>(&get_theme().body_font, label, get_theme().text_color)); - auto save_directory_button = std::make_unique<Button>(&get_theme().body_font, "/home/dec05eba/Videos", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + auto save_directory_button = std::make_unique<Button>(&get_theme().body_font, "/home/dec05eba", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + save_directory_button_ptr = save_directory_button.get(); save_directory_button->on_click = [this]() { auto select_directory_page = std::make_unique<GsrPage>(); - select_directory_page->set_on_back_button_click([this]() { - page_stack->pop(); - }); + select_directory_page->add_button("Save", "save", get_theme().tint_color); + select_directory_page->add_button("Cancel", "cancel", get_theme().page_bg_color); - auto file_chooser = std::make_unique<gsr::FileChooser>("/home/dec05eba", select_directory_page->get_inner_size()); + auto file_chooser = std::make_unique<FileChooser>(save_directory_button_ptr->get_text().c_str(), select_directory_page->get_inner_size()); + FileChooser *file_chooser_ptr = file_chooser.get(); select_directory_page->add_widget(std::move(file_chooser)); + select_directory_page->on_click = [this, file_chooser_ptr](const std::string &id) { + if(id == "save") + save_directory_button_ptr->set_text(file_chooser_ptr->get_current_directory()); + page_stack->pop(); + }; + page_stack->push(std::move(select_directory_page)); }; - save_directory_button_ptr = save_directory_button.get(); save_directory_list->add_widget(std::move(save_directory_button)); return save_directory_list; } diff --git a/src/gui/Utils.cpp b/src/gui/Utils.cpp index 20c7b73..e000b7a 100644 --- a/src/gui/Utils.cpp +++ b/src/gui/Utils.cpp @@ -3,6 +3,8 @@ #include <mglpp/graphics/Rectangle.hpp> namespace gsr { + static double frame_delta_seconds = 1.0; + // TODO: Use vertices to make it one draw call void draw_rectangle_outline(mgl::Window &window, mgl::vec2f pos, mgl::vec2f size, mgl::Color color, float border_size) { pos = pos.floor(); @@ -40,4 +42,12 @@ namespace gsr { window.draw(rect); } } + + double get_frame_delta_seconds() { + return frame_delta_seconds; + } + + void set_frame_delta_seconds(double frame_delta) { + frame_delta_seconds = frame_delta; + } }
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 138cd3f..042a9af 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -666,7 +666,12 @@ int main(int argc, char **argv) { window.display(); }; + mgl::Clock frame_delta_clock; while(window.is_open()) { + const double frame_delta_seconds = frame_delta_clock.get_elapsed_time_seconds(); + frame_delta_clock.restart(); + gsr::set_frame_delta_seconds(frame_delta_seconds); + if(page_stack.empty() || !running) { running = false; goto quit; |