diff options
author | dec05eba <dec05eba@protonmail.com> | 2024-08-22 21:44:06 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2024-08-23 18:53:19 +0200 |
commit | 54c60d9a18d103011a12939c5029dd35a8e9e200 (patch) | |
tree | fefba1d63a13df6c135a6b1f5e8640b12a9c0ff5 /include | |
parent | ba007c2b69dca6813c86115b2c1834de45c886be (diff) |
Start on file chooser, page stack
Diffstat (limited to 'include')
-rw-r--r-- | include/GsrInfo.hpp | 1 | ||||
-rw-r--r-- | include/SafeVector.hpp | 127 | ||||
-rw-r--r-- | include/Theme.hpp | 5 | ||||
-rw-r--r-- | include/Utils.hpp | 4 | ||||
-rw-r--r-- | include/gui/Button.hpp | 2 | ||||
-rw-r--r-- | include/gui/ComboBox.hpp | 2 | ||||
-rw-r--r-- | include/gui/FileChooser.hpp | 35 | ||||
-rw-r--r-- | include/gui/GsrPage.hpp | 39 | ||||
-rw-r--r-- | include/gui/List.hpp | 16 | ||||
-rw-r--r-- | include/gui/Page.hpp | 2 | ||||
-rw-r--r-- | include/gui/PageStack.hpp | 29 | ||||
-rw-r--r-- | include/gui/RadioButton.hpp | 2 | ||||
-rw-r--r-- | include/gui/ScrollablePage.hpp | 17 | ||||
-rw-r--r-- | include/gui/SettingsPage.hpp | 20 |
14 files changed, 259 insertions, 42 deletions
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 <vector> +#include <memory> +#include <functional> + +// A vector that can be modified while iterating +template <typename T> +class SafeVector { +public: + using PointerType = typename std::pointer_traits<T>::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<bool(T &t)> 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<bool(T &t)> 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<T> data; + std::vector<PointerType> 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 <functional> -#include <optional> #include <string_view> #include <map> #include <string> @@ -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<KeyValue> 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<void()> 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 <functional> +#include <vector> + +#include <mglpp/graphics/Text.hpp> +#include <mglpp/system/Clock.hpp> + +// 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<mgl::Text> 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 <mglpp/graphics/Text.hpp> + +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> widget) override; + + void set_margins(float top, float bottom, float left, float right); + void set_on_back_button_click(std::function<void()> 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 <vector> +#include "../SafeVector.hpp" #include <memory> 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> 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<std::unique_ptr<Widget>>& get_child_widgets() const; + // Return true from |callback| to continue + void for_each_child_widget(std::function<bool(std::unique_ptr<Widget> &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<std::unique_ptr<Widget>> widgets; - std::vector<std::unique_ptr<Widget>> add_queue; - std::vector<Widget*> remove_queue; + SafeVector<std::unique_ptr<Widget>> 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> widget); + virtual void add_widget(std::unique_ptr<Widget> widget); protected: std::vector<std::unique_ptr<Widget>> 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 <memory> + +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> 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<std::unique_ptr<Page>> 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 <memory> +#include <vector> 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> 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<std::unique_ptr<Widget>> 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 <functional> - 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<AudioDevice> &audio_devices, std::optional<Config> &config); + SettingsPage(Type type, const GsrInfo &gsr_info, const std::vector<AudioDevice> &audio_devices, std::optional<Config> &config, PageStack *page_stack); SettingsPage(const SettingsPage&) = delete; SettingsPage& operator=(const SettingsPage&) = delete; void save(); void on_navigate_away_from_page() override; - - std::function<void()> on_back_button_handler; private: - std::unique_ptr<Button> create_back_button(); - std::unique_ptr<CustomRendererWidget> create_settings_icon(); std::unique_ptr<RadioButton> create_view_radio_button(); std::unique_ptr<ComboBox> create_record_area_box(const GsrInfo &gsr_info); std::unique_ptr<List> create_record_area(const GsrInfo &gsr_info); @@ -69,7 +63,7 @@ namespace gsr { std::unique_ptr<List> create_framerate_mode(); std::unique_ptr<List> create_framerate_section(); std::unique_ptr<List> create_settings(const GsrInfo &gsr_info, const std::vector<AudioDevice> &audio_devices); - void add_widgets(const gsr::GsrInfo &gsr_info, const std::vector<gsr::AudioDevice> &audio_devices); + void add_widgets(const GsrInfo &gsr_info, const std::vector<AudioDevice> &audio_devices); void add_page_specific_widgets(); @@ -97,7 +91,7 @@ namespace gsr { Type type; std::optional<Config> &config; - ScrollablePage *content_page_ptr = nullptr; + GsrPage *content_page_ptr = nullptr; List *settings_list_ptr = nullptr; List *select_window_list_ptr = nullptr; List *area_size_list_ptr = nullptr; @@ -131,12 +125,14 @@ namespace gsr { CheckBox *show_video_saved_notification_checkbox_ptr = nullptr; CheckBox *show_streaming_started_notification_checkbox_ptr = nullptr; CheckBox *show_streaming_stopped_notification_checkbox_ptr = nullptr; - Entry *save_directory_entry_ptr = nullptr; + Button *save_directory_button_ptr = nullptr; Entry *twitch_stream_key_entry_ptr = nullptr; Entry *youtube_stream_key_entry_ptr = nullptr; Entry *stream_url_entry_ptr = nullptr; Entry *replay_time_entry_ptr = nullptr; + PageStack *page_stack = nullptr; + mgl::Text settings_title_text; }; }
\ No newline at end of file |