diff options
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/Button.cpp | 44 | ||||
-rw-r--r-- | src/gui/ComboBox.cpp | 34 | ||||
-rw-r--r-- | src/gui/CustomRendererWidget.cpp | 14 | ||||
-rw-r--r-- | src/gui/DropdownButton.cpp | 4 | ||||
-rw-r--r-- | src/gui/FileChooser.cpp | 12 | ||||
-rw-r--r-- | src/gui/GlobalSettingsPage.cpp | 653 | ||||
-rw-r--r-- | src/gui/GsrPage.cpp | 13 | ||||
-rw-r--r-- | src/gui/RadioButton.cpp | 20 | ||||
-rw-r--r-- | src/gui/ScrollablePage.cpp | 39 | ||||
-rw-r--r-- | src/gui/SettingsPage.cpp | 296 | ||||
-rw-r--r-- | src/gui/StaticPage.cpp | 12 | ||||
-rw-r--r-- | src/gui/Utils.cpp | 17 |
12 files changed, 982 insertions, 176 deletions
diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp index fbf5cdd..54d1854 100644 --- a/src/gui/Button.cpp +++ b/src/gui/Button.cpp @@ -12,7 +12,15 @@ namespace gsr { static const float padding_left_scale = 0.007f; static const float padding_right_scale = 0.007f; - Button::Button(mgl::Font *font, const char *text, mgl::vec2f size, mgl::Color bg_color) : size(size), bg_color(bg_color), text(text, *font) { + // These are relative to the button size + static const float padding_top_icon_scale = 0.25f; + static const float padding_bottom_icon_scale = 0.25f; + static const float padding_left_icon_scale = 0.25f; + static const float padding_right_icon_scale = 0.25f; + + Button::Button(mgl::Font *font, const char *text, mgl::vec2f size, mgl::Color bg_color) : + size(size), bg_color(bg_color), bg_hover_color(bg_color), text(text, *font) + { } @@ -37,17 +45,23 @@ namespace gsr { return; const mgl::vec2f draw_pos = position + offset; - const mgl::vec2f item_size = get_size().floor(); + const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget(); + mgl::Rectangle background(item_size); background.set_position(draw_pos.floor()); - background.set_color(bg_color); + background.set_color(mouse_inside ? bg_hover_color : bg_color); window.draw(background); text.set_position((draw_pos + item_size * 0.5f - text.get_bounds().size * 0.5f).floor()); window.draw(text); - const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget(); + if(sprite.get_texture() && sprite.get_texture()->is_valid()) { + scale_sprite_to_button_size(); + sprite.set_position((background.get_position() + background.get_size() * 0.5f - sprite.get_size() * 0.5f).floor()); + window.draw(sprite); + } + if(mouse_inside) { const mgl::Color outline_color = (bg_color == get_color_theme().tint_color) ? mgl::Color(255, 255, 255) : get_color_theme().tint_color; draw_rectangle_outline(window, draw_pos, item_size, outline_color, std::max(1.0f, border_scale * get_theme().window_height)); @@ -76,6 +90,14 @@ namespace gsr { border_scale = scale; } + void Button::set_bg_hover_color(mgl::Color color) { + bg_hover_color = color; + } + + void Button::set_icon(mgl::Texture *texture) { + sprite.set_texture(texture); + } + const std::string& Button::get_text() const { return text.get_string(); } @@ -83,4 +105,18 @@ namespace gsr { void Button::set_text(std::string str) { text.set_string(std::move(str)); } + + void Button::scale_sprite_to_button_size() { + if(!sprite.get_texture() || !sprite.get_texture()->is_valid()) + return; + + const mgl::vec2f button_size = get_size(); + const int padding_icon_top = padding_top_icon_scale * button_size.y; + const int padding_icon_bottom = padding_bottom_icon_scale * button_size.y; + const int padding_icon_left = padding_left_icon_scale * button_size.y; + const int padding_icon_right = padding_right_icon_scale * button_size.y; + + const mgl::vec2f desired_size = button_size - mgl::vec2f(padding_icon_left + padding_icon_right, padding_icon_top + padding_icon_bottom); + sprite.set_size(scale_keep_aspect_ratio(sprite.get_texture()->get_size().to_vec2f(), desired_size).floor()); + } }
\ No newline at end of file diff --git a/src/gui/ComboBox.cpp b/src/gui/ComboBox.cpp index 62b2086..948e3a4 100644 --- a/src/gui/ComboBox.cpp +++ b/src/gui/ComboBox.cpp @@ -26,16 +26,21 @@ namespace gsr { return true; if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) { + const int padding_top = padding_top_scale * get_theme().window_height; + const int padding_bottom = padding_bottom_scale * get_theme().window_height; + const mgl::vec2f mouse_pos = { (float)event.mouse_button.x, (float)event.mouse_button.y }; - const mgl::vec2f item_size = get_size(); + mgl::vec2f item_size = get_size(); if(show_dropdown) { for(size_t i = 0; i < items.size(); ++i) { Item &item = items[i]; + item_size.y = padding_top + item.text.get_bounds().size.y + padding_bottom; if(mgl::FloatRect(item.position, item_size).contains(mouse_pos)) { const size_t prev_selected_item = selected_item; selected_item = i; show_dropdown = false; + dirty = true; remove_widget_as_selected_in_parent(); if(selected_item != prev_selected_item && on_selection_changed) @@ -47,6 +52,7 @@ namespace gsr { } const mgl::vec2f draw_pos = position + offset; + item_size = get_size(); if(mgl::FloatRect(draw_pos, item_size).contains(mouse_pos)) { show_dropdown = !show_dropdown; if(show_dropdown) @@ -66,9 +72,10 @@ namespace gsr { if(!visible) return; + //const mgl::Scissor scissor = window.get_scissor(); update_if_dirty(); - const mgl::vec2f draw_pos = (position + offset).floor(); + //max_size.x = std::min((scissor.position.x + scissor.size.x) - draw_pos.x, max_size.x); if(show_dropdown) draw_selected(window, draw_pos); @@ -78,6 +85,8 @@ namespace gsr { void ComboBox::add_item(const std::string &text, const std::string &id) { items.push_back({mgl::Text(text, *font), id, {0.0f, 0.0f}}); + items.back().text.set_max_width(font->get_character_size() * 22); // TODO: Make a proper solution + //items.back().text.set_max_rows(1); dirty = true; } @@ -87,6 +96,7 @@ namespace gsr { if(item.id == id) { const size_t prev_selected_item = selected_item; selected_item = i; + dirty = true; 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); @@ -107,13 +117,13 @@ namespace gsr { void ComboBox::draw_selected(mgl::Window &window, mgl::vec2f draw_pos) { const int padding_top = padding_top_scale * get_theme().window_height; + const int padding_bottom = padding_bottom_scale * get_theme().window_height; const int padding_left = padding_left_scale * get_theme().window_height; - mgl_scissor scissor; - mgl_window_get_scissor(window.internal_window(), &scissor); + const mgl::Scissor scissor = window.get_scissor(); const bool bottom_is_outside_scissor = draw_pos.y + max_size.y > scissor.position.y + scissor.size.y; - const mgl::vec2f item_size = get_size(); + mgl::vec2f item_size = get_size(); mgl::vec2f items_draw_pos = draw_pos + mgl::vec2f(0.0f, item_size.y); mgl::Rectangle background(draw_pos, item_size.floor()); @@ -137,6 +147,9 @@ namespace gsr { const mgl::vec2f mouse_pos = window.get_mouse_position().to_vec2f(); for(size_t i = 0; i < items.size(); ++i) { + Item &item = items[i]; + item_size.y = padding_top + item.text.get_bounds().size.y + padding_bottom; + if(!cursor_inside) { cursor_inside = mgl::FloatRect(items_draw_pos, item_size).contains(mouse_pos); if(cursor_inside) { @@ -146,7 +159,6 @@ namespace gsr { } } - Item &item = items[i]; item.text.set_position((items_draw_pos + mgl::vec2f(padding_left, padding_top)).floor()); window.draw(item.text); @@ -160,7 +172,7 @@ namespace gsr { const int padding_left = padding_left_scale * get_theme().window_height; const int padding_right = padding_right_scale * get_theme().window_height; - const mgl::vec2f item_size = get_size(); + mgl::vec2f item_size = get_size(); mgl::Rectangle background(draw_pos.floor(), item_size.floor()); background.set_color(mgl::Color(0, 0, 0, 120)); window.draw(background); @@ -197,11 +209,12 @@ namespace gsr { const int padding_left = padding_left_scale * get_theme().window_height; const int padding_right = padding_right_scale * get_theme().window_height; - max_size = { 0.0f, font->get_character_size() + (float)padding_top + (float)padding_bottom }; + Item *selected_item_ptr = (selected_item < items.size()) ? &items[selected_item] : nullptr; + max_size = { 0.0f, padding_top + padding_bottom + (selected_item_ptr ? selected_item_ptr->text.get_bounds().size.y : 0.0f) }; for(Item &item : items) { const mgl::vec2f bounds = item.text.get_bounds().size; max_size.x = std::max(max_size.x, bounds.x + padding_left + padding_right); - max_size.y += bounds.y + padding_top + padding_bottom; + max_size.y += padding_top + bounds.y + padding_bottom; } if(max_size.x <= 0.001f) @@ -219,7 +232,8 @@ namespace gsr { const int padding_top = padding_top_scale * get_theme().window_height; const int padding_bottom = padding_bottom_scale * get_theme().window_height; - return { max_size.x, font->get_character_size() + (float)padding_top + (float)padding_bottom }; + Item *selected_item_ptr = (selected_item < items.size()) ? &items[selected_item] : nullptr; + return { max_size.x, padding_top + padding_bottom + (selected_item_ptr ? selected_item_ptr->text.get_bounds().size.y : 0.0f) }; } float ComboBox::get_dropdown_arrow_height() const { diff --git a/src/gui/CustomRendererWidget.cpp b/src/gui/CustomRendererWidget.cpp index cfb113b..5b6c809 100644 --- a/src/gui/CustomRendererWidget.cpp +++ b/src/gui/CustomRendererWidget.cpp @@ -17,19 +17,11 @@ namespace gsr { const mgl::vec2f draw_pos = position + offset; - mgl_scissor prev_scissor; - mgl_window_get_scissor(window.internal_window(), &prev_scissor); - - mgl_scissor new_scissor = { - mgl_vec2i{(int)draw_pos.x, (int)draw_pos.y}, - mgl_vec2i{(int)size.x, (int)size.y} - }; - mgl_window_set_scissor(window.internal_window(), &new_scissor); - + const mgl::Scissor prev_scissor = window.get_scissor(); + window.set_scissor({draw_pos.to_vec2i(), size.to_vec2i()}); if(draw_handler) draw_handler(window, draw_pos, size); - - mgl_window_set_scissor(window.internal_window(), &prev_scissor); + window.set_scissor(prev_scissor); } mgl::vec2f CustomRendererWidget::get_size() { diff --git a/src/gui/DropdownButton.cpp b/src/gui/DropdownButton.cpp index 4a2ae3a..81bc015 100644 --- a/src/gui/DropdownButton.cpp +++ b/src/gui/DropdownButton.cpp @@ -20,7 +20,7 @@ namespace gsr { { if(icon_texture && icon_texture->is_valid()) { icon_sprite.set_texture(icon_texture); - icon_sprite.set_height((int)(size.y * 0.5f)); + icon_sprite.set_height((int)(size.y * 0.45f)); } this->description.set_color(mgl::Color(150, 150, 150)); } @@ -242,4 +242,4 @@ namespace gsr { update_if_dirty(); return size; } -}
\ No newline at end of file +} diff --git a/src/gui/FileChooser.cpp b/src/gui/FileChooser.cpp index a58a582..ceb8c94 100644 --- a/src/gui/FileChooser.cpp +++ b/src/gui/FileChooser.cpp @@ -65,8 +65,7 @@ namespace gsr { if(!visible) return; - mgl_scissor scissor; - mgl_window_get_scissor(window.internal_window(), &scissor); + const mgl::Scissor scissor = window.get_scissor(); const mgl::vec2f draw_pos = position + offset; const mgl::vec2f mouse_pos = window.get_mouse_position().to_vec2f(); @@ -96,7 +95,12 @@ namespace gsr { selected_item_background.set_color(get_color_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)) { + + if(!has_parent_with_selected_child_widget() && mouse_over_item == -1 && + mouse_pos.x >= scissor.position.x && mouse_pos.x <= scissor.position.x + scissor.size.x && + mouse_pos.y >= scissor.position.y && mouse_pos.y <= scissor.position.y + scissor.size.y && + 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)); @@ -106,7 +110,7 @@ namespace gsr { mouse_over_item = i; } - if(item_pos.y + item_size.y >= draw_pos.y && item_pos.y < scissor.position.y + scissor.size.y) { + if(item_pos.y + item_size.y >= scissor.position.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). diff --git a/src/gui/GlobalSettingsPage.cpp b/src/gui/GlobalSettingsPage.cpp new file mode 100644 index 0000000..d00ad49 --- /dev/null +++ b/src/gui/GlobalSettingsPage.cpp @@ -0,0 +1,653 @@ +#include "../../include/gui/GlobalSettingsPage.hpp" + +#include "../../include/Overlay.hpp" +#include "../../include/GlobalHotkeys.hpp" +#include "../../include/Theme.hpp" +#include "../../include/Process.hpp" +#include "../../include/gui/GsrPage.hpp" +#include "../../include/gui/PageStack.hpp" +#include "../../include/gui/ScrollablePage.hpp" +#include "../../include/gui/Subsection.hpp" +#include "../../include/gui/List.hpp" +#include "../../include/gui/Label.hpp" +#include "../../include/gui/RadioButton.hpp" +#include "../../include/gui/LineSeparator.hpp" +#include "../../include/gui/CustomRendererWidget.hpp" + +#include <assert.h> +#include <X11/Xlib.h> +extern "C" { +#include <mgl/mgl.h> +} +#include <mglpp/window/Window.hpp> +#include <mglpp/graphics/Rectangle.hpp> +#include <mglpp/graphics/Text.hpp> + +#ifndef GSR_UI_VERSION +#define GSR_UI_VERSION "Unknown" +#endif + +#ifndef GSR_FLATPAK_VERSION +#define GSR_FLATPAK_VERSION "Unknown" +#endif + +namespace gsr { + static const char* gpu_vendor_to_color_name(GpuVendor vendor) { + switch(vendor) { + case GpuVendor::UNKNOWN: return "amd"; + case GpuVendor::AMD: return "amd"; + case GpuVendor::INTEL: return "intel"; + case GpuVendor::NVIDIA: return "nvidia"; + } + return "amd"; + } + + static const char* gpu_vendor_to_string(GpuVendor vendor) { + switch(vendor) { + case GpuVendor::UNKNOWN: return "Unknown"; + case GpuVendor::AMD: return "AMD"; + case GpuVendor::INTEL: return "Intel"; + case GpuVendor::NVIDIA: return "NVIDIA"; + } + return "unknown"; + } + + static uint32_t mgl_modifier_to_hotkey_modifier(mgl::Keyboard::Key modifier_key) { + switch(modifier_key) { + case mgl::Keyboard::LControl: return HOTKEY_MOD_LCTRL; + case mgl::Keyboard::LShift: return HOTKEY_MOD_LSHIFT; + case mgl::Keyboard::LAlt: return HOTKEY_MOD_LALT; + case mgl::Keyboard::LSystem: return HOTKEY_MOD_LSUPER; + case mgl::Keyboard::RControl: return HOTKEY_MOD_RCTRL; + case mgl::Keyboard::RShift: return HOTKEY_MOD_RSHIFT; + case mgl::Keyboard::RAlt: return HOTKEY_MOD_RALT; + case mgl::Keyboard::RSystem: return HOTKEY_MOD_RSUPER; + default: return 0; + } + return 0; + } + + static std::vector<mgl::Keyboard::Key> hotkey_modifiers_to_mgl_keys(uint32_t modifiers) { + std::vector<mgl::Keyboard::Key> result; + if(modifiers & HOTKEY_MOD_LCTRL) + result.push_back(mgl::Keyboard::LControl); + if(modifiers & HOTKEY_MOD_LSHIFT) + result.push_back(mgl::Keyboard::LShift); + if(modifiers & HOTKEY_MOD_LALT) + result.push_back(mgl::Keyboard::LAlt); + if(modifiers & HOTKEY_MOD_LSUPER) + result.push_back(mgl::Keyboard::LSystem); + if(modifiers & HOTKEY_MOD_RCTRL) + result.push_back(mgl::Keyboard::RControl); + if(modifiers & HOTKEY_MOD_RSHIFT) + result.push_back(mgl::Keyboard::RShift); + if(modifiers & HOTKEY_MOD_RALT) + result.push_back(mgl::Keyboard::RAlt); + if(modifiers & HOTKEY_MOD_RSUPER) + result.push_back(mgl::Keyboard::RSystem); + return result; + } + + static std::string config_hotkey_to_string(ConfigHotkey config_hotkey) { + std::string result; + + const std::vector<mgl::Keyboard::Key> modifier_keys = hotkey_modifiers_to_mgl_keys(config_hotkey.modifiers); + for(const mgl::Keyboard::Key modifier_key : modifier_keys) { + if(!result.empty()) + result += " + "; + result += mgl::Keyboard::key_to_string(modifier_key); + } + + if(config_hotkey.key != 0) { + if(!result.empty()) + result += " + "; + result += mgl::Keyboard::key_to_string((mgl::Keyboard::Key)config_hotkey.key); + } + + return result; + } + + GlobalSettingsPage::GlobalSettingsPage(Overlay *overlay, const GsrInfo *gsr_info, Config &config, PageStack *page_stack) : + StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()), + overlay(overlay), + config(config), + gsr_info(gsr_info), + page_stack(page_stack) + { + auto content_page = std::make_unique<GsrPage>(); + content_page->add_button("Back", "back", get_color_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)); + + add_widgets(); + load(); + + auto hotkey_overlay = std::make_unique<CustomRendererWidget>(get_size()); + hotkey_overlay->draw_handler = [this](mgl::Window &window, mgl::vec2f, mgl::vec2f) { + Button *configure_hotkey_button = configure_hotkey_get_button_by_active_type(); + if(!configure_hotkey_button) + return; + + mgl::Text title_text("Press a key combination to use for the hotkey \"" + hotkey_configure_action_name + "\":", get_theme().title_font); + mgl::Text hotkey_text(configure_hotkey_button->get_text(), get_theme().top_bar_font); + mgl::Text description_text("The hotkey has to contain one or more of these keys: Alt, Ctrl, Shift and Super. Press Esc to cancel.", get_theme().body_font); + const float text_max_width = std::max(title_text.get_bounds().size.x, std::max(hotkey_text.get_bounds().size.x, description_text.get_bounds().size.x)); + + const float padding_horizontal = int(get_theme().window_height * 0.01f); + const float padding_vertical = int(get_theme().window_height * 0.01f); + + const mgl::vec2f bg_size = mgl::vec2f(text_max_width + padding_horizontal*2.0f, get_theme().window_height * 0.1f).floor(); + mgl::Rectangle bg_rect(mgl::vec2f(get_theme().window_width*0.5f - bg_size.x*0.5f, get_theme().window_height*0.5f - bg_size.y*0.5f).floor(), bg_size); + bg_rect.set_color(get_color_theme().page_bg_color); + window.draw(bg_rect); + + const mgl::vec2f tint_size = mgl::vec2f(bg_size.x, 0.004f * get_theme().window_height).floor(); + mgl::Rectangle tint_rect(bg_rect.get_position() - mgl::vec2f(0.0f, tint_size.y), tint_size); + tint_rect.set_color(get_color_theme().tint_color); + window.draw(tint_rect); + + title_text.set_position(mgl::vec2f(bg_rect.get_position() + mgl::vec2f(bg_rect.get_size().x*0.5f - title_text.get_bounds().size.x*0.5f, padding_vertical)).floor()); + window.draw(title_text); + + hotkey_text.set_position(mgl::vec2f(bg_rect.get_position() + bg_rect.get_size()*0.5f - hotkey_text.get_bounds().size*0.5f).floor()); + window.draw(hotkey_text); + + const float caret_padding_x = int(0.001f * get_theme().window_height); + const mgl::vec2f caret_size = mgl::vec2f(std::max(2.0f, 0.002f * get_theme().window_height), hotkey_text.get_bounds().size.y).floor(); + mgl::Rectangle caret_rect(hotkey_text.get_position() + mgl::vec2f(hotkey_text.get_bounds().size.x + caret_padding_x, hotkey_text.get_bounds().size.y*0.5f - caret_size.y*0.5f).floor(), caret_size); + window.draw(caret_rect); + + description_text.set_position(mgl::vec2f(bg_rect.get_position() + mgl::vec2f(bg_rect.get_size().x*0.5f - description_text.get_bounds().size.x*0.5f, bg_rect.get_size().y - description_text.get_bounds().size.y - padding_vertical)).floor()); + window.draw(description_text); + }; + hotkey_overlay->set_visible(false); + hotkey_overlay_ptr = hotkey_overlay.get(); + add_widget(std::move(hotkey_overlay)); + } + + std::unique_ptr<Subsection> GlobalSettingsPage::create_appearance_subsection(ScrollablePage *parent_page) { + auto list = std::make_unique<List>(List::Orientation::VERTICAL); + list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Accent color", get_color_theme().text_color)); + auto tint_color_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL); + tint_color_radio_button_ptr = tint_color_radio_button.get(); + tint_color_radio_button->add_item("Red", "amd"); + tint_color_radio_button->add_item("Green", "nvidia"); + tint_color_radio_button->add_item("blue", "intel"); + tint_color_radio_button->on_selection_changed = [](const std::string&, const std::string &id) { + if(id == "amd") + get_color_theme().tint_color = mgl::Color(221, 0, 49); + else if(id == "nvidia") + get_color_theme().tint_color = mgl::Color(118, 185, 0); + else if(id == "intel") + get_color_theme().tint_color = mgl::Color(8, 109, 183); + return true; + }; + list->add_widget(std::move(tint_color_radio_button)); + return std::make_unique<Subsection>("Appearance", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f)); + } + + std::unique_ptr<Subsection> GlobalSettingsPage::create_startup_subsection(ScrollablePage *parent_page) { + auto list = std::make_unique<List>(List::Orientation::VERTICAL); + list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Start program on system startup?", get_color_theme().text_color)); + auto startup_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL); + startup_radio_button_ptr = startup_radio_button.get(); + startup_radio_button->add_item("Yes", "start_on_system_startup"); + startup_radio_button->add_item("No", "dont_start_on_system_startup"); + startup_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) { + bool enable = false; + if(id == "dont_start_on_system_startup") + enable = false; + else if(id == "start_on_system_startup") + enable = true; + else + return false; + + const char *args[] = { "systemctl", enable ? "enable" : "disable", "--user", "gpu-screen-recorder-ui", nullptr }; + std::string stdout_str; + const int exit_status = exec_program_on_host_get_stdout(args, stdout_str); + if(on_startup_changed) + on_startup_changed(enable, exit_status); + return exit_status == 0; + }; + list->add_widget(std::move(startup_radio_button)); + return std::make_unique<Subsection>("Startup", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f)); + } + + std::unique_ptr<RadioButton> GlobalSettingsPage::create_enable_keyboard_hotkeys_button() { + auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL); + enable_keyboard_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get(); + enable_hotkeys_radio_button->add_item("Yes", "enable_hotkeys"); + enable_hotkeys_radio_button->add_item("No", "disable_hotkeys"); + enable_hotkeys_radio_button->add_item("Only grab virtual devices (supports input remapping software)", "enable_hotkeys_virtual_devices"); + enable_hotkeys_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) { + if(on_keyboard_hotkey_changed) + on_keyboard_hotkey_changed(id.c_str()); + return true; + }; + return enable_hotkeys_radio_button; + } + + std::unique_ptr<RadioButton> GlobalSettingsPage::create_enable_joystick_hotkeys_button() { + auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL); + enable_joystick_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get(); + enable_hotkeys_radio_button->add_item("Yes", "enable_hotkeys"); + enable_hotkeys_radio_button->add_item("No", "disable_hotkeys"); + enable_hotkeys_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) { + if(on_joystick_hotkey_changed) + on_joystick_hotkey_changed(id.c_str()); + return true; + }; + return enable_hotkeys_radio_button; + } + + std::unique_ptr<List> GlobalSettingsPage::create_show_hide_hotkey_options() { + auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); + + list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Show/hide UI:", get_color_theme().text_color)); + auto show_hide_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + show_hide_button_ptr = show_hide_button.get(); + list->add_widget(std::move(show_hide_button)); + + show_hide_button_ptr->on_click = [this] { + configure_hotkey_start(ConfigureHotkeyType::SHOW_HIDE); + }; + + return list; + } + + std::unique_ptr<List> GlobalSettingsPage::create_replay_hotkey_options() { + auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); + + list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Turn replay on/off:", get_color_theme().text_color)); + auto turn_replay_on_off_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + turn_replay_on_off_button_ptr = turn_replay_on_off_button.get(); + list->add_widget(std::move(turn_replay_on_off_button)); + + list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Save replay:", get_color_theme().text_color)); + auto save_replay_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + save_replay_button_ptr = save_replay_button.get(); + list->add_widget(std::move(save_replay_button)); + + turn_replay_on_off_button_ptr->on_click = [this] { + configure_hotkey_start(ConfigureHotkeyType::REPLAY_START_STOP); + }; + + save_replay_button_ptr->on_click = [this] { + configure_hotkey_start(ConfigureHotkeyType::REPLAY_SAVE); + }; + + return list; + } + + std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_options() { + auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); + + list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Start/stop recording:", get_color_theme().text_color)); + auto start_stop_recording_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + start_stop_recording_button_ptr = start_stop_recording_button.get(); + list->add_widget(std::move(start_stop_recording_button)); + + list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Pause/unpause recording:", get_color_theme().text_color)); + auto pause_unpause_recording_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + pause_unpause_recording_button_ptr = pause_unpause_recording_button.get(); + list->add_widget(std::move(pause_unpause_recording_button)); + + start_stop_recording_button_ptr->on_click = [this] { + configure_hotkey_start(ConfigureHotkeyType::RECORD_START_STOP); + }; + + pause_unpause_recording_button_ptr->on_click = [this] { + configure_hotkey_start(ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE); + }; + + return list; + } + + std::unique_ptr<List> GlobalSettingsPage::create_stream_hotkey_options() { + auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); + + list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Start/stop streaming:", get_color_theme().text_color)); + auto start_stop_streaming_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + start_stop_streaming_button_ptr = start_stop_streaming_button.get(); + list->add_widget(std::move(start_stop_streaming_button)); + + start_stop_streaming_button_ptr->on_click = [this] { + configure_hotkey_start(ConfigureHotkeyType::STREAM_START_STOP); + }; + + return list; + } + + std::unique_ptr<List> GlobalSettingsPage::create_hotkey_control_buttons() { + auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); + + // auto clear_hotkeys_button = std::make_unique<Button>(&get_theme().body_font, "Clear hotkeys", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + // clear_hotkeys_button->on_click = [this] { + // config.streaming_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0}; + // config.record_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0}; + // config.record_config.pause_unpause_hotkey = {mgl::Keyboard::Unknown, 0}; + // config.replay_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0}; + // config.replay_config.save_hotkey = {mgl::Keyboard::Unknown, 0}; + // config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0}; + // load_hotkeys(); + // overlay->rebind_all_keyboard_hotkeys(); + // }; + // list->add_widget(std::move(clear_hotkeys_button)); + + auto reset_hotkeys_button = std::make_unique<Button>(&get_theme().body_font, "Reset hotkeys to default", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + reset_hotkeys_button->on_click = [this] { + config.streaming_config.start_stop_hotkey = {mgl::Keyboard::F8, HOTKEY_MOD_LALT}; + config.record_config.start_stop_hotkey = {mgl::Keyboard::F9, HOTKEY_MOD_LALT}; + config.record_config.pause_unpause_hotkey = {mgl::Keyboard::F7, HOTKEY_MOD_LALT}; + config.replay_config.start_stop_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT | HOTKEY_MOD_LSHIFT}; + config.replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT}; + config.main_config.show_hide_hotkey = {mgl::Keyboard::Z, HOTKEY_MOD_LALT}; + load_hotkeys(); + overlay->rebind_all_keyboard_hotkeys(); + }; + list->add_widget(std::move(reset_hotkeys_button)); + + return list; + } + + std::unique_ptr<Subsection> GlobalSettingsPage::create_hotkey_subsection(ScrollablePage *parent_page) { + auto list = std::make_unique<List>(List::Orientation::VERTICAL); + List *list_ptr = list.get(); + auto subsection = std::make_unique<Subsection>("Hotkeys", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f)); + + list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, "Enable keyboard hotkeys?", get_color_theme().text_color)); + list_ptr->add_widget(create_enable_keyboard_hotkeys_button()); + list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, "Enable controller hotkeys?", get_color_theme().text_color)); + list_ptr->add_widget(create_enable_joystick_hotkeys_button()); + list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x)); + list_ptr->add_widget(create_show_hide_hotkey_options()); + list_ptr->add_widget(create_replay_hotkey_options()); + list_ptr->add_widget(create_record_hotkey_options()); + list_ptr->add_widget(create_stream_hotkey_options()); + list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, "Double-click the controller share button to save a replay", get_color_theme().text_color)); + list_ptr->add_widget(create_hotkey_control_buttons()); + return subsection; + } + + std::unique_ptr<Button> GlobalSettingsPage::create_exit_program_button() { + auto exit_program_button = std::make_unique<Button>(&get_theme().body_font, "Exit program", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + exit_program_button->on_click = [&]() { + if(on_click_exit_program_button) + on_click_exit_program_button("exit"); + }; + return exit_program_button; + } + + std::unique_ptr<Button> GlobalSettingsPage::create_go_back_to_old_ui_button() { + auto exit_program_button = std::make_unique<Button>(&get_theme().body_font, "Go back to the old UI", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); + exit_program_button->on_click = [&]() { + if(on_click_exit_program_button) + on_click_exit_program_button("back-to-old-ui"); + }; + return exit_program_button; + } + + std::unique_ptr<Subsection> GlobalSettingsPage::create_application_options_subsection(ScrollablePage *parent_page) { + const bool inside_flatpak = getenv("FLATPAK_ID") != NULL; + auto list = std::make_unique<List>(List::Orientation::HORIZONTAL); + list->add_widget(create_exit_program_button()); + if(inside_flatpak) + list->add_widget(create_go_back_to_old_ui_button()); + return std::make_unique<Subsection>("Application options", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f)); + } + + std::unique_ptr<Subsection> GlobalSettingsPage::create_application_info_subsection(ScrollablePage *parent_page) { + const bool inside_flatpak = getenv("FLATPAK_ID") != NULL; + auto list = std::make_unique<List>(List::Orientation::VERTICAL); + + char str[128]; + const std::string gsr_version = gsr_info->system_info.gsr_version.to_string(); + snprintf(str, sizeof(str), "GSR version: %s", gsr_version.c_str()); + list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color)); + + snprintf(str, sizeof(str), "GSR-UI version: %s", GSR_UI_VERSION); + list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color)); + + if(inside_flatpak) { + snprintf(str, sizeof(str), "Flatpak version: %s", GSR_FLATPAK_VERSION); + list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color)); + } + + snprintf(str, sizeof(str), "GPU vendor: %s", gpu_vendor_to_string(gsr_info->gpu_info.vendor)); + list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color)); + + return std::make_unique<Subsection>("Application info", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f)); + } + + void GlobalSettingsPage::add_widgets() { + auto scrollable_page = std::make_unique<ScrollablePage>(content_page_ptr->get_inner_size()); + + auto settings_list = std::make_unique<List>(List::Orientation::VERTICAL); + settings_list->set_spacing(0.018f); + settings_list->add_widget(create_appearance_subsection(scrollable_page.get())); + settings_list->add_widget(create_startup_subsection(scrollable_page.get())); + settings_list->add_widget(create_hotkey_subsection(scrollable_page.get())); + settings_list->add_widget(create_application_options_subsection(scrollable_page.get())); + settings_list->add_widget(create_application_info_subsection(scrollable_page.get())); + scrollable_page->add_widget(std::move(settings_list)); + + content_page_ptr->add_widget(std::move(scrollable_page)); + } + + void GlobalSettingsPage::on_navigate_away_from_page() { + save(); + } + + void GlobalSettingsPage::load() { + if(config.main_config.tint_color.empty()) + tint_color_radio_button_ptr->set_selected_item(gpu_vendor_to_color_name(gsr_info->gpu_info.vendor)); + else + tint_color_radio_button_ptr->set_selected_item(config.main_config.tint_color); + + const char *args[] = { "systemctl", "is-enabled", "--quiet", "--user", "gpu-screen-recorder-ui", nullptr }; + std::string stdout_str; + const int exit_status = exec_program_on_host_get_stdout(args, stdout_str); + startup_radio_button_ptr->set_selected_item(exit_status == 0 ? "start_on_system_startup" : "dont_start_on_system_startup", false, false); + + enable_keyboard_hotkeys_radio_button_ptr->set_selected_item(config.main_config.hotkeys_enable_option, false, false); + enable_joystick_hotkeys_radio_button_ptr->set_selected_item(config.main_config.joystick_hotkeys_enable_option, false, false); + + load_hotkeys(); + } + + void GlobalSettingsPage::load_hotkeys() { + turn_replay_on_off_button_ptr->set_text(config_hotkey_to_string(config.replay_config.start_stop_hotkey)); + save_replay_button_ptr->set_text(config_hotkey_to_string(config.replay_config.save_hotkey)); + + start_stop_recording_button_ptr->set_text(config_hotkey_to_string(config.record_config.start_stop_hotkey)); + pause_unpause_recording_button_ptr->set_text(config_hotkey_to_string(config.record_config.pause_unpause_hotkey)); + + start_stop_streaming_button_ptr->set_text(config_hotkey_to_string(config.streaming_config.start_stop_hotkey)); + + show_hide_button_ptr->set_text(config_hotkey_to_string(config.main_config.show_hide_hotkey)); + } + + void GlobalSettingsPage::save() { + configure_hotkey_cancel(); + config.main_config.tint_color = tint_color_radio_button_ptr->get_selected_id(); + config.main_config.hotkeys_enable_option = enable_keyboard_hotkeys_radio_button_ptr->get_selected_id(); + config.main_config.joystick_hotkeys_enable_option = enable_joystick_hotkeys_radio_button_ptr->get_selected_id(); + save_config(config); + } + + bool GlobalSettingsPage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) { + if(!StaticPage::on_event(event, window, offset)) + return false; + + if(configure_hotkey_type == ConfigureHotkeyType::NONE) + return true; + + Button *configure_hotkey_button = configure_hotkey_get_button_by_active_type(); + if(!configure_hotkey_button) + return true; + + if(event.type == mgl::Event::KeyPressed) { + if(event.key.code == mgl::Keyboard::Escape) + return false; + + if(mgl::Keyboard::key_is_modifier(event.key.code)) { + configure_config_hotkey.modifiers |= mgl_modifier_to_hotkey_modifier(event.key.code); + configure_hotkey_button->set_text(config_hotkey_to_string(configure_config_hotkey)); + } else if(configure_config_hotkey.modifiers != 0) { + configure_config_hotkey.key = event.key.code; + configure_hotkey_button->set_text(config_hotkey_to_string(configure_config_hotkey)); + configure_hotkey_stop_and_save(); + } + + return false; + } else if(event.type == mgl::Event::KeyReleased) { + if(event.key.code == mgl::Keyboard::Escape) { + configure_hotkey_cancel(); + return false; + } + + if(mgl::Keyboard::key_is_modifier(event.key.code)) { + configure_config_hotkey.modifiers &= ~mgl_modifier_to_hotkey_modifier(event.key.code); + configure_hotkey_button->set_text(config_hotkey_to_string(configure_config_hotkey)); + } + + return false; + } + + return true; + } + + Button* GlobalSettingsPage::configure_hotkey_get_button_by_active_type() { + switch(configure_hotkey_type) { + case ConfigureHotkeyType::NONE: + return nullptr; + case ConfigureHotkeyType::REPLAY_START_STOP: + return turn_replay_on_off_button_ptr; + case ConfigureHotkeyType::REPLAY_SAVE: + return save_replay_button_ptr; + case ConfigureHotkeyType::RECORD_START_STOP: + return start_stop_recording_button_ptr; + case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE: + return pause_unpause_recording_button_ptr; + case ConfigureHotkeyType::STREAM_START_STOP: + return start_stop_streaming_button_ptr; + case ConfigureHotkeyType::SHOW_HIDE: + return show_hide_button_ptr; + } + return nullptr; + } + + ConfigHotkey* GlobalSettingsPage::configure_hotkey_get_config_by_active_type() { + switch(configure_hotkey_type) { + case ConfigureHotkeyType::NONE: + return nullptr; + case ConfigureHotkeyType::REPLAY_START_STOP: + return &config.replay_config.start_stop_hotkey; + case ConfigureHotkeyType::REPLAY_SAVE: + return &config.replay_config.save_hotkey; + case ConfigureHotkeyType::RECORD_START_STOP: + return &config.record_config.start_stop_hotkey; + case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE: + return &config.record_config.pause_unpause_hotkey; + case ConfigureHotkeyType::STREAM_START_STOP: + return &config.streaming_config.start_stop_hotkey; + case ConfigureHotkeyType::SHOW_HIDE: + return &config.main_config.show_hide_hotkey; + } + return nullptr; + } + + void GlobalSettingsPage::for_each_config_hotkey(std::function<void(ConfigHotkey *config_hotkey)> callback) { + ConfigHotkey *config_hotkeys[] = { + &config.replay_config.start_stop_hotkey, + &config.replay_config.save_hotkey, + &config.record_config.start_stop_hotkey, + &config.record_config.pause_unpause_hotkey, + &config.streaming_config.start_stop_hotkey, + &config.main_config.show_hide_hotkey + }; + for(ConfigHotkey *config_hotkey : config_hotkeys) { + callback(config_hotkey); + } + } + + void GlobalSettingsPage::configure_hotkey_start(ConfigureHotkeyType hotkey_type) { + assert(hotkey_type != ConfigureHotkeyType::NONE); + configure_config_hotkey = {0, 0}; + configure_hotkey_type = hotkey_type; + + content_page_ptr->set_visible(false); + hotkey_overlay_ptr->set_visible(true); + overlay->unbind_all_keyboard_hotkeys(); + configure_hotkey_get_button_by_active_type()->set_text(""); + + switch(hotkey_type) { + case ConfigureHotkeyType::NONE: + hotkey_configure_action_name = ""; + break; + case ConfigureHotkeyType::REPLAY_START_STOP: + hotkey_configure_action_name = "Turn replay on/off"; + break; + case ConfigureHotkeyType::REPLAY_SAVE: + hotkey_configure_action_name = "Save replay"; + break; + case ConfigureHotkeyType::RECORD_START_STOP: + hotkey_configure_action_name = "Start/stop recording"; + break; + case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE: + hotkey_configure_action_name = "Pause/unpause recording"; + break; + case ConfigureHotkeyType::STREAM_START_STOP: + hotkey_configure_action_name = "Start/stop streaming"; + break; + case ConfigureHotkeyType::SHOW_HIDE: + hotkey_configure_action_name = "Show/hide UI"; + break; + } + } + + void GlobalSettingsPage::configure_hotkey_cancel() { + Button *config_hotkey_button = configure_hotkey_get_button_by_active_type(); + ConfigHotkey *config_hotkey = configure_hotkey_get_config_by_active_type(); + if(config_hotkey_button && config_hotkey) + config_hotkey_button->set_text(config_hotkey_to_string(*config_hotkey)); + + configure_config_hotkey = {0, 0}; + configure_hotkey_type = ConfigureHotkeyType::NONE; + content_page_ptr->set_visible(true); + hotkey_overlay_ptr->set_visible(false); + overlay->rebind_all_keyboard_hotkeys(); + } + + void GlobalSettingsPage::configure_hotkey_stop_and_save() { + Button *config_hotkey_button = configure_hotkey_get_button_by_active_type(); + ConfigHotkey *config_hotkey = configure_hotkey_get_config_by_active_type(); + if(config_hotkey_button && config_hotkey) { + bool hotkey_used_by_another_action = false; + for_each_config_hotkey([&](ConfigHotkey *config_hotkey_item) { + if(config_hotkey_item != config_hotkey && *config_hotkey_item == configure_config_hotkey) + hotkey_used_by_another_action = true; + }); + + if(hotkey_used_by_another_action) { + const std::string error_msg = "The hotkey \"" + config_hotkey_to_string(configure_config_hotkey) + " is already used for something else"; + overlay->show_notification(error_msg.c_str(), 3.0, mgl::Color(255, 0, 0, 255), mgl::Color(255, 0, 0, 255), NotificationType::NONE); + config_hotkey_button->set_text(config_hotkey_to_string(*config_hotkey)); + configure_config_hotkey = {0, 0}; + return; + } + + *config_hotkey = configure_config_hotkey; + } + + configure_config_hotkey = {0, 0}; + configure_hotkey_type = ConfigureHotkeyType::NONE; + content_page_ptr->set_visible(true); + hotkey_overlay_ptr->set_visible(false); + overlay->rebind_all_keyboard_hotkeys(); + } +} diff --git a/src/gui/GsrPage.cpp b/src/gui/GsrPage.cpp index e6ee5fc..c5fa263 100644 --- a/src/gui/GsrPage.cpp +++ b/src/gui/GsrPage.cpp @@ -102,15 +102,8 @@ namespace gsr { void GsrPage::draw_children(mgl::Window &window, mgl::vec2f position) { Widget *selected_widget = selected_child_widget; - mgl_scissor prev_scissor; - mgl_window_get_scissor(window.internal_window(), &prev_scissor); - - const mgl::vec2f inner_size = get_inner_size(); - mgl_scissor new_scissor = { - mgl_vec2i{(int)position.x, (int)position.y}, - mgl_vec2i{(int)inner_size.x, (int)inner_size.y} - }; - mgl_window_set_scissor(window.internal_window(), &new_scissor); + const mgl::Scissor prev_scissor = window.get_scissor(); + window.set_scissor({position.to_vec2i(), get_inner_size().to_vec2i()}); for(size_t i = 0; i < widgets.size(); ++i) { auto &widget = widgets[i]; @@ -121,7 +114,7 @@ namespace gsr { if(selected_widget) selected_widget->draw(window, position); - mgl_window_set_scissor(window.internal_window(), &prev_scissor); + window.set_scissor(prev_scissor); } mgl::vec2f GsrPage::get_size() { diff --git a/src/gui/RadioButton.cpp b/src/gui/RadioButton.cpp index 061d811..a6ef96a 100644 --- a/src/gui/RadioButton.cpp +++ b/src/gui/RadioButton.cpp @@ -35,12 +35,12 @@ namespace gsr { const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y)); if(mouse_inside) { - const size_t prev_selected_item = selected_item; - selected_item = i; - - if(selected_item != prev_selected_item && on_selection_changed) - on_selection_changed(item.text.get_string(), item.id); + if(selected_item != i && on_selection_changed) { + if(!on_selection_changed(item.text.get_string(), item.id)) + return false; + } + selected_item = i; return false; } @@ -158,12 +158,12 @@ namespace gsr { 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 && (trigger_event_even_if_selection_not_changed || selected_item != prev_selected_item) && on_selection_changed) - on_selection_changed(item.text.get_string(), item.id); + if(trigger_event && (trigger_event_even_if_selection_not_changed || selected_item != i) && on_selection_changed) { + if(!on_selection_changed(item.text.get_string(), item.id)) + break; + } + selected_item = i; break; } } diff --git a/src/gui/ScrollablePage.cpp b/src/gui/ScrollablePage.cpp index 74dd715..d5e92d0 100644 --- a/src/gui/ScrollablePage.cpp +++ b/src/gui/ScrollablePage.cpp @@ -19,15 +19,37 @@ namespace gsr { if(!visible) return true; - offset = position + offset + mgl::vec2f(0.0f, scroll_y); + offset = position + offset; + + const mgl::vec2f content_size = get_inner_size(); + const mgl::vec2i scissor_pos(offset.x, offset.y); + const mgl::vec2i scissor_size(content_size.x, content_size.y); + + offset.y += scroll_y; Widget *selected_widget = selected_child_widget; + if(event.type == mgl::Event::MouseButtonPressed && scrollbar_rect.contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) { + set_widget_as_selected_in_parent(); + moving_scrollbar_with_cursor = true; + scrollbar_move_cursor_start_pos = mgl::vec2f(event.mouse_button.x, event.mouse_button.y); + scrollbar_move_cursor_scroll_y_start = scroll_y; + return false; + } + if(event.type == mgl::Event::MouseButtonReleased && moving_scrollbar_with_cursor) { moving_scrollbar_with_cursor = false; remove_widget_as_selected_in_parent(); return false; } + if(event.type == mgl::Event::MouseButtonPressed || event.type == mgl::Event::MouseButtonReleased) { + if(!mgl::IntRect(scissor_pos, scissor_size).contains({event.mouse_button.x, event.mouse_button.y})) + return true; + } else if(event.type == mgl::Event::MouseMoved) { + if(!mgl::IntRect(scissor_pos, scissor_size).contains({event.mouse_move.x, event.mouse_move.y})) + return true; + } + if(selected_widget) { if(!selected_widget->on_event(event, window, offset)) return false; @@ -51,14 +73,6 @@ namespace gsr { return false; } - if(event.type == mgl::Event::MouseButtonPressed && scrollbar_rect.contains(mgl::vec2f(event.mouse_button.x, event.mouse_button.y))) { - set_widget_as_selected_in_parent(); - moving_scrollbar_with_cursor = true; - scrollbar_move_cursor_start_pos = mgl::vec2f(event.mouse_button.x, event.mouse_button.y); - scrollbar_move_cursor_scroll_y_start = scroll_y; - return false; - } - return true; } @@ -75,11 +89,10 @@ namespace gsr { offset = position + offset; - mgl_scissor prev_scissor; - mgl_window_get_scissor(window.internal_window(), &prev_scissor); + const mgl::Scissor prev_scissor = window.get_scissor(); const mgl::vec2f content_size = get_inner_size(); - mgl_scissor new_scissor = { + const mgl_scissor new_scissor = { mgl_vec2i{(int)offset.x, (int)offset.y}, mgl_vec2i{(int)content_size.x, (int)content_size.y} }; @@ -136,7 +149,7 @@ namespace gsr { apply_animation(); limit_scroll(child_height); - mgl_window_set_scissor(window.internal_window(), &prev_scissor); + window.set_scissor(prev_scissor); double scrollbar_height = 1.0; if(child_height > 0.001) diff --git a/src/gui/SettingsPage.cpp b/src/gui/SettingsPage.cpp index 79f6c52..4d1109a 100644 --- a/src/gui/SettingsPage.cpp +++ b/src/gui/SettingsPage.cpp @@ -22,15 +22,16 @@ namespace gsr { APPLICATION_CUSTOM }; - SettingsPage::SettingsPage(Type type, const GsrInfo &gsr_info, Config &config, PageStack *page_stack) : + SettingsPage::SettingsPage(Type type, const GsrInfo *gsr_info, Config &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) + gsr_info(gsr_info), + page_stack(page_stack) { audio_devices = get_audio_devices(); application_audio = get_application_audio(); + capture_options = get_supported_capture_options(*gsr_info); auto content_page = std::make_unique<GsrPage>(); content_page->add_button("Back", "back", get_color_theme().page_bg_color); @@ -41,9 +42,9 @@ namespace gsr { content_page_ptr = content_page.get(); add_widget(std::move(content_page)); - add_widgets(gsr_info); - add_page_specific_widgets(gsr_info); - load(gsr_info); + add_widgets(); + add_page_specific_widgets(); + load(); } std::unique_ptr<RadioButton> SettingsPage::create_view_radio_button() { @@ -55,31 +56,29 @@ namespace gsr { return view_radio_button; } - std::unique_ptr<ComboBox> SettingsPage::create_record_area_box(const GsrInfo &gsr_info) { + std::unique_ptr<ComboBox> SettingsPage::create_record_area_box() { auto record_area_box = std::make_unique<ComboBox>(&get_theme().body_font); // TODO: Show options not supported but disable them // TODO: Enable this - //if(gsr_info.supported_capture_options.window) + //if(capture_options.window) // record_area_box->add_item("Window", "window"); - if(gsr_info.supported_capture_options.focused) + if(capture_options.focused) record_area_box->add_item("Follow focused window", "focused"); - if(gsr_info.supported_capture_options.screen) - record_area_box->add_item("All monitors", "screen"); - for(const auto &monitor : gsr_info.supported_capture_options.monitors) { + for(const auto &monitor : capture_options.monitors) { char name[256]; snprintf(name, sizeof(name), "Monitor %s (%dx%d)", monitor.name.c_str(), monitor.size.x, monitor.size.y); record_area_box->add_item(name, monitor.name); } - if(gsr_info.supported_capture_options.portal) + if(capture_options.portal) record_area_box->add_item("Desktop portal", "portal"); record_area_box_ptr = record_area_box.get(); return record_area_box; } - std::unique_ptr<Widget> SettingsPage::create_record_area(const GsrInfo &gsr_info) { + std::unique_ptr<Widget> SettingsPage::create_record_area() { auto record_area_list = std::make_unique<List>(List::Orientation::VERTICAL); record_area_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Capture target:", get_color_theme().text_color)); - record_area_list->add_widget(create_record_area_box(gsr_info)); + record_area_list->add_widget(create_record_area_box()); return record_area_list; } @@ -172,11 +171,11 @@ namespace gsr { return checkbox; } - std::unique_ptr<Widget> SettingsPage::create_capture_target(const GsrInfo &gsr_info) { + std::unique_ptr<Widget> SettingsPage::create_capture_target() { auto ll = std::make_unique<List>(List::Orientation::VERTICAL); auto capture_target_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); - capture_target_list->add_widget(create_record_area(gsr_info)); + capture_target_list->add_widget(create_record_area()); capture_target_list->add_widget(create_select_window()); capture_target_list->add_widget(create_area_size_section()); capture_target_list->add_widget(create_video_resolution_section()); @@ -305,7 +304,8 @@ namespace gsr { std::unique_ptr<Widget> SettingsPage::create_audio_section() { auto audio_device_section_list = std::make_unique<List>(List::Orientation::VERTICAL); audio_device_section_list->add_widget(create_audio_track_section()); - audio_device_section_list->add_widget(create_merge_audio_tracks_checkbox()); + if(type != Type::STREAM) + audio_device_section_list->add_widget(create_merge_audio_tracks_checkbox()); audio_device_section_list->add_widget(create_application_audio_invert_checkbox()); audio_device_section_list->add_widget(create_audio_codec()); return std::make_unique<Subsection>("Audio", std::move(audio_device_section_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)); @@ -339,11 +339,27 @@ namespace gsr { return list; } - std::unique_ptr<Entry> SettingsPage::create_video_bitrate_entry() { + std::unique_ptr<List> SettingsPage::create_video_bitrate_entry() { + auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); auto video_bitrate_entry = std::make_unique<Entry>(&get_theme().body_font, "15000", (int)(get_theme().body_font.get_character_size() * 4.0f)); video_bitrate_entry->validate_handler = create_entry_validator_integer_in_range(1, 500000); video_bitrate_entry_ptr = video_bitrate_entry.get(); - return video_bitrate_entry; + list->add_widget(std::move(video_bitrate_entry)); + + if(type == Type::STREAM) { + auto size_mb_label = std::make_unique<Label>(&get_theme().body_font, "1.92MB", get_color_theme().text_color); + Label *size_mb_label_ptr = size_mb_label.get(); + list->add_widget(std::move(size_mb_label)); + + video_bitrate_entry_ptr->on_changed = [size_mb_label_ptr](const std::string &text) { + const double video_bitrate_mb_per_seconds = (double)atoi(text.c_str()) / 1000LL / 8LL * 1.024; + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%.2fMB", video_bitrate_mb_per_seconds); + size_mb_label_ptr->set_text(buffer); + }; + } + + return list; } std::unique_ptr<List> SettingsPage::create_video_bitrate() { @@ -378,40 +394,40 @@ namespace gsr { return quality_list; } - std::unique_ptr<ComboBox> SettingsPage::create_video_codec_box(const GsrInfo &gsr_info) { + std::unique_ptr<ComboBox> SettingsPage::create_video_codec_box() { auto video_codec_box = std::make_unique<ComboBox>(&get_theme().body_font); // TODO: Show options not supported but disable them. // TODO: Show error if no encoders are supported. // TODO: Show warning (once) if only software encoder is available. video_codec_box->add_item("Auto (Recommended)", "auto"); - if(gsr_info.supported_video_codecs.h264) + if(gsr_info->supported_video_codecs.h264) video_codec_box->add_item("H264", "h264"); - if(gsr_info.supported_video_codecs.hevc) + if(gsr_info->supported_video_codecs.hevc) video_codec_box->add_item("HEVC", "hevc"); - if(gsr_info.supported_video_codecs.av1) + if(gsr_info->supported_video_codecs.hevc_10bit) + video_codec_box->add_item("HEVC (10 bit, reduces banding)", "hevc_10bit"); + if(gsr_info->supported_video_codecs.hevc_hdr) + video_codec_box->add_item("HEVC (HDR)", "hevc_hdr"); + if(gsr_info->supported_video_codecs.av1) video_codec_box->add_item("AV1", "av1"); - if(gsr_info.supported_video_codecs.vp8) + if(gsr_info->supported_video_codecs.av1_10bit) + video_codec_box->add_item("AV1 (10 bit, reduces banding)", "av1_10bit"); + if(gsr_info->supported_video_codecs.av1_hdr) + video_codec_box->add_item("AV1 (HDR)", "av1_hdr"); + if(gsr_info->supported_video_codecs.vp8) video_codec_box->add_item("VP8", "vp8"); - if(gsr_info.supported_video_codecs.vp9) + if(gsr_info->supported_video_codecs.vp9) video_codec_box->add_item("VP9", "vp9"); - if(gsr_info.supported_video_codecs.hevc_hdr) - video_codec_box->add_item("HEVC (HDR)", "hevc_hdr"); - if(gsr_info.supported_video_codecs.hevc_10bit) - video_codec_box->add_item("HEVC (10 bit, reduces banding)", "hevc_10bit"); - if(gsr_info.supported_video_codecs.av1_hdr) - video_codec_box->add_item("AV1 (HDR)", "av1_hdr"); - if(gsr_info.supported_video_codecs.av1_10bit) - video_codec_box->add_item("AV1 (10 bit, reduces banding)", "av1_10bit"); - if(gsr_info.supported_video_codecs.h264_software) + if(gsr_info->supported_video_codecs.h264_software) video_codec_box->add_item("H264 Software Encoder (Slow, not recommended)", "h264_software"); video_codec_box_ptr = video_codec_box.get(); return video_codec_box; } - std::unique_ptr<List> SettingsPage::create_video_codec(const GsrInfo &gsr_info) { + std::unique_ptr<List> SettingsPage::create_video_codec() { auto video_codec_list = std::make_unique<List>(List::Orientation::VERTICAL); video_codec_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Video codec:", get_color_theme().text_color)); - video_codec_list->add_widget(create_video_codec_box(gsr_info)); + video_codec_list->add_widget(create_video_codec_box()); video_codec_ptr = video_codec_list.get(); return video_codec_list; } @@ -477,16 +493,16 @@ namespace gsr { return record_cursor_checkbox; } - std::unique_ptr<Widget> SettingsPage::create_video_section(const GsrInfo &gsr_info) { + std::unique_ptr<Widget> SettingsPage::create_video_section() { auto video_section_list = std::make_unique<List>(List::Orientation::VERTICAL); video_section_list->add_widget(create_video_quality_section()); - video_section_list->add_widget(create_video_codec(gsr_info)); + video_section_list->add_widget(create_video_codec()); video_section_list->add_widget(create_framerate_section()); video_section_list->add_widget(create_record_cursor_section()); return std::make_unique<Subsection>("Video", std::move(video_section_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)); } - std::unique_ptr<Widget> SettingsPage::create_settings(const GsrInfo &gsr_info) { + std::unique_ptr<Widget> SettingsPage::create_settings() { auto page_list = std::make_unique<List>(List::Orientation::VERTICAL); page_list->set_spacing(0.018f); page_list->add_widget(create_view_radio_button()); @@ -496,16 +512,16 @@ namespace gsr { auto settings_list = std::make_unique<List>(List::Orientation::VERTICAL); settings_list->set_spacing(0.018f); - settings_list->add_widget(create_capture_target(gsr_info)); + settings_list->add_widget(create_capture_target()); settings_list->add_widget(create_audio_section()); - settings_list->add_widget(create_video_section(gsr_info)); + settings_list->add_widget(create_video_section()); settings_list_ptr = settings_list.get(); settings_scrollable_page_ptr->add_widget(std::move(settings_list)); return page_list; } - void SettingsPage::add_widgets(const GsrInfo &gsr_info) { - content_page_ptr->add_widget(create_settings(gsr_info)); + void SettingsPage::add_widgets() { + content_page_ptr->add_widget(create_settings()); record_area_box_ptr->on_selection_changed = [this](const std::string &text, const std::string &id) { (void)text; @@ -517,6 +533,7 @@ namespace gsr { video_resolution_list_ptr->set_visible(!focused_selected && change_video_resolution_checkbox_ptr->is_checked()); change_video_resolution_checkbox_ptr->set_visible(!focused_selected); restore_portal_session_list_ptr->set_visible(portal_selected); + return true; }; change_video_resolution_checkbox_ptr->on_changed = [this](bool checked) { @@ -531,35 +548,37 @@ namespace gsr { if(estimated_file_size_ptr) estimated_file_size_ptr->set_visible(custom_selected); + + return true; }; video_quality_box_ptr->on_selection_changed("", video_quality_box_ptr->get_selected_id()); - if(!gsr_info.supported_capture_options.monitors.empty()) - record_area_box_ptr->set_selected_item(gsr_info.supported_capture_options.monitors.front().name); - else if(gsr_info.supported_capture_options.portal) + if(!capture_options.monitors.empty()) + record_area_box_ptr->set_selected_item(capture_options.monitors.front().name); + else if(capture_options.portal) record_area_box_ptr->set_selected_item("portal"); - else if(gsr_info.supported_capture_options.window) + else if(capture_options.window) record_area_box_ptr->set_selected_item("window"); else record_area_box_ptr->on_selection_changed("", ""); - if(!gsr_info.system_info.supports_app_audio) { + if(!gsr_info->system_info.supports_app_audio) { add_application_audio_button_ptr->set_visible(false); add_custom_application_audio_button_ptr->set_visible(false); application_audio_invert_checkbox_ptr->set_visible(false); } } - void SettingsPage::add_page_specific_widgets(const GsrInfo &gsr_info) { + void SettingsPage::add_page_specific_widgets() { switch(type) { case Type::REPLAY: - add_replay_widgets(gsr_info); + add_replay_widgets(); break; case Type::RECORD: - add_record_widgets(gsr_info); + add_record_widgets(); break; case Type::STREAM: - add_stream_widgets(gsr_info); + add_stream_widgets(); break; } } @@ -579,10 +598,12 @@ namespace gsr { 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") + if(id == "save") { save_directory_button_ptr->set_text(file_chooser_ptr->get_current_directory()); - else if(id == "cancel") page_stack->pop(); + } else if(id == "cancel") { + page_stack->pop(); + } }; page_stack->push(std::move(select_directory_page)); @@ -608,70 +629,100 @@ namespace gsr { return container_list; } - std::unique_ptr<Entry> SettingsPage::create_replay_time_entry() { + std::unique_ptr<List> SettingsPage::create_replay_time_entry() { + auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); + auto replay_time_entry = std::make_unique<Entry>(&get_theme().body_font, "60", get_theme().body_font.get_character_size() * 3); - replay_time_entry->validate_handler = create_entry_validator_integer_in_range(1, 1200); + replay_time_entry->validate_handler = create_entry_validator_integer_in_range(1, 10800); replay_time_entry_ptr = replay_time_entry.get(); - return replay_time_entry; + list->add_widget(std::move(replay_time_entry)); + + auto replay_time_label = std::make_unique<Label>(&get_theme().body_font, "00h:00m:00s", get_color_theme().text_color); + replay_time_label_ptr = replay_time_label.get(); + list->add_widget(std::move(replay_time_label)); + + return list; } std::unique_ptr<List> SettingsPage::create_replay_time() { auto replay_time_list = std::make_unique<List>(List::Orientation::VERTICAL); - replay_time_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Replay time in seconds:", get_color_theme().text_color)); + replay_time_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Replay duration in seconds:", get_color_theme().text_color)); replay_time_list->add_widget(create_replay_time_entry()); return replay_time_list; } - std::unique_ptr<RadioButton> SettingsPage::create_start_replay_automatically(const GsrInfo &gsr_info) { + std::unique_ptr<RadioButton> SettingsPage::create_start_replay_automatically() { char fullscreen_text[256]; - snprintf(fullscreen_text, sizeof(fullscreen_text), "Turn on replay when starting a fullscreen application%s", gsr_info.system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)"); + snprintf(fullscreen_text, sizeof(fullscreen_text), "Turn on replay when starting a fullscreen application%s", gsr_info->system_info.display_server == DisplayServer::X11 ? "" : " (X11 applications only)"); auto radiobutton = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::VERTICAL); radiobutton->add_item("Don't turn on replay automatically", "dont_turn_on_automatically"); - radiobutton->add_item("Turn on replay at system startup", "turn_on_at_system_startup"); + radiobutton->add_item("Turn on replay when this program starts", "turn_on_at_system_startup"); radiobutton->add_item(fullscreen_text, "turn_on_at_fullscreen"); radiobutton->add_item("Turn on replay when power supply is connected", "turn_on_at_power_supply_connected"); turn_on_replay_automatically_mode_ptr = radiobutton.get(); return radiobutton; } - std::unique_ptr<CheckBox> SettingsPage::create_save_replay_in_game_folder(const GsrInfo &gsr_info) { + std::unique_ptr<CheckBox> SettingsPage::create_save_replay_in_game_folder() { char text[256]; - snprintf(text, sizeof(text), "Save video in a folder with the name of the game%s", gsr_info.system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)"); + snprintf(text, sizeof(text), "Save video in a folder with the name of the game%s", gsr_info->system_info.display_server == DisplayServer::X11 ? "" : " (X11 applications only)"); auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text); save_replay_in_game_folder_ptr = checkbox.get(); return checkbox; } - std::unique_ptr<Label> SettingsPage::create_estimated_file_size() { - auto label = std::make_unique<Label>(&get_theme().body_font, "Estimated video max file size in RAM: 5.23MB", get_color_theme().text_color); + std::unique_ptr<CheckBox> SettingsPage::create_restart_replay_on_save() { + auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Restart replay on save"); + restart_replay_on_save = checkbox.get(); + return checkbox; + } + + std::unique_ptr<Label> SettingsPage::create_estimated_replay_file_size() { + auto label = std::make_unique<Label>(&get_theme().body_font, "Estimated video max file size in RAM: 57.60MB", get_color_theme().text_color); estimated_file_size_ptr = label.get(); return label; } - void SettingsPage::update_estimated_file_size() { + void SettingsPage::update_estimated_replay_file_size() { const int64_t replay_time_seconds = atoi(replay_time_entry_ptr->get_text().c_str()); const int64_t video_bitrate_bps = atoi(video_bitrate_entry_ptr->get_text().c_str()) * 1000LL / 8LL; - const double video_filesize_mb = ((double)replay_time_seconds * (double)video_bitrate_bps) / 1024.0 / 1024.0; + const double video_filesize_mb = ((double)replay_time_seconds * (double)video_bitrate_bps) / 1000.0 / 1000.0 * 1.024; - char buffer[512]; - snprintf(buffer, sizeof(buffer), "Estimated video max file size in RAM: %.2fMB", video_filesize_mb); + char buffer[256]; + snprintf(buffer, sizeof(buffer), "Estimated video max file size in RAM: %.2fMB.\nChange video bitrate or replay duration to change file size.", video_filesize_mb); estimated_file_size_ptr->set_text(buffer); } - void SettingsPage::add_replay_widgets(const GsrInfo &gsr_info) { + void SettingsPage::update_replay_time_text() { + int seconds = atoi(replay_time_entry_ptr->get_text().c_str()); + + const int hours = seconds / 60 / 60; + seconds -= (hours * 60 * 60); + + const int minutes = seconds / 60; + seconds -= (minutes * 60); + + char buffer[256]; + snprintf(buffer, sizeof(buffer), "%02dh:%02dm:%02ds", hours, minutes, seconds); + replay_time_label_ptr->set_text(buffer); + } + + void SettingsPage::add_replay_widgets() { auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL); auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL); file_info_data_list->add_widget(create_save_directory("Directory to save replays:")); file_info_data_list->add_widget(create_container_section()); file_info_data_list->add_widget(create_replay_time()); file_info_list->add_widget(std::move(file_info_data_list)); - file_info_list->add_widget(create_estimated_file_size()); + file_info_list->add_widget(create_estimated_replay_file_size()); settings_list_ptr->add_widget(std::make_unique<Subsection>("File info", std::move(file_info_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f))); auto general_list = std::make_unique<List>(List::Orientation::VERTICAL); - general_list->add_widget(create_start_replay_automatically(gsr_info)); - general_list->add_widget(create_save_replay_in_game_folder(gsr_info)); + general_list->add_widget(create_start_replay_automatically()); + general_list->add_widget(create_save_replay_in_game_folder()); + if(gsr_info->system_info.gsr_version >= GsrVersion{5, 0, 3}) + general_list->add_widget(create_restart_replay_on_save()); settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f))); auto checkboxes_list = std::make_unique<List>(List::Orientation::VERTICAL); @@ -704,34 +755,54 @@ namespace gsr { framerate_mode_list_ptr->set_visible(advanced_view); notifications_subsection_ptr->set_visible(advanced_view); settings_scrollable_page_ptr->reset_scroll(); + return true; }; view_radio_button_ptr->on_selection_changed("Simple", "simple"); replay_time_entry_ptr->on_changed = [this](const std::string&) { - update_estimated_file_size(); + update_estimated_replay_file_size(); + update_replay_time_text(); }; video_bitrate_entry_ptr->on_changed = [this](const std::string&) { - update_estimated_file_size(); + update_estimated_replay_file_size(); }; } - std::unique_ptr<CheckBox> SettingsPage::create_save_recording_in_game_folder(const GsrInfo &gsr_info) { + std::unique_ptr<CheckBox> SettingsPage::create_save_recording_in_game_folder() { char text[256]; - snprintf(text, sizeof(text), "Save video in a folder with the name of the game%s", gsr_info.system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)"); + snprintf(text, sizeof(text), "Save video in a folder with the name of the game%s", gsr_info->system_info.display_server == DisplayServer::X11 ? "" : " (X11 applications only)"); auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text); save_recording_in_game_folder_ptr = checkbox.get(); return checkbox; } - void SettingsPage::add_record_widgets(const GsrInfo &gsr_info) { - auto file_list = std::make_unique<List>(List::Orientation::HORIZONTAL); - file_list->add_widget(create_save_directory("Directory to save the video:")); - file_list->add_widget(create_container_section()); - settings_list_ptr->add_widget(std::make_unique<Subsection>("File info", std::move(file_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f))); + std::unique_ptr<Label> SettingsPage::create_estimated_record_file_size() { + auto label = std::make_unique<Label>(&get_theme().body_font, "Estimated video file size per minute (excluding audio): 345.60MB", get_color_theme().text_color); + estimated_file_size_ptr = label.get(); + return label; + } + + void SettingsPage::update_estimated_record_file_size() { + const int64_t video_bitrate_bps = atoi(video_bitrate_entry_ptr->get_text().c_str()) * 1000LL / 8LL; + const double video_filesize_mb_per_minute = (60.0 * (double)video_bitrate_bps) / 1000.0 / 1000.0 * 1.024; + + char buffer[512]; + snprintf(buffer, sizeof(buffer), "Estimated video file size per minute (excluding audio): %.2fMB", video_filesize_mb_per_minute); + estimated_file_size_ptr->set_text(buffer); + } + + void SettingsPage::add_record_widgets() { + auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL); + auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL); + file_info_data_list->add_widget(create_save_directory("Directory to save the video:")); + file_info_data_list->add_widget(create_container_section()); + file_info_list->add_widget(std::move(file_info_data_list)); + file_info_list->add_widget(create_estimated_record_file_size()); + settings_list_ptr->add_widget(std::make_unique<Subsection>("File info", std::move(file_info_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f))); auto general_list = std::make_unique<List>(List::Orientation::VERTICAL); - general_list->add_widget(create_save_recording_in_game_folder(gsr_info)); + general_list->add_widget(create_save_recording_in_game_folder()); settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f))); auto checkboxes_list = std::make_unique<List>(List::Orientation::VERTICAL); @@ -759,8 +830,13 @@ namespace gsr { framerate_mode_list_ptr->set_visible(advanced_view); notifications_subsection_ptr->set_visible(advanced_view); settings_scrollable_page_ptr->reset_scroll(); + return true; }; view_radio_button_ptr->on_selection_changed("Simple", "simple"); + + video_bitrate_entry_ptr->on_changed = [this](const std::string&) { + update_estimated_record_file_size(); + }; } std::unique_ptr<ComboBox> SettingsPage::create_streaming_service_box() { @@ -825,7 +901,7 @@ namespace gsr { return container_list; } - void SettingsPage::add_stream_widgets(const GsrInfo&) { + void SettingsPage::add_stream_widgets() { auto streaming_info_list = std::make_unique<List>(List::Orientation::HORIZONTAL); streaming_info_list->add_widget(create_streaming_service_section()); streaming_info_list->add_widget(create_stream_key_section()); @@ -859,6 +935,7 @@ namespace gsr { container_list_ptr->set_visible(custom_option); twitch_stream_key_entry_ptr->set_visible(twitch_option); youtube_stream_key_entry_ptr->set_visible(youtube_option); + return true; }; streaming_service_box_ptr->on_selection_changed("Twitch", "twitch"); @@ -871,6 +948,7 @@ namespace gsr { framerate_mode_list_ptr->set_visible(advanced_view); notifications_subsection_ptr->set_visible(advanced_view); settings_scrollable_page_ptr->reset_scroll(); + return true; }; view_radio_button_ptr->on_selection_changed("Simple", "simple"); } @@ -879,21 +957,22 @@ namespace gsr { save(); } - void SettingsPage::load(const GsrInfo &gsr_info) { + void SettingsPage::load() { switch(type) { case Type::REPLAY: - load_replay(gsr_info); + load_replay(); break; case Type::RECORD: - load_record(gsr_info); + load_record(); break; case Type::STREAM: - load_stream(gsr_info); + load_stream(); break; } } void SettingsPage::save() { + Config prev_config = config; switch(type) { case Type::REPLAY: save_replay(); @@ -906,6 +985,9 @@ namespace gsr { break; } save_config(config); + + if(on_config_changed && config != prev_config) + on_config_changed(); } static const std::string* get_application_audio_by_name_case_insensitive(const std::vector<std::string> &application_audio, const std::string &name) { @@ -921,11 +1003,11 @@ namespace gsr { return str.size() >= len && memcmp(str.data(), substr, len) == 0; } - void SettingsPage::load_audio_tracks(const RecordOptions &record_options, const GsrInfo &gsr_info) { + void SettingsPage::load_audio_tracks(const RecordOptions &record_options) { audio_track_list_ptr->clear(); for(const std::string &audio_track : record_options.audio_tracks) { if(starts_with(audio_track, "app:")) { - if(!gsr_info.system_info.supports_app_audio) + if(!gsr_info->system_info.supports_app_audio) continue; std::string audio_track_name = audio_track.substr(4); @@ -955,12 +1037,13 @@ namespace gsr { } } - void SettingsPage::load_common(RecordOptions &record_options, const GsrInfo &gsr_info) { + void SettingsPage::load_common(RecordOptions &record_options) { record_area_box_ptr->set_selected_item(record_options.record_area_option); - merge_audio_tracks_checkbox_ptr->set_checked(record_options.merge_audio_tracks); + if(merge_audio_tracks_checkbox_ptr) + merge_audio_tracks_checkbox_ptr->set_checked(record_options.merge_audio_tracks); application_audio_invert_checkbox_ptr->set_checked(record_options.application_audio_invert); change_video_resolution_checkbox_ptr->set_checked(record_options.change_video_resolution); - load_audio_tracks(record_options, gsr_info); + load_audio_tracks(record_options); color_range_box_ptr->set_selected_item(record_options.color_range); video_quality_box_ptr->set_selected_item(record_options.video_quality); video_codec_box_ptr->set_selected_item(record_options.video_codec); @@ -1009,23 +1092,27 @@ namespace gsr { video_bitrate_entry_ptr->set_text(std::to_string(record_options.video_bitrate)); } - void SettingsPage::load_replay(const GsrInfo &gsr_info) { - load_common(config.replay_config.record_options, gsr_info); + void SettingsPage::load_replay() { + load_common(config.replay_config.record_options); turn_on_replay_automatically_mode_ptr->set_selected_item(config.replay_config.turn_on_replay_automatically_mode); save_replay_in_game_folder_ptr->set_checked(config.replay_config.save_video_in_game_folder); + if(restart_replay_on_save) + restart_replay_on_save->set_checked(config.replay_config.restart_replay_on_save); show_replay_started_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_started_notifications); show_replay_stopped_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_stopped_notifications); show_replay_saved_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_saved_notifications); save_directory_button_ptr->set_text(config.replay_config.save_directory); container_box_ptr->set_selected_item(config.replay_config.container); - if(config.replay_config.replay_time < 5) - config.replay_config.replay_time = 5; + if(config.replay_config.replay_time < 2) + config.replay_config.replay_time = 2; + if(config.replay_config.replay_time > 10800) + config.replay_config.replay_time = 10800; replay_time_entry_ptr->set_text(std::to_string(config.replay_config.replay_time)); } - void SettingsPage::load_record(const GsrInfo &gsr_info) { - load_common(config.record_config.record_options, gsr_info); + void SettingsPage::load_record() { + load_common(config.record_config.record_options); save_recording_in_game_folder_ptr->set_checked(config.record_config.save_video_in_game_folder); show_recording_started_notification_checkbox_ptr->set_checked(config.record_config.show_recording_started_notifications); show_video_saved_notification_checkbox_ptr->set_checked(config.record_config.show_video_saved_notifications); @@ -1033,8 +1120,8 @@ namespace gsr { container_box_ptr->set_selected_item(config.record_config.container); } - void SettingsPage::load_stream(const GsrInfo &gsr_info) { - load_common(config.streaming_config.record_options, gsr_info); + void SettingsPage::load_stream() { + load_common(config.streaming_config.record_options); show_streaming_started_notification_checkbox_ptr->set_checked(config.streaming_config.show_streaming_started_notifications); show_streaming_stopped_notification_checkbox_ptr->set_checked(config.streaming_config.show_streaming_stopped_notifications); streaming_service_box_ptr->set_selected_item(config.streaming_config.streaming_service); @@ -1078,7 +1165,8 @@ namespace gsr { record_options.video_height = atoi(video_height_entry_ptr->get_text().c_str()); record_options.fps = atoi(framerate_entry_ptr->get_text().c_str()); record_options.video_bitrate = atoi(video_bitrate_entry_ptr->get_text().c_str()); - record_options.merge_audio_tracks = merge_audio_tracks_checkbox_ptr->is_checked(); + if(merge_audio_tracks_checkbox_ptr) + record_options.merge_audio_tracks = merge_audio_tracks_checkbox_ptr->is_checked(); record_options.application_audio_invert = application_audio_invert_checkbox_ptr->is_checked(); record_options.change_video_resolution = change_video_resolution_checkbox_ptr->is_checked(); save_audio_tracks(record_options.audio_tracks, audio_track_list_ptr); @@ -1140,6 +1228,8 @@ namespace gsr { save_common(config.replay_config.record_options); config.replay_config.turn_on_replay_automatically_mode = turn_on_replay_automatically_mode_ptr->get_selected_id(); config.replay_config.save_video_in_game_folder = save_replay_in_game_folder_ptr->is_checked(); + if(restart_replay_on_save) + config.replay_config.restart_replay_on_save = restart_replay_on_save->is_checked(); config.replay_config.show_replay_started_notifications = show_replay_started_notification_checkbox_ptr->is_checked(); config.replay_config.show_replay_stopped_notifications = show_replay_stopped_notification_checkbox_ptr->is_checked(); config.replay_config.show_replay_saved_notifications = show_replay_saved_notification_checkbox_ptr->is_checked(); diff --git a/src/gui/StaticPage.cpp b/src/gui/StaticPage.cpp index a89fc42..182464c 100644 --- a/src/gui/StaticPage.cpp +++ b/src/gui/StaticPage.cpp @@ -36,14 +36,8 @@ namespace gsr { offset = draw_pos; Widget *selected_widget = selected_child_widget; - mgl_scissor prev_scissor; - mgl_window_get_scissor(window.internal_window(), &prev_scissor); - - mgl_scissor new_scissor = { - mgl_vec2i{(int)draw_pos.x, (int)draw_pos.y}, - mgl_vec2i{(int)size.x, (int)size.y} - }; - mgl_window_set_scissor(window.internal_window(), &new_scissor); + const mgl::Scissor prev_scissor = window.get_scissor(); + window.set_scissor({draw_pos.to_vec2i(), size.to_vec2i()}); for(size_t i = 0; i < widgets.size(); ++i) { auto &widget = widgets[i]; @@ -54,7 +48,7 @@ namespace gsr { if(selected_widget) selected_widget->draw(window, offset); - mgl_window_set_scissor(window.internal_window(), &prev_scissor); + window.set_scissor(prev_scissor); } mgl::vec2f StaticPage::get_size() { diff --git a/src/gui/Utils.cpp b/src/gui/Utils.cpp index e000b7a..d1643f2 100644 --- a/src/gui/Utils.cpp +++ b/src/gui/Utils.cpp @@ -50,4 +50,21 @@ namespace gsr { void set_frame_delta_seconds(double frame_delta) { frame_delta_seconds = frame_delta; } + + mgl::vec2f scale_keep_aspect_ratio(mgl::vec2f from, mgl::vec2f to) { + if(std::abs(from.x) <= 0.0001f || std::abs(from.y) <= 0.0001f) + return {0.0f, 0.0f}; + + const double height_to_width_ratio = (double)from.y / (double)from.x; + from.x = to.x; + from.y = from.x * height_to_width_ratio; + + if(from.y > to.y) { + const double width_height_ratio = (double)from.x / (double)from.y; + from.y = to.y; + from.x = from.y * width_height_ratio; + } + + return from; + } }
\ No newline at end of file |