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 --- TODO | 8 +- depends/mglpp | 2 +- images/folder.png | Bin 0 -> 738 bytes include/GsrInfo.hpp | 1 + include/SafeVector.hpp | 127 ++++++++++++++++++++++++++++++ include/Theme.hpp | 5 +- include/Utils.hpp | 4 - include/gui/Button.hpp | 2 + include/gui/ComboBox.hpp | 2 +- include/gui/FileChooser.hpp | 35 +++++++++ include/gui/GsrPage.hpp | 39 +++++++++ include/gui/List.hpp | 16 +--- include/gui/Page.hpp | 2 +- include/gui/PageStack.hpp | 29 +++++++ include/gui/RadioButton.hpp | 2 +- include/gui/ScrollablePage.hpp | 17 ++-- include/gui/SettingsPage.hpp | 20 ++--- meson.build | 5 +- src/Config.cpp | 7 ++ src/GsrInfo.cpp | 14 +++- src/Theme.cpp | 13 +-- src/Utils.cpp | 7 -- 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 +++++++------------ src/main.cpp | 68 ++++------------ 32 files changed, 724 insertions(+), 260 deletions(-) create mode 100644 images/folder.png create mode 100644 include/SafeVector.hpp create mode 100644 include/gui/FileChooser.hpp create mode 100644 include/gui/GsrPage.hpp create mode 100644 include/gui/PageStack.hpp create mode 100644 src/gui/FileChooser.cpp create mode 100644 src/gui/GsrPage.cpp create mode 100644 src/gui/PageStack.cpp diff --git a/TODO b/TODO index c4588a2..cbf9da5 100644 --- a/TODO +++ b/TODO @@ -20,4 +20,10 @@ Handle events in draw function because the render position of elements is availa Add nvidia overclock option. -Add support for window selection in capture. \ No newline at end of file +Add support for window selection in capture. + +Add option to record the focused monitor. This works on wayland too when using kms capture since we can get cursor position without root and see which monitor (crtc) the cursor is on. + +Add option to select hevc_10bit and av1_10bit. + +For system startup dont allow default output/input audio, as that can change after startup. For example if default output is a bluetooth devices that gets connected after startup. diff --git a/depends/mglpp b/depends/mglpp index 66d126f..17254ca 160000 --- a/depends/mglpp +++ b/depends/mglpp @@ -1 +1 @@ -Subproject commit 66d126f824bc755bad042e1cb94c1d79a3203c97 +Subproject commit 17254ca13ce7985e87a413df1d8546779734f05e diff --git a/images/folder.png b/images/folder.png new file mode 100644 index 0000000..d24ae63 Binary files /dev/null and b/images/folder.png differ diff --git a/include/GsrInfo.hpp b/include/GsrInfo.hpp index 483b40f..d90d72f 100644 --- a/include/GsrInfo.hpp +++ b/include/GsrInfo.hpp @@ -58,6 +58,7 @@ namespace gsr { enum class GsrInfoExitStatus { OK, + BROKEN_DRIVERS, FAILED_TO_RUN_COMMAND, OPENGL_FAILED, NO_DRM_CARD diff --git a/include/SafeVector.hpp b/include/SafeVector.hpp new file mode 100644 index 0000000..cea9cb9 --- /dev/null +++ b/include/SafeVector.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include + +// A vector that can be modified while iterating +template +class SafeVector { +public: + using PointerType = typename std::pointer_traits::element_type*; + + void push_back(T item) { + data.push_back(std::move(item)); + } + + // Safe to call when vector is empty + void pop_back() { + if(!data.empty()) + data.pop_back(); + } + + // Might not remove the data immediately if inside for_each loop. + // In that case the item is remove at the end of the loop. + void remove(PointerType item_to_remove) { + if(for_each_depth == 0) + remove_item(item_to_remove); + else + remove_queue.push_back(item_to_remove); + } + + // Safe to call when vector is empty, in which case it returns nullptr + T* back() { + if(data.empty()) + return nullptr; + else + return &data.back(); + } + + void clear() { + data.clear(); + remove_queue.clear(); + } + + // Return true from |callback| to continue. This function returns false if |callback| returned false + bool for_each(std::function callback) { + bool result = true; + ++for_each_depth; + + for(size_t i = 0; i < data.size(); ++i) { + result = callback(data[i]); + if(!result) + break; + } + + --for_each_depth; + if(for_each_depth == 0) { + for(PointerType item_to_remove : remove_queue) { + remove_item(item_to_remove); + } + remove_queue.clear(); + } + + return result; + } + + // Return true from |callback| to continue. This function returns false if |callback| returned false + bool for_each_reverse(std::function callback) { + bool result = true; + ++for_each_depth; + + int i = (int)data.size() - 1; + while(true) { + // pop_back can be called while iterating so handle that case + if(i >= (int)data.size()) + i = (int)data.size() - 1; + + if(i < 0) + break; + + result = callback(data[i]); + if(!result) + break; + + --i; + } + + --for_each_depth; + if(for_each_depth == 0) { + for(PointerType item_to_remove : remove_queue) { + remove_item(item_to_remove); + } + remove_queue.clear(); + } + + return result; + } + + T& operator[](size_t index) { + return data[index]; + } + + const T& operator[](size_t index) const { + return data[index]; + } + + size_t size() const { + return data.size(); + } + + bool empty() const { + return data.empty(); + } +private: + void remove_item(PointerType item_to_remove) { + for(auto it = data.begin(), end = data.end(); it != end; ++it) { + if(&*(*it) == item_to_remove) { + data.erase(it); + return; + } + } + } +private: + std::vector data; + std::vector remove_queue; + int for_each_depth = 0; +}; \ No newline at end of file diff --git a/include/Theme.hpp b/include/Theme.hpp index cc321bd..5f228af 100644 --- a/include/Theme.hpp +++ b/include/Theme.hpp @@ -30,9 +30,12 @@ namespace gsr { mgl::Texture combobox_arrow_texture; mgl::Texture settings_texture; + mgl::Texture folder_texture; + + double double_click_timeout_seconds = 0.4; }; - bool init_theme(const gsr::GsrInfo &gsr_info, mgl::vec2i window_size, const std::string &resources_path); + bool init_theme(const GsrInfo &gsr_info, mgl::vec2i window_size, const std::string &resources_path); void deinit_theme(); Theme& get_theme(); } \ No newline at end of file diff --git a/include/Utils.hpp b/include/Utils.hpp index f917834..6943e5e 100644 --- a/include/Utils.hpp +++ b/include/Utils.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -16,9 +15,6 @@ namespace gsr { void string_split_char(std::string_view str, char delimiter, StringSplitCallback callback_func); - // key value separated by one space - std::optional parse_key_value(std::string_view line); - std::string get_home_dir(); std::string get_config_dir(); diff --git a/include/gui/Button.hpp b/include/gui/Button.hpp index f4c44ef..a4dcca1 100644 --- a/include/gui/Button.hpp +++ b/include/gui/Button.hpp @@ -21,6 +21,8 @@ namespace gsr { mgl::vec2f get_size() override; void set_border_scale(float scale); + const std::string& get_text() const; + std::function on_click; private: mgl::vec2f size; diff --git a/include/gui/ComboBox.hpp b/include/gui/ComboBox.hpp index e501132..96489e6 100644 --- a/include/gui/ComboBox.hpp +++ b/include/gui/ComboBox.hpp @@ -19,7 +19,7 @@ namespace gsr { void draw(mgl::Window &window, mgl::vec2f offset) override; void add_item(const std::string &text, const std::string &id); - void set_selected_item(const std::string &id, bool trigger_event = true); + void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true); const std::string& get_selected_id() const; mgl::vec2f get_size() override; diff --git a/include/gui/FileChooser.hpp b/include/gui/FileChooser.hpp new file mode 100644 index 0000000..b8ea23e --- /dev/null +++ b/include/gui/FileChooser.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "Widget.hpp" +#include +#include + +#include +#include + +// This currently only supports displaying folders +// TODO: Support files as well + +namespace gsr { + class FileChooser : public Widget { + public: + FileChooser(const char *start_directory, mgl::vec2f content_size); + FileChooser(const FileChooser&) = delete; + FileChooser& operator=(const FileChooser&) = 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; + + void set_current_directory(const char *directory); + private: + mgl::vec2f content_size; + mgl::Text current_directory_text; + int mouse_over_item = -1; + int selected_item = -1; + std::vector folders; + mgl::Clock double_click_timer; + int times_clicked_within_timer = 0; + }; +} \ No newline at end of file diff --git a/include/gui/GsrPage.hpp b/include/gui/GsrPage.hpp new file mode 100644 index 0000000..9f38382 --- /dev/null +++ b/include/gui/GsrPage.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "Page.hpp" +#include "ScrollablePage.hpp" +#include "Button.hpp" + +#include + +namespace gsr { + class GsrPage : public Page { + public: + GsrPage(); + GsrPage(const GsrPage&) = delete; + GsrPage& operator=(const GsrPage&) = 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 add_widget(std::unique_ptr widget) override; + + void set_margins(float top, float bottom, float left, float right); + void set_on_back_button_click(std::function on_click_handler); + private: + void draw_page_label(mgl::Window &window, mgl::vec2f body_pos); + float get_border_size() const; + float get_horizontal_spacing() const; + private: + float margin_top_scale = 0.0f; + float margin_bottom_scale = 0.0f; + float margin_left_scale = 0.0f; + float margin_right_scale = 0.0f; + ScrollablePage scrollable_body; + Button back_button; + mgl::Text label_text; + }; +} \ No newline at end of file diff --git a/include/gui/List.hpp b/include/gui/List.hpp index 241a75c..55a5b84 100644 --- a/include/gui/List.hpp +++ b/include/gui/List.hpp @@ -1,7 +1,7 @@ #pragma once #include "Widget.hpp" -#include +#include "../SafeVector.hpp" #include namespace gsr { @@ -27,28 +27,20 @@ namespace gsr { //void remove_child_widget(Widget *widget) override; - // Might not take effect immediately but at the next draw iteration if inside an event loop void add_widget(std::unique_ptr widget); - // Might not take effect immediately but at the next draw iteration if inside an event loop void remove_widget(Widget *widget); - // Excludes widgets from queue - const std::vector>& get_child_widgets() const; + // Return true from |callback| to continue + void for_each_child_widget(std::function &widget)> callback); // Returns nullptr if index is invalid Widget* get_child_widget_by_index(size_t index) const; void set_spacing(float spacing); mgl::vec2f get_size() override; - private: - void update(); - void remove_widget_immediate(Widget *widget); protected: - std::vector> widgets; - std::vector> add_queue; - std::vector remove_queue; + SafeVector> widgets; Orientation orientation; Alignment content_alignment; - bool inside_event_handler = false; float spacing_scale = 0.009f; }; } \ No newline at end of file diff --git a/include/gui/Page.hpp b/include/gui/Page.hpp index 4c63702..a8fa515 100644 --- a/include/gui/Page.hpp +++ b/include/gui/Page.hpp @@ -17,7 +17,7 @@ namespace gsr { //void remove_child_widget(Widget *widget) override; - void add_widget(std::unique_ptr widget); + virtual void add_widget(std::unique_ptr widget); protected: std::vector> widgets; }; diff --git a/include/gui/PageStack.hpp b/include/gui/PageStack.hpp new file mode 100644 index 0000000..cf42b7d --- /dev/null +++ b/include/gui/PageStack.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Widget.hpp" +#include "Page.hpp" +#include "../SafeVector.hpp" +#include + +namespace gsr { + class PageStack : public Widget { + public: + PageStack(); + PageStack(const PageStack&) = delete; + PageStack& operator=(const PageStack&) = 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; + + void push(std::unique_ptr page); + // This can be used even if the stack is empty + void pop(); + // This can be used even if the stack is empty + Page* top(); + bool empty() const; + private: + SafeVector> widget_stack; + }; +} \ No newline at end of file diff --git a/include/gui/RadioButton.hpp b/include/gui/RadioButton.hpp index 7839c68..69339db 100644 --- a/include/gui/RadioButton.hpp +++ b/include/gui/RadioButton.hpp @@ -17,7 +17,7 @@ namespace gsr { void draw(mgl::Window &window, mgl::vec2f offset) override; void add_item(const std::string &text, const std::string &id); - void set_selected_item(const std::string &id, bool trigger_event = true); + void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true); const std::string get_selected_id() const; mgl::vec2f get_size() override; diff --git a/include/gui/ScrollablePage.hpp b/include/gui/ScrollablePage.hpp index 60d719f..5048627 100644 --- a/include/gui/ScrollablePage.hpp +++ b/include/gui/ScrollablePage.hpp @@ -1,9 +1,11 @@ #pragma once -#include "Page.hpp" +#include "Widget.hpp" +#include +#include namespace gsr { - class ScrollablePage : public Page { + class ScrollablePage : public Widget { public: ScrollablePage(mgl::vec2f size); ScrollablePage(const ScrollablePage&) = delete; @@ -13,16 +15,11 @@ namespace gsr { 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); - void set_margins(float top, float bottom, float left, float right); - private: - float get_border_size() const; + void add_widget(std::unique_ptr widget); private: mgl::vec2f size; - float margin_top_scale = 0.0f; - float margin_bottom_scale = 0.0f; - float margin_left_scale = 0.0f; - float margin_right_scale = 0.0f; + std::vector> widgets; }; } \ No newline at end of file diff --git a/include/gui/SettingsPage.hpp b/include/gui/SettingsPage.hpp index 1b301bf..1ad4c67 100644 --- a/include/gui/SettingsPage.hpp +++ b/include/gui/SettingsPage.hpp @@ -7,14 +7,12 @@ #include "RadioButton.hpp" #include "CheckBox.hpp" #include "Button.hpp" -#include "CustomRendererWidget.hpp" #include "../GsrInfo.hpp" #include "../Config.hpp" -#include - namespace gsr { - class ScrollablePage; + class GsrPage; + class PageStack; class SettingsPage : public StaticPage { public: @@ -24,17 +22,13 @@ namespace gsr { STREAM }; - SettingsPage(Type type, const GsrInfo &gsr_info, const std::vector &audio_devices, std::optional &config); + SettingsPage(Type type, const GsrInfo &gsr_info, const std::vector &audio_devices, std::optional &config, PageStack *page_stack); SettingsPage(const SettingsPage&) = delete; SettingsPage& operator=(const SettingsPage&) = delete; void save(); void on_navigate_away_from_page() override; - - std::function on_back_button_handler; private: - std::unique_ptr