Change text renderer to pango (supports all languages and loads font automatically), start on game detection

This commit is contained in:
dec05eba
2026-04-18 00:37:16 +02:00
parent 72c8c79896
commit 010787854e
39 changed files with 636 additions and 886 deletions

View File

@@ -31,7 +31,8 @@ These are the dependencies needed to build GPU Screen Recorder UI:
* libdrm
* libdbus
* wayland (wayland-client, wayland-egl, wayland-scanner)
* setcap (libcap)
* libcap
* libpango (pangoft2)
## Runtime dependencies
There are also additional dependencies needed at runtime:
@@ -52,7 +53,7 @@ as gpu screen recorder tries to grab keys and keyd grabs gpu screen recorder, le
If you are stuck in such a lock where you cant press and keyboard keys you can press (left) ctrl+shift+alt+esc to close gpu screen recorder and remove it from system startup.
# License
This software is licensed under GPL-3.0-only, see the LICENSE file for more information. Files under `fonts/` directory belong to the Noto Sans Google fonts project and they are licensed under `SIL Open Font License`.\
This software is licensed under GPL-3.0-only, see the LICENSE file for more information.
`images/default.cur` it part of the [Adwaita icon theme](https://gitlab.gnome.org/GNOME/adwaita-icon-theme/-/tree/master) which is licensed under `CC BY-SA 3.0`.\
The controller buttons under `images/` were created by [Julio Cacko](https://juliocacko.itch.io/free-input-prompts) and they are licensed under `CC0 1.0 Universal`.\
The PlayStation logo under `images/` was created by [ArksDigital](https://arks.itch.io/ps4-buttons) and it's licensed under `CC BY 4.0`.

8
TODO
View File

@@ -129,8 +129,6 @@ If CursorTrackerWayland fails then fallback to getting focused monitor by window
Maybe automatically switch to recording with the device that controls the monitor.
In that case also add all monitors available to capture in the capture list and automatically choose the gpu that controls the monitor.
Support cjk font. Use fc-match to find the location of the font. This also works in flatpak, in which case the fonts are in /run/host/..., where it lists system fonts.
Keyboard layout is incorrect on wayland when using kde plasma keyboard settings to setup multiple keyboards, for example when changing to french.
Text input is correct, but hotkey is incorrect.
Need to use "setxkbmap fr" as well.
@@ -164,8 +162,6 @@ Show message that replay/streaming has to be restarted if recording settings are
Support vector graphics. Maybe support svg, rendering it to a texture for better performance.
Support freetype for text rendering. Maybe load freetype as runtime (with dlopen) and use that when available and fallback to stb_freetype if not available.
Show .webm container option. It's currently chosen automatically if vp8/vp9 is chosen. The available containers should automatically switch depending on the video codec.
In settings show audio levels for each audio. Maybe show audio level image beside the audio name in the dropdown box and switch to a different image (have 3-4 different images for each level) depending on the volume.
@@ -260,3 +256,7 @@ Add simple video cutting in the ui.
Keep replay turned on when opening a fullscreen application and alt-tabbing. Only stop replay when closing the application (when closing all fullscreen applications).
When alt-tabbing check if the previously fullscreen window still exists.
Show vulkan codec options.
Use game information to save video under the game title if available, fallback to window title.

Binary file not shown.

Binary file not shown.

View File

@@ -114,7 +114,7 @@ namespace gsr {
void handle_keyboard_mapping_event();
void on_event(mgl::Event &event);
void recreate_global_hotkeys(const char *hotkey_option);
void recreate_global_hotkeys(std::string_view hotkey_option);
void update_led_indicator_after_settings_change();
void recreate_frontpage_ui_components();
void xi_setup();
@@ -255,6 +255,11 @@ namespace gsr {
XEvent x11_xev;
Atom net_active_window_atom;
bool update_focused_window = true;
std::vector<Window> game_windows;
double event_current_time_seconds = 0.0;
double gamescope_running_last_checked_seconds = 0.0;
bool is_gamescope_running = false;
bool is_game_running = false;
struct wl_display *wayland_dpy = nullptr;

View File

@@ -2,7 +2,6 @@
#include <mglpp/system/MemoryMappedFile.hpp>
#include <mglpp/graphics/Color.hpp>
#include <mglpp/graphics/Font.hpp>
#include <mglpp/graphics/Texture.hpp>
#include <string>
@@ -19,12 +18,10 @@ namespace gsr {
float window_width = 0.0f;
float window_height = 0.0f;
mgl::MemoryMappedFile body_font_file;
mgl::MemoryMappedFile title_font_file;
mgl::Font body_font;
mgl::Font title_font;
mgl::Font top_bar_font;
mgl::Font camera_setup_font;
std::string body_font_desc;
std::string title_font_desc;
std::string top_bar_font_desc;
std::string camera_setup_font_desc;
mgl::Texture combobox_arrow_texture;
mgl::Texture settings_texture;

View File

@@ -8,8 +8,8 @@ namespace gsr {
public:
static Translation& instance();
void init(const char* translations_directory, const char* initial_language = nullptr);
bool load_language(const char* lang);
bool is_language_supported(const char* lang);
bool load_language(std::string_view lang);
bool is_language_supported(std::string_view lang);
bool plural_numbers_are_complex();
const char* translate(const char* key);

View File

@@ -12,7 +12,7 @@ namespace gsr {
public:
// If width is 0 then the width of the text is used instead (with padding).
// If height is 0 then the height of the text is used instead (with padding).
Button(mgl::Font *font, const char *text, mgl::vec2f size, mgl::Color bg_color);
Button(const char *font_desc, const char *text, mgl::vec2f size, mgl::Color bg_color);
Button(const Button&) = delete;
Button& operator=(const Button&) = delete;
@@ -25,8 +25,8 @@ namespace gsr {
void set_bg_hover_color(mgl::Color color);
void set_icon(mgl::Texture *texture);
const std::string& get_text() const;
void set_text(std::string str);
std::string_view get_text() const;
void set_text(std::string_view str);
std::function<void()> on_click;
private:

View File

@@ -11,7 +11,7 @@
namespace gsr {
class CheckBox : public Widget {
public:
CheckBox(mgl::Font *font, const char *text);
CheckBox(const char *font_desc, const char *text);
CheckBox(const CheckBox&) = delete;
CheckBox& operator=(const CheckBox&) = delete;

View File

@@ -11,24 +11,24 @@
namespace gsr {
class ComboBox : public Widget {
public:
ComboBox(mgl::Font *font);
ComboBox(const char *font_desc);
ComboBox(const ComboBox&) = delete;
ComboBox& operator=(const ComboBox&) = delete;
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
void draw(mgl::Window &window, mgl::vec2f offset) override;
void add_item(const std::string &text, const std::string &id, bool allow_duplicate = true);
void add_item(std::string_view text, const std::string &id, bool allow_duplicate = true);
void clear_items();
// The item can only be selected if it's enabled
void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
void set_item_enabled(const std::string &id, bool enabled);
const std::string& get_selected_id() const;
void set_selected_item(std::string_view id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
void set_item_enabled(std::string_view id, bool enabled);
std::string_view get_selected_id() const;
mgl::vec2f get_size() override;
std::function<void(const std::string &text, const std::string &id)> on_selection_changed;
std::function<void(std::string_view text, std::string_view id)> on_selection_changed;
private:
void draw_selected(mgl::Window &window, mgl::vec2f draw_pos);
void draw_unselected(mgl::Window &window, mgl::vec2f draw_pos);
@@ -44,7 +44,8 @@ namespace gsr {
};
mgl::vec2f max_size;
mgl::Font *font;
std::string font_desc;
int font_size = 0;
std::vector<Item> items;
mgl::Sprite dropdown_arrow;
bool dirty = true;

View File

@@ -10,7 +10,7 @@
namespace gsr {
class DropdownButton : public Widget {
public:
DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size);
DropdownButton(const char *title_font_desc, const char *description_font_desc, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size);
DropdownButton(const DropdownButton&) = delete;
DropdownButton& operator=(const DropdownButton&) = delete;
@@ -18,10 +18,10 @@ namespace gsr {
void draw(mgl::Window &window, mgl::vec2f offset) override;
void add_item(const std::string &text, const std::string &id, const std::string &description = "");
void set_item_label(const std::string &id, const std::string &new_label);
void set_item_icon(const std::string &id, mgl::Texture *texture);
void set_item_description(const std::string &id, const std::string &new_description);
void set_item_enabled(const std::string &id, bool enabled);
void set_item_label(std::string_view id, const std::string &new_label);
void set_item_icon(std::string_view id, mgl::Texture *texture);
void set_item_description(std::string_view id, const std::string &new_description);
void set_item_enabled(std::string_view id, bool enabled);
void set_description(std::string description_text);
void set_activated(bool activated);
@@ -41,8 +41,8 @@ namespace gsr {
};
std::vector<Item> items;
mgl::Font *title_font;
mgl::Font *description_font;
std::string title_font_desc;
std::string description_font_desc;
mgl::vec2f size;
bool mouse_inside = false;
bool show_dropdown = false;

View File

@@ -4,32 +4,16 @@
#include <functional>
#include <mglpp/graphics/Color.hpp>
#include <mglpp/graphics/Text32.hpp>
#include <mglpp/graphics/TextEdit.hpp>
#include <mglpp/graphics/Text.hpp>
#include <mglpp/graphics/Rectangle.hpp>
namespace gsr {
class Entry;
enum class EntryValidateHandlerResult {
DENY,
ALLOW,
REPLACED
};
using EntryValidateHandler = std::function<EntryValidateHandlerResult(Entry &entry, const std::u32string &str)>;
struct CaretIndexPos {
int index;
mgl::vec2f pos;
};
class Entry : public Widget {
public:
enum class Direction {
LEFT,
RIGHT
};
Entry(mgl::Font *font, const char *text, float max_width);
Entry(const char *font_desc, const char *text, float max_width);
Entry(const Entry&) = delete;
Entry& operator=(const Entry&) = delete;
@@ -38,44 +22,18 @@ namespace gsr {
mgl::vec2f get_size() override;
EntryValidateHandlerResult set_text(const std::string &str);
std::string get_text() const;
void set_text(std::string_view str);
std::string_view get_text() const;
void set_masked(bool masked);
bool is_masked() const;
// Return false to specify that the string should not be accepted. This reverts the string back to its previous value.
// The input can be changed by changing the input parameter and returning true.
EntryValidateHandler validate_handler;
void set_number_mode(bool enabled, int min_val, int max_val);
std::function<void(const std::string &text)> on_changed;
std::function<void(std::string_view text)> on_changed;
private:
// Also updates the cursor position
void replace_text(size_t index, size_t size, const std::u32string &replacement);
void move_caret_word(Direction direction, size_t max_codepoints);
EntryValidateHandlerResult set_text_internal(std::u32string str);
void draw_caret(mgl::Window &window, mgl::vec2f draw_pos, mgl::vec2f caret_size);
void draw_caret_selection(mgl::Window &window, mgl::vec2f draw_pos, mgl::vec2f caret_size);
CaretIndexPos find_closest_caret_index_by_position(mgl::vec2f position);
private:
struct Caret {
float offset_x = 0.0f;
int index = 0;
};
mgl::Rectangle background;
mgl::Text32 text;
mgl::Text32 masked_text;
float max_width;
bool selected = false;
bool selecting_text = false;
bool selecting_with_keyboard = false;
bool show_selection = false;
bool masked = false;
Caret caret;
Caret selection_start_caret;
float text_overflow = 0.0f;
mgl::TextEdit text_edit;
float max_width = 0.0f;
};
EntryValidateHandler create_entry_validator_integer_in_range(int min, int max);
}

View File

@@ -29,7 +29,7 @@ namespace gsr {
mgl::vec2f get_inner_size() override;
void set_size(mgl::vec2f size);
private:
void set_current_directory(const char *directory);
void set_current_directory(std::string_view directory);
private:
struct Folder {
mgl::Text text;
@@ -48,7 +48,7 @@ namespace gsr {
class FileChooser : public Widget {
public:
FileChooser(const char *start_directory, mgl::vec2f size);
FileChooser(std::string_view start_directory, mgl::vec2f size);
FileChooser(const FileChooser&) = delete;
FileChooser& operator=(const FileChooser&) = delete;
@@ -58,10 +58,10 @@ namespace gsr {
mgl::vec2f get_size() override;
void set_current_directory(const char *directory);
void open_subdirectory(const char *name);
void set_current_directory(std::string_view directory);
void open_subdirectory(std::string_view name);
void open_parent_directory();
const std::string& get_current_directory() const;
std::string_view get_current_directory() const;
private:
struct Folder {
mgl::Text text;

View File

@@ -49,9 +49,9 @@ namespace gsr {
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
std::function<void(bool enable, int exit_status)> on_startup_changed;
std::function<void(const char *reason)> on_click_exit_program_button;
std::function<void(const char *hotkey_option)> on_keyboard_hotkey_changed;
std::function<void(const char *hotkey_option)> on_joystick_hotkey_changed;
std::function<void(std::string_view reason)> on_click_exit_program_button;
std::function<void(std::string_view hotkey_option)> on_keyboard_hotkey_changed;
std::function<void(std::string_view hotkey_option)> on_joystick_hotkey_changed;
std::function<void()> on_page_closed;
private:
void load_hotkeys();

View File

@@ -8,8 +8,7 @@
namespace gsr {
class Label : public Widget {
public:
// TODO: Allow specifying max width, at which either a line-break should occur or elipses should show
Label(mgl::Font *font, const char *text, mgl::Color color);
Label(const char *font_desc, const char *text, mgl::Color color);
Label(const Label&) = delete;
Label& operator=(const Label&) = delete;
@@ -18,8 +17,14 @@ namespace gsr {
mgl::vec2f get_size() override;
void set_text(std::string str);
const std::string& get_text() const;
void set_text(std::string_view str);
std::string_view get_text() const;
// Set to 0 to disable
void set_wrap_width(int width);
// Set to 0 to disable
void set_max_rows(int max_rows);
private:
mgl::Text text;
};

View File

@@ -14,7 +14,7 @@ namespace gsr {
HORIZONTAL
};
RadioButton(mgl::Font *font, Orientation orientation);
RadioButton(const char *font_desc, Orientation orientation);
RadioButton(const RadioButton&) = delete;
RadioButton& operator=(const RadioButton&) = delete;
@@ -22,14 +22,14 @@ 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, bool trigger_event_even_if_selection_not_changed = true);
const std::string& get_selected_id() const;
const std::string& get_selected_text() const;
void set_selected_item(std::string_view id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
std::string_view get_selected_id() const;
std::string_view get_selected_text() const;
mgl::vec2f get_size() override;
// Return false to revert the change
std::function<bool(const std::string &text, const std::string &id)> on_selection_changed;
std::function<bool(std::string_view text, std::string_view id)> on_selection_changed;
private:
void update_if_dirty();
private:
@@ -38,7 +38,7 @@ namespace gsr {
std::string id;
};
mgl::Font *font;
std::string font_desc;
Orientation orientation;
std::vector<Item> items;
size_t selected_item = 0;

View File

@@ -125,7 +125,7 @@ namespace gsr {
std::unique_ptr<CheckBox> create_save_replay_in_game_folder();
std::unique_ptr<CheckBox> create_restart_replay_on_save();
std::unique_ptr<Label> create_estimated_replay_file_size();
void update_estimated_replay_file_size(const std::string &replay_storage_type);
void update_estimated_replay_file_size(std::string_view replay_storage_type);
void update_replay_time_text();
std::unique_ptr<CheckBox> create_save_recording_in_game_folder();
std::unique_ptr<Label> create_estimated_record_file_size();
@@ -140,6 +140,9 @@ namespace gsr {
std::unique_ptr<ComboBox> create_streaming_service_box();
std::unique_ptr<List> create_streaming_service_section();
std::unique_ptr<List> create_stream_key_section();
std::unique_ptr<List> create_stream_kick_url();
std::unique_ptr<List> create_stream_kick_key();
std::unique_ptr<List> create_stream_kick_section();
std::unique_ptr<List> create_stream_custom_url();
std::unique_ptr<List> create_stream_custom_key();
std::unique_ptr<List> create_stream_custom_section();
@@ -200,6 +203,7 @@ namespace gsr {
ComboBox *streaming_service_box_ptr = nullptr;
List *stream_key_list_ptr = nullptr;
List *custom_stream_list_ptr = nullptr;
List *kick_stream_list_ptr = nullptr;
CheckBox *save_replay_in_game_folder_ptr = nullptr;
CheckBox *restart_replay_on_save = nullptr;
Label *estimated_file_size_ptr = nullptr;

View File

@@ -6,7 +6,7 @@
namespace gsr {
class Tooltip : public Widget {
public:
Tooltip(mgl::Font *font);
Tooltip(const char *font_desc);
Tooltip(const Tooltip&) = delete;
Tooltip& operator=(const Tooltip&) = delete;
@@ -15,7 +15,7 @@ namespace gsr {
mgl::vec2f get_size() override;
void set_text(std::string text);
void set_text(std::string_view text);
private:
mgl::Text label;
};

View File

@@ -141,7 +141,6 @@ executable(
)
install_subdir('images', install_dir : gsr_ui_resources_path)
install_subdir('fonts', install_dir : gsr_ui_resources_path)
install_subdir('translations', install_dir : gsr_ui_resources_path)
if get_option('desktop-files') == true

View File

@@ -39,13 +39,13 @@ namespace gsr {
static uint32_t mgl_key_modifiers_to_x11_modifier_mask(const mgl::Event::KeyEvent &key_event) {
uint32_t mask = 0;
if(key_event.shift)
if(key_event.key_states.shift)
mask |= ShiftMask;
if(key_event.control)
if(key_event.key_states.control)
mask |= ControlMask;
if(key_event.alt)
if(key_event.key_states.alt)
mask |= Mod1Mask;
if(key_event.system)
if(key_event.key_states.system)
mask |= Mod4Mask;
return mask;
}

View File

@@ -466,6 +466,14 @@ namespace gsr {
}
}
static double clock_get_monotonic_seconds(void) {
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 0;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + (double)ts.tv_nsec * 0.000000001;
}
Overlay::Overlay(std::string resources_path, GsrInfo gsr_info, SupportedCaptureOptions capture_options, egl_functions egl_funcs) :
resources_path(std::move(resources_path)),
gsr_info(std::move(gsr_info)),
@@ -476,6 +484,9 @@ namespace gsr {
top_bar_background({0.0f, 0.0f}),
close_button_widget({0.0f, 0.0f})
{
event_current_time_seconds = clock_get_monotonic_seconds();
gamescope_running_last_checked_seconds = event_current_time_seconds - 10.0;
if(this->gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
wayland_dpy = wl_display_connect(nullptr);
if(!wayland_dpy)
@@ -487,10 +498,10 @@ namespace gsr {
gsr_icon_path = this->resources_path + "images/gpu_screen_recorder_logo.png";
key_bindings[0].key_event.code = mgl::Keyboard::Escape;
key_bindings[0].key_event.alt = false;
key_bindings[0].key_event.control = false;
key_bindings[0].key_event.shift = false;
key_bindings[0].key_event.system = false;
key_bindings[0].key_event.key_states.alt = false;
key_bindings[0].key_event.key_states.control = false;
key_bindings[0].key_event.key_states.shift = false;
key_bindings[0].key_event.key_states.system = false;
key_bindings[0].callback = [this]() {
page_stack.pop();
};
@@ -712,10 +723,10 @@ namespace gsr {
}
static uint32_t key_event_to_bitmask(mgl::Event::KeyEvent key_event) {
return ((uint32_t)key_event.alt << (uint32_t)0)
| ((uint32_t)key_event.control << (uint32_t)1)
| ((uint32_t)key_event.shift << (uint32_t)2)
| ((uint32_t)key_event.system << (uint32_t)3);
return ((uint32_t)key_event.key_states.alt << (uint32_t)0)
| ((uint32_t)key_event.key_states.control << (uint32_t)1)
| ((uint32_t)key_event.key_states.shift << (uint32_t)2)
| ((uint32_t)key_event.key_states.system << (uint32_t)3);
}
void Overlay::process_key_bindings(mgl::Event &event) {
@@ -729,6 +740,64 @@ namespace gsr {
}
}
static bool x11_window_is_steam_game(Display *dpy, Window window) {
unsigned long steam_game_id = 0;
unsigned int property_size = 0;
unsigned char* steam_game_property = window_get_property(dpy, window, XA_CARDINAL, "STEAM_GAME", &property_size);
if(steam_game_property) {
if(property_size == 8)
steam_game_id = *(unsigned long*)steam_game_property;
XFree(steam_game_property);
}
const unsigned long steam_webhelper_game_id = 769;
return steam_game_id != 0 && steam_game_id != steam_webhelper_game_id;
}
// WINE/Godot/Unity application detection
static bool x11_window_class_is_game(Display *dpy, Window window) {
XClassHint class_hint = {nullptr, nullptr};
XGetClassHint(dpy, window, &class_hint);
const bool is_godot_application = class_hint.res_name && strcmp(class_hint.res_name, "Godot_Engine") == 0;
const bool is_wine_application = class_hint.res_class && (ends_with(class_hint.res_class, ".exe") || ends_with(class_hint.res_class, ".EXE"));
const bool is_native_unity_32bit_application = class_hint.res_class && ends_with(class_hint.res_class, ".x86");
const bool is_native_unity_64bit_application = class_hint.res_class && (ends_with(class_hint.res_class, ".x86_64") || ends_with(class_hint.res_class, ".x64"));
if(class_hint.res_name)
XFree(class_hint.res_name);
if(class_hint.res_class)
XFree(class_hint.res_class);
return is_godot_application || is_wine_application || is_native_unity_32bit_application || is_native_unity_64bit_application;
}
static bool x11_window_is_game(Display *dpy, Window window) {
return x11_window_is_steam_game(dpy, window) || x11_window_class_is_game(dpy, window);
}
static bool x11_is_server_gamescope(Display *dpy) {
bool is_gamescope = false;
unsigned int property_size = 0;
unsigned char* gamescope_focused_window = window_get_property(dpy, DefaultRootWindow(dpy), XA_CARDINAL, "GAMESCOPE_FOCUSED_WINDOW", &property_size);
if(gamescope_focused_window) {
is_gamescope = true;
XFree(gamescope_focused_window);
}
return is_gamescope;
}
static bool is_gamescope_x11_server_running() {
bool is_gamescope = false;
Display *gamescope_x11_dpy = XOpenDisplay(":2");
if(gamescope_x11_dpy) {
is_gamescope = x11_is_server_gamescope(gamescope_x11_dpy);
XCloseDisplay(gamescope_x11_dpy);
}
return is_gamescope;
}
void Overlay::handle_keyboard_mapping_event() {
if(!x11_dpy)
return;
@@ -743,9 +812,14 @@ namespace gsr {
break;
}
case PropertyNotify: {
if(x11_xev.xproperty.state == PropertyNewValue && x11_xev.xproperty.atom == net_active_window_atom) {
if(x11_xev.xproperty.state == PropertyNewValue && x11_xev.xproperty.atom == net_active_window_atom)
update_focused_window = true;
}
break;
}
case DestroyNotify: {
auto it = std::find(game_windows.begin(), game_windows.end(), x11_xev.xdestroywindow.window);
if(it != game_windows.end())
game_windows.erase(it);
break;
}
}
@@ -756,10 +830,35 @@ namespace gsr {
if(update_focused_window) {
update_focused_window = false;
const Window focused_window = get_focused_window(x11_dpy, WindowCaptureType::FOCUSED, false);
if(x11_window_is_game(x11_dpy, focused_window)) {
if(std::find(game_windows.begin(), game_windows.end(), focused_window) == game_windows.end()) {
XSelectInput(x11_dpy, focused_window, StructureNotifyMask);
game_windows.push_back(focused_window);
}
}
}
if(event_current_time_seconds - gamescope_running_last_checked_seconds >= 3.0) {
gamescope_running_last_checked_seconds = event_current_time_seconds;
is_gamescope_running = is_gamescope_x11_server_running();
}
const bool prev_game_is_running = is_game_running;
is_game_running = !game_windows.empty() || is_gamescope_running;
if(is_game_running != prev_game_is_running) {
if(is_game_running) {
//fprintf(stderr, "started game\n");
} else {
//fprintf(stderr, "stopped game\n");
}
}
}
void Overlay::handle_events() {
event_current_time_seconds = clock_get_monotonic_seconds();
if(led_indicator)
led_indicator->update();
@@ -788,6 +887,7 @@ namespace gsr {
}
handle_keyboard_mapping_event();
region_selector.poll_events();
if(region_selector.take_canceled()) {
on_region_selected = nullptr;
@@ -1189,15 +1289,15 @@ namespace gsr {
draw();
}
void Overlay::recreate_global_hotkeys(const char *hotkey_option) {
void Overlay::recreate_global_hotkeys(std::string_view hotkey_option) {
global_hotkeys.reset();
if(strcmp(hotkey_option, "enable_hotkeys") == 0)
if(hotkey_option == "enable_hotkeys")
global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::ALL);
else if(strcmp(hotkey_option, "enable_hotkeys_virtual_devices") == 0)
else if(hotkey_option == "enable_hotkeys_virtual_devices")
global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::VIRTUAL);
else if(strcmp(hotkey_option, "enable_hotkeys_no_grab") == 0)
else if(hotkey_option == "enable_hotkeys_no_grab")
global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::NO_GRAB);
else if(strcmp(hotkey_option, "disable_hotkeys") == 0)
else if(hotkey_option == "disable_hotkeys")
global_hotkeys.reset();
}
@@ -1213,7 +1313,7 @@ namespace gsr {
void Overlay::recreate_frontpage_ui_components() {
bg_screenshot_overlay = mgl::Rectangle(mgl::vec2f(get_theme().window_width, get_theme().window_height));
top_bar_background = mgl::Rectangle(mgl::vec2f(get_theme().window_width, get_theme().window_height*0.06f).floor());
top_bar_text = mgl::Text("GPU Screen Recorder", get_theme().top_bar_font);
top_bar_text = mgl::Text("GPU Screen Recorder", get_theme().top_bar_font_desc.c_str());
logo_sprite = mgl::Sprite(&get_theme().logo_texture);
close_button_widget.set_size(mgl::vec2f(top_bar_background.get_size().y * 0.35f, top_bar_background.get_size().y * 0.35f).floor());
@@ -1245,7 +1345,7 @@ namespace gsr {
List * main_buttons_list_ptr = main_buttons_list.get();
main_buttons_list->set_spacing(0.0f);
{
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, TR("Instant Replay"), TR("Off"), &get_theme().replay_button_texture,
auto button = std::make_unique<DropdownButton>(get_theme().title_font_desc.c_str(), get_theme().body_font_desc.c_str(), TR("Instant Replay"), TR("Off"), &get_theme().replay_button_texture,
mgl::vec2f(button_width, button_height));
replay_dropdown_button_ptr = button.get();
button->add_item(TR("Turn on"), "start", config.replay_config.start_stop_hotkey.to_string(false, false));
@@ -1285,7 +1385,7 @@ namespace gsr {
main_buttons_list->add_widget(std::move(button));
}
{
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, TR("Record"), TR("Not recording"), &get_theme().record_button_texture,
auto button = std::make_unique<DropdownButton>(get_theme().title_font_desc.c_str(), get_theme().body_font_desc.c_str(), TR("Record"), TR("Not recording"), &get_theme().record_button_texture,
mgl::vec2f(button_width, button_height));
record_dropdown_button_ptr = button.get();
button->add_item(TR("Start"), "start", config.record_config.start_stop_hotkey.to_string(false, false));
@@ -1314,7 +1414,7 @@ namespace gsr {
main_buttons_list->add_widget(std::move(button));
}
{
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, TR("Livestream"), TR("Not streaming"), &get_theme().stream_button_texture,
auto button = std::make_unique<DropdownButton>(get_theme().title_font_desc.c_str(), get_theme().body_font_desc.c_str(), TR("Livestream"), TR("Not streaming"), &get_theme().stream_button_texture,
mgl::vec2f(button_width, button_height));
stream_dropdown_button_ptr = button.get();
button->add_item(TR("Start"), "start", config.streaming_config.start_stop_hotkey.to_string(false, false));
@@ -1345,7 +1445,7 @@ namespace gsr {
{
const mgl::vec2f main_buttons_size = main_buttons_list_ptr->get_size();
const int settings_button_size = main_buttons_size.y * 0.33f;
auto button = std::make_unique<Button>(&get_theme().title_font, "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
auto button = std::make_unique<Button>(get_theme().title_font_desc.c_str(), "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
button->set_position((main_buttons_list_ptr->get_position() + main_buttons_size - mgl::vec2f(0.0f, settings_button_size) + mgl::vec2f(settings_button_size * 0.333f, 0.0f)).floor());
button->set_bg_hover_color(mgl::Color(0, 0, 0, 255));
button->set_icon(&get_theme().settings_small_texture);
@@ -1378,20 +1478,20 @@ namespace gsr {
show_notification(TR("Failed to remove GPU Screen Recorder from system startup"), notification_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
};
settings_page->on_click_exit_program_button = [this](const char *reason) {
settings_page->on_click_exit_program_button = [this](std::string_view reason) {
do_exit = true;
exit_reason = reason;
};
settings_page->on_keyboard_hotkey_changed = [this](const char *hotkey_option) {
settings_page->on_keyboard_hotkey_changed = [this](std::string_view hotkey_option) {
recreate_global_hotkeys(hotkey_option);
};
settings_page->on_joystick_hotkey_changed = [this](const char *hotkey_option) {
settings_page->on_joystick_hotkey_changed = [this](std::string_view hotkey_option) {
global_hotkeys_js.reset();
if(strcmp(hotkey_option, "enable_hotkeys") == 0)
if(hotkey_option == "enable_hotkeys")
global_hotkeys_js = register_joystick_hotkeys(this);
else if(strcmp(hotkey_option, "disable_hotkeys") == 0)
else if(hotkey_option == "disable_hotkeys")
global_hotkeys_js.reset();
};
@@ -1418,7 +1518,7 @@ namespace gsr {
{
const mgl::vec2f main_buttons_size = main_buttons_list_ptr->get_size();
const int settings_button_size = main_buttons_size.y * 0.33f;
auto button = std::make_unique<Button>(&get_theme().title_font, "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
auto button = std::make_unique<Button>(get_theme().title_font_desc.c_str(), "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
button->set_position((main_buttons_list_ptr->get_position() + main_buttons_size - mgl::vec2f(0.0f, settings_button_size*2) + mgl::vec2f(settings_button_size * 0.333f, 0.0f)).floor());
button->set_bg_hover_color(mgl::Color(0, 0, 0, 255));
button->set_icon(&get_theme().screenshot_texture);

View File

@@ -39,17 +39,10 @@ namespace gsr {
window_width = window_size.x;
window_height = window_size.y;
if(!theme->title_font.load_from_file(theme->title_font_file, std::max(16.0f, window_size.y * 0.019f)))
return false;
if(!theme->top_bar_font.load_from_file(theme->title_font_file, std::max(23.0f, window_size.y * 0.03f)))
return false;
if(!theme->body_font.load_from_file(theme->body_font_file, std::max(13.0f, window_size.y * 0.015f)))
return false;
if(!theme->camera_setup_font.load_from_file(theme->body_font_file, 24))
return false;
theme->title_font_desc = std::string("Noto Sans Bold ") + std::to_string((int)(std::max(16.0f, window_size.y * 0.019f)/1.8));
theme->top_bar_font_desc = std::string("Noto Sans Bold ") + std::to_string((int)(std::max(23.0f, window_size.y * 0.03f)/1.8));
theme->body_font_desc = std::string("Noto Sans ") + std::to_string((int)(std::max(13.0f, window_size.y * 0.015f)/1.8));
theme->camera_setup_font_desc = "Noto Sans 14";
return true;
}
@@ -60,12 +53,6 @@ namespace gsr {
theme = new Theme();
if(!theme->body_font_file.load((resources_path + "fonts/NotoSans-Regular.ttf").c_str(), mgl::MemoryMappedFile::LoadOptions{true, false}))
goto error;
if(!theme->title_font_file.load((resources_path + "fonts/NotoSans-Bold.ttf").c_str(), mgl::MemoryMappedFile::LoadOptions{true, false}))
goto error;
if(!theme->combobox_arrow_texture.load_from_file((resources_path + "images/combobox_arrow.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error;

View File

@@ -42,13 +42,13 @@ namespace gsr {
}
}
bool Translation::is_language_supported(const char* lang) {
if(strcmp(lang, "en") == 0)
bool Translation::is_language_supported(std::string_view lang) {
if(lang == "en")
return true;
std::string paths[] = {
std::string("translations/") + lang + ".txt",
std::string(this->translations_directory) + lang + ".txt"
std::string("translations/") + std::string(lang) + ".txt",
std::string(this->translations_directory) + std::string(lang) + ".txt"
};
for (const auto& path : paths) {
@@ -61,27 +61,27 @@ namespace gsr {
return false;
}
bool Translation::load_language(const char* lang) {
bool Translation::load_language(std::string_view lang) {
translations.clear();
const std::string system_language = get_system_language();
if(!lang || lang[0] == '\0')
lang = system_language.c_str();
if(lang.empty())
lang = system_language;
if (!is_language_supported(lang)) {
fprintf(stderr, "Warning: language '%s' is not supported\n", lang);
fprintf(stderr, "Warning: language '%.*s' is not supported\n", (int)lang.size(), lang.data());
return false;
}
current_language = lang;
if (strcmp(lang, "en") == 0) {
if (lang == "en") {
return true; // english is the base
}
std::string paths[] = {
std::string("translations/") + lang + ".txt",
std::string(this->translations_directory) + lang + ".txt"
std::string("translations/") + std::string(lang) + ".txt",
std::string(this->translations_directory) + std::string(lang) + ".txt"
};
for (const auto& path : paths) {
@@ -105,11 +105,11 @@ namespace gsr {
translations[key] = value;
}
fprintf(stderr, "Info: Loaded translation file for '%s' from %s\n", lang, path.c_str());
fprintf(stderr, "Info: Loaded translation file for '%.*s' from %s\n", (int)lang.size(), lang.data(), path.c_str());
return true;
}
fprintf(stderr, "Warning: translation file for '%s' not found\n", lang);
fprintf(stderr, "Warning: translation file for '%.*s' not found\n", (int)lang.size(), lang.data());
return false;
}

View File

@@ -18,8 +18,8 @@ namespace gsr {
//static const float padding_left_icon_scale = 0.25f;
static const float padding_right_icon_scale = 0.15f;
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)
Button::Button(const char *font_desc, const char *text, mgl::vec2f size, mgl::Color bg_color) :
size(size), bg_color(bg_color), bg_hover_color(bg_color), text(text, font_desc)
{
}
@@ -117,12 +117,12 @@ namespace gsr {
sprite.set_texture(texture);
}
const std::string& Button::get_text() const {
std::string_view Button::get_text() const {
return text.get_string();
}
void Button::set_text(std::string str) {
text.set_string(std::move(str));
void Button::set_text(std::string_view str) {
text.set_string(str);
}
void Button::scale_sprite_to_button_size() {

View File

@@ -27,8 +27,8 @@ namespace gsr {
return color;
}
CheckBox::CheckBox(mgl::Font *font, const char *text) :
text(text, *font),
CheckBox::CheckBox(const char *font_desc, const char *text) :
text(text, font_desc),
background_sprite(&get_theme().checkbox_background_texture),
circle_sprite(&get_theme().checkbox_circle_texture)
{

View File

@@ -2,7 +2,6 @@
#include "../../include/gui/Utils.hpp"
#include "../../include/Theme.hpp"
#include <mglpp/graphics/Rectangle.hpp>
#include <mglpp/graphics/Font.hpp>
#include <mglpp/window/Window.hpp>
#include <mglpp/window/Event.hpp>
#include <assert.h>
@@ -14,8 +13,9 @@ namespace gsr {
static const float padding_right_scale = 0.007f;
static const float border_scale = 0.0015f;
ComboBox::ComboBox(mgl::Font *font) : font(font), dropdown_arrow(&get_theme().combobox_arrow_texture) {
assert(font);
ComboBox::ComboBox(const char *font_desc) : font_desc(font_desc), dropdown_arrow(&get_theme().combobox_arrow_texture) {
assert(font_desc);
font_size = mgl::Text::get_font_size_from_font_description(font_desc);
}
bool ComboBox::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
@@ -83,7 +83,7 @@ namespace gsr {
draw_unselected(window, draw_pos);
}
void ComboBox::add_item(const std::string &text, const std::string &id, bool allow_duplicate) {
void ComboBox::add_item(std::string_view text, const std::string &id, bool allow_duplicate) {
if(!allow_duplicate) {
for(const auto &item : items) {
if(item.id == id)
@@ -91,9 +91,9 @@ namespace gsr {
}
}
items.push_back({mgl::Text(text, *font), id, {0.0f, 0.0f}});
items.back().text.set_max_width(font->get_character_size() * 20); // TODO: Make a proper solution
//items.back().text.set_max_rows(1);
items.push_back({mgl::Text(text, font_desc.c_str()), id, {0.0f, 0.0f}});
items.back().text.set_wrap_width(items.back().text.get_font_size() * 40); // TODO: Make a proper solution
items.back().text.set_max_rows(2);
dirty = true;
}
@@ -104,7 +104,7 @@ namespace gsr {
dirty = true;
}
void ComboBox::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
void ComboBox::set_selected_item(std::string_view id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
for(size_t i = 0; i < items.size(); ++i) {
auto &item = items[i];
if(item.id == id && item.enabled) {
@@ -120,7 +120,7 @@ namespace gsr {
}
}
void ComboBox::set_item_enabled(const std::string &id, bool enabled) {
void ComboBox::set_item_enabled(std::string_view id, bool enabled) {
for(size_t i = 0; i < items.size(); ++i) {
auto &item = items[i];
if(item.id == id) {
@@ -136,10 +136,9 @@ namespace gsr {
}
}
const std::string& ComboBox::get_selected_id() const {
std::string_view ComboBox::get_selected_id() const {
if(items.empty()) {
static std::string dummy;
return dummy;
return "";
} else {
return items[selected_item].id;
}
@@ -240,7 +239,7 @@ namespace gsr {
const int padding_right = padding_right_scale * get_theme().window_height;
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 : font->get_character_size()) };
max_size = { 0.0f, padding_top + padding_bottom + (selected_item_ptr ? selected_item_ptr->text.get_bounds().size.y : font_size) };
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);
@@ -263,12 +262,12 @@ namespace gsr {
const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
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 : font->get_character_size()) };
return { max_size.x, padding_top + padding_bottom + (selected_item_ptr ? selected_item_ptr->text.get_bounds().size.y : font_size) };
}
float ComboBox::get_dropdown_arrow_height() const {
const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
return (font->get_character_size() + padding_top + padding_bottom) * 0.4f;
return (font_size * 2.0f + padding_top + padding_bottom) * 0.4f;
}
}

View File

@@ -15,8 +15,12 @@ namespace gsr {
static const float icon_spacing_scale = 0.008f;
static const float border_scale = 0.003f;
DropdownButton::DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size) :
title_font(title_font), description_font(description_font), size(size), title(title, *title_font), description(description, *description_font)
DropdownButton::DropdownButton(const char *title_font_desc, const char *description_font_desc, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size) :
title_font_desc(title_font_desc),
description_font_desc(description_font_desc),
size(size),
title(title, title_font_desc),
description(description, description_font_desc)
{
if(icon_texture && icon_texture->is_valid()) {
icon_sprite.set_texture(icon_texture);
@@ -193,11 +197,11 @@ namespace gsr {
if(item.id == id)
return;
}
items.push_back({mgl::Text(text, *title_font), mgl::Text(description, *description_font), nullptr, id});
items.push_back({mgl::Text(text, title_font_desc.c_str()), mgl::Text(description, description_font_desc.c_str()), nullptr, id});
dirty = true;
}
void DropdownButton::set_item_label(const std::string &id, const std::string &new_label) {
void DropdownButton::set_item_label(std::string_view id, const std::string &new_label) {
for(auto &item : items) {
if(item.id == id) {
item.text.set_string(new_label);
@@ -206,7 +210,7 @@ namespace gsr {
}
}
void DropdownButton::set_item_icon(const std::string &id, mgl::Texture *texture) {
void DropdownButton::set_item_icon(std::string_view id, mgl::Texture *texture) {
for(auto &item : items) {
if(item.id == id) {
item.icon_texture = texture;
@@ -215,7 +219,7 @@ namespace gsr {
}
}
void DropdownButton::set_item_description(const std::string &id, const std::string &new_description) {
void DropdownButton::set_item_description(std::string_view id, const std::string &new_description) {
for(auto &item : items) {
if(item.id == id) {
item.description_text.set_string(new_description);
@@ -224,7 +228,7 @@ namespace gsr {
}
}
void DropdownButton::set_item_enabled(const std::string &id, bool enabled) {
void DropdownButton::set_item_enabled(std::string_view id, bool enabled) {
for(auto &item : items) {
if(item.id == id) {
item.enabled = enabled;

View File

@@ -3,164 +3,38 @@
#include "../../include/Theme.hpp"
#include <mglpp/window/Window.hpp>
#include <mglpp/window/Event.hpp>
#include <mglpp/system/FloatRect.hpp>
#include <mglpp/system/Utf8.hpp>
#include <optional>
#include <string.h>
#include <string>
namespace gsr {
static const float padding_top_scale = 0.004629f;
static const float padding_top_scale = 0.004629f;
static const float padding_bottom_scale = 0.004629f;
static const float padding_left_scale = 0.007f;
static const float padding_right_scale = 0.007f;
static const float border_scale = 0.0015f;
static const float caret_width_scale = 0.001f;
static const float padding_left_scale = 0.007f;
static const float padding_right_scale = 0.007f;
static const float border_scale = 0.0015f;
static void string_replace_all(std::string &str, char old_char, char new_char) {
for(char &c : str) {
if(c == old_char)
c = new_char;
}
}
Entry::Entry(mgl::Font *font, const char *text, float max_width) :
text(std::u32string(), *font),
masked_text(std::u32string(), *font),
Entry::Entry(const char *font_desc, const char *text, float max_width) :
text_edit(font_desc, std::max(0.0f, max_width - (padding_left_scale * get_theme().window_height) - (padding_right_scale * get_theme().window_height))),
max_width(max_width)
{
this->text.set_color(get_color_theme().text_color);
this->masked_text.set_color(get_color_theme().text_color);
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;
const int padding_right = padding_right_scale * get_theme().window_height;
text_edit.set_single_paragraph_mode(true);
text_edit.set_color(get_color_theme().text_color);
text_edit.set_margins(padding_left, padding_top, padding_right, padding_bottom);
set_text(text);
text_edit.sync();
}
bool Entry::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) {
bool Entry::on_event(mgl::Event &event, mgl::Window &/*window*/, mgl::vec2f /*offset*/) {
if(!visible)
return true;
mgl::Text32 &active_text = masked ? masked_text : text;
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
const mgl::vec2f mouse_pos = { (float)event.mouse_button.x, (float)event.mouse_button.y };
selected = mgl::FloatRect(position + offset, get_size()).contains(mouse_pos);
if(selected) {
selecting_text = true;
const auto caret_index_mouse = find_closest_caret_index_by_position(mouse_pos);
caret.index = caret_index_mouse.index;
caret.offset_x = caret_index_mouse.pos.x - active_text.get_position().x;
selection_start_caret = caret;
show_selection = true;
} else {
selecting_text = false;
selecting_with_keyboard = false;
show_selection = false;
}
} else if(event.type == mgl::Event::MouseButtonReleased && event.mouse_button.button == mgl::Mouse::Left) {
selecting_text = false;
if(caret.index == selection_start_caret.index)
show_selection = false;
} else if(event.type == mgl::Event::MouseMoved && selected) {
if(selecting_text) {
const auto caret_index_mouse = find_closest_caret_index_by_position(mgl::vec2f(event.mouse_move.x, event.mouse_move.y));
caret.index = caret_index_mouse.index;
caret.offset_x = caret_index_mouse.pos.x - active_text.get_position().x;
return false;
}
} else if(event.type == mgl::Event::KeyPressed && selected) {
int selection_start_byte = caret.index;
int selection_end_byte = caret.index;
if(show_selection) {
selection_start_byte = std::min(caret.index, selection_start_caret.index);
selection_end_byte = std::max(caret.index, selection_start_caret.index);
}
if(event.key.code == mgl::Keyboard::Backspace) {
if(selection_start_byte == selection_end_byte && caret.index > 0)
selection_start_byte -= 1;
replace_text(selection_start_byte, selection_end_byte - selection_start_byte, std::u32string());
} else if(event.key.code == mgl::Keyboard::Delete) {
if(selection_start_byte == selection_end_byte && caret.index < (int)active_text.get_string().size())
selection_end_byte += 1;
replace_text(selection_start_byte, selection_end_byte - selection_start_byte, std::u32string());
} else if(event.key.code == mgl::Keyboard::C && event.key.control) {
const size_t selection_num_bytes = selection_end_byte - selection_start_byte;
if(selection_num_bytes > 0)
window.set_clipboard(mgl::utf32_to_utf8(text.get_string().substr(selection_start_byte, selection_num_bytes)));
} else if(event.key.code == mgl::Keyboard::V && event.key.control) {
std::string clipboard_string = window.get_clipboard_string();
string_replace_all(clipboard_string, '\n', ' ');
replace_text(selection_start_byte, selection_end_byte - selection_start_byte, mgl::utf8_to_utf32(clipboard_string));
} else if(event.key.code == mgl::Keyboard::A && event.key.control) {
selection_start_caret.index = 0;
selection_start_caret.offset_x = 0.0f;
caret.index = active_text.get_string().size();
// TODO: Optimize
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
show_selection = true;
} else if(event.key.code == mgl::Keyboard::Left) {
if(!selecting_with_keyboard && show_selection)
show_selection = false;
else
move_caret_word(Direction::LEFT, event.key.control ? 999999 : 1);
if(!selecting_with_keyboard) {
selection_start_caret = caret;
show_selection = false;
}
} else if(event.key.code == mgl::Keyboard::Right) {
if(!selecting_with_keyboard && show_selection)
show_selection = false;
else
move_caret_word(Direction::RIGHT, event.key.control ? 999999 : 1);
if(!selecting_with_keyboard) {
selection_start_caret = caret;
show_selection = false;
}
} else if(event.key.code == mgl::Keyboard::Home) {
caret.index = 0;
caret.offset_x = 0.0f;
if(!selecting_with_keyboard) {
selection_start_caret = caret;
show_selection = false;
}
} else if(event.key.code == mgl::Keyboard::End) {
caret.index = active_text.get_string().size();
// TODO: Optimize
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
if(!selecting_with_keyboard) {
selection_start_caret = caret;
show_selection = false;
}
} else if(event.key.code == mgl::Keyboard::LShift || event.key.code == mgl::Keyboard::RShift) {
if(!show_selection)
selection_start_caret = caret;
selecting_with_keyboard = true;
show_selection = true;
}
return false;
} else if(event.type == mgl::Event::KeyReleased && selected) {
if(event.key.code == mgl::Keyboard::LShift || event.key.code == mgl::Keyboard::RShift) {
selecting_with_keyboard = false;
}
return false;
} else if(event.type == mgl::Event::TextEntered && selected && event.text.codepoint >= 32 && event.text.codepoint != 127) {
int selection_start_byte = caret.index;
int selection_end_byte = caret.index;
if(show_selection) {
selection_start_byte = std::min(caret.index, selection_start_caret.index);
selection_end_byte = std::max(caret.index, selection_start_caret.index);
}
replace_text(selection_start_byte, selection_end_byte - selection_start_byte, mgl::utf8_to_utf32((const unsigned char*)event.text.str, event.text.size));
if(text_edit.handle_event(event)) {
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left)
return true;
return false;
}
@@ -173,297 +47,47 @@ namespace gsr {
const mgl::vec2f draw_pos = position + offset;
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;
const int padding_right = padding_right_scale * get_theme().window_height;
const mgl::vec2f size = get_size();
mgl::Text32 &active_text = masked ? masked_text : text;
background.set_size(get_size());
background.set_size(size);
background.set_position(draw_pos.floor());
background.set_color(selected ? mgl::Color(0, 0, 0, 255) : mgl::Color(0, 0, 0, 120));
background.set_color(text_edit.is_focused() ? mgl::Color(0, 0, 0, 255) : mgl::Color(0, 0, 0, 120));
window.draw(background);
const int caret_width = std::max(1.0f, caret_width_scale * get_theme().window_height);
const mgl::vec2f caret_size = mgl::vec2f(caret_width, active_text.get_bounds().size.y).floor();
const float overflow_left = (caret.offset_x + padding_left) - (padding_left + text_overflow);
if(overflow_left < 0.0f)
text_overflow += overflow_left;
const float overflow_right = (caret.offset_x + padding_left) - (background.get_size().x - padding_right);
if(overflow_right - text_overflow > 0.0f)
text_overflow = overflow_right;
active_text.set_position((draw_pos + mgl::vec2f(padding_left, get_size().y * 0.5f - active_text.get_bounds().size.y * 0.5f) - mgl::vec2f(text_overflow, 0.0f)).floor());
const auto text_bounds = active_text.get_bounds();
const bool text_larger_than_background = text_bounds.size.x > (background.get_size().x - padding_left - padding_right);
const float text_overflow_right = (text_bounds.position.x + text_bounds.size.x) - (background.get_position().x + background.get_size().x - padding_right);
if(text_larger_than_background) {
if(text_overflow_right < 0.0f) {
text_overflow += text_overflow_right;
active_text.set_position(active_text.get_position() + mgl::vec2f(-text_overflow_right, 0.0f));
}
} else {
active_text.set_position(active_text.get_position() + mgl::vec2f(-text_overflow, 0.0f));
text_overflow = 0.0f;
}
if(selected) {
if(text_edit.is_focused()) {
const int border_size = std::max(1.0f, border_scale * get_theme().window_height);
draw_rectangle_outline(window, draw_pos.floor(), get_size().floor(), get_color_theme().tint_color, border_size);
draw_caret(window, draw_pos, caret_size);
draw_rectangle_outline(window, draw_pos.floor(), size.floor(), get_color_theme().tint_color, border_size);
}
const mgl::Scissor parent_scissor = window.get_scissor();
const mgl::Scissor scissor = scissor_get_sub_area(parent_scissor,
mgl::Scissor{
(background.get_position() + mgl::vec2f(padding_left, padding_top)).to_vec2i(),
(background.get_size() - mgl::vec2f(padding_left + padding_right, padding_top + padding_bottom)).to_vec2i()
});
window.set_scissor(scissor);
window.draw(active_text);
if(show_selection)
draw_caret_selection(window, draw_pos, caret_size);
window.set_scissor(parent_scissor);
}
void Entry::draw_caret(mgl::Window &window, mgl::vec2f draw_pos, mgl::vec2f caret_size) {
const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_left = padding_left_scale * get_theme().window_height;
mgl::Rectangle caret_rect(caret_size);
mgl::vec2f caret_draw_pos = draw_pos + mgl::vec2f(padding_left + caret.offset_x - text_overflow, padding_top);
caret_rect.set_position(caret_draw_pos.floor());
caret_rect.set_color(mgl::Color(255, 255, 255));
window.draw(caret_rect);
}
void Entry::draw_caret_selection(mgl::Window &window, mgl::vec2f draw_pos, mgl::vec2f caret_size) {
if(selection_start_caret.index == caret.index)
return;
const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_left = padding_left_scale * get_theme().window_height;
const int caret_width = std::max(1.0f, caret_width_scale * get_theme().window_height);
const int offset = caret.index < selection_start_caret.index ? caret_width : 0;
mgl::Rectangle caret_selection_rect(mgl::vec2f(std::abs(selection_start_caret.offset_x - caret.offset_x) - offset, caret_size.y).floor());
caret_selection_rect.set_position((draw_pos + mgl::vec2f(padding_left + std::min(caret.offset_x, selection_start_caret.offset_x) - text_overflow + offset, padding_top)).floor());
mgl::Color caret_select_color = get_color_theme().tint_color;
caret_select_color.a = 100;
caret_selection_rect.set_color(caret_select_color);
window.draw(caret_selection_rect);
text_edit.set_position(draw_pos.floor());
window.draw(text_edit);
}
mgl::vec2f Entry::get_size() {
if(!visible)
return {0.0f, 0.0f};
const int padding_top = padding_top_scale * get_theme().window_height;
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
return { max_width, text.get_bounds().size.y + padding_top + padding_bottom };
const mgl::vec2i text_size = text_edit.get_size(true);
return text_size.to_vec2f();
}
void Entry::move_caret_word(Direction direction, size_t max_codepoints) {
mgl::Text32 &active_text = masked ? masked_text : text;
const int dir_step = direction == Direction::LEFT ? -1 : 1;
const int num_delimiter_chars = 15;
const char delimiter_chars[num_delimiter_chars + 1] = " \t\n/.,:;\\[](){}";
const char32_t *text_str = active_text.get_string().data();
int num_non_delimiter_chars_found = 0;
for(size_t i = 0; i < max_codepoints; ++i) {
const uint32_t codepoint = text_str[caret.index];
const bool is_delimiter_char = codepoint < 127 && !!memchr(delimiter_chars, codepoint, num_delimiter_chars);
if(is_delimiter_char) {
if(num_non_delimiter_chars_found > 0)
break;
} else {
++num_non_delimiter_chars_found;
}
if(caret.index + dir_step < 0 || caret.index + dir_step > (int)active_text.get_string().size())
break;
caret.index += dir_step;
}
// TODO: Move right by some characters instead of calculating every character to caret index
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
void Entry::set_text(std::string_view str) {
text_edit.set_text(std::string(str).c_str());
}
EntryValidateHandlerResult Entry::set_text(const std::string &str) {
EntryValidateHandlerResult validate_result = set_text_internal(mgl::utf8_to_utf32(str));
if(validate_result == EntryValidateHandlerResult::ALLOW) {
mgl::Text32 &active_text = masked ? masked_text : text;
caret.index = active_text.get_string().size();
// TODO: Optimize
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
selection_start_caret = caret;
selecting_text = false;
selecting_with_keyboard = false;
show_selection = false;
}
return validate_result;
}
EntryValidateHandlerResult Entry::set_text_internal(std::u32string str) {
EntryValidateHandlerResult validate_result = EntryValidateHandlerResult::ALLOW;
if(validate_handler)
validate_result = validate_handler(*this, str);
if(validate_result == EntryValidateHandlerResult::ALLOW) {
text.set_string(std::move(str));
if(masked)
masked_text.set_string(std::u32string(text.get_string().size(), '*'));
// TODO: Call callback with utf32 instead?
if(on_changed)
on_changed(mgl::utf32_to_utf8(text.get_string()));
}
return validate_result;
}
std::string Entry::get_text() const {
return mgl::utf32_to_utf8(text.get_string());
std::string_view Entry::get_text() const {
return text_edit.get_text();
}
void Entry::set_masked(bool masked) {
if(masked == this->masked)
return;
this->masked = masked;
if(masked)
masked_text.set_string(std::u32string(text.get_string().size(), '*'));
else
masked_text.set_string(std::u32string());
mgl::Text32 &active_text = masked ? masked_text : text;
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
selection_start_caret.offset_x = active_text.find_character_pos(selection_start_caret.index).x - active_text.get_position().x;
text_edit.set_masked(masked);
}
bool Entry::is_masked() const {
return masked;
return text_edit.is_masked();
}
void Entry::replace_text(size_t index, size_t size, const std::u32string &replacement) {
if(index + size > text.get_string().size())
return;
const auto prev_caret = caret;
if((int)index >= caret.index)
caret.index += replacement.size();
else
caret.index = caret.index - size + replacement.size();
std::u32string str = text.get_string();
str.replace(index, size, replacement);
const EntryValidateHandlerResult validate_result = set_text_internal(std::move(str));
if(validate_result == EntryValidateHandlerResult::DENY) {
caret = prev_caret;
return;
} else if(validate_result == EntryValidateHandlerResult::REPLACED) {
return;
}
mgl::Text32 &active_text = masked ? masked_text : text;
// TODO: Optimize
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
selection_start_caret = caret;
selecting_text = false;
selecting_with_keyboard = false;
show_selection = false;
}
CaretIndexPos Entry::find_closest_caret_index_by_position(mgl::vec2f position) {
mgl::Text32 &active_text = masked ? masked_text : text;
const std::u32string &str = active_text.get_string();
mgl::Font *font = active_text.get_font();
CaretIndexPos result = {0, {active_text.get_position().x, active_text.get_position().y}};
for(result.index = 0; result.index < (int)str.size(); ++result.index) {
const uint32_t codepoint = str[result.index];
float glyph_width = 0.0f;
if(codepoint == '\t') {
const auto glyph = font->get_glyph(' ');
const int tab_width = 4;
glyph_width = glyph.advance * tab_width;
} else {
const auto glyph = font->get_glyph(codepoint);
glyph_width = glyph.advance;
}
if(result.pos.x + glyph_width * 0.5f >= position.x)
break;
result.pos.x += glyph_width;
}
return result;
}
static bool is_number(uint8_t c) {
return c >= '0' && c <= '9';
}
static std::optional<int> to_integer(const std::u32string &str) {
if(str.empty())
return std::nullopt;
size_t i = 0;
const bool negative = str[0] == '-';
if(negative)
i = 1;
int number = 0;
for(; i < str.size(); ++i) {
if(!is_number(str[i]))
return std::nullopt;
const int new_number = number * 10 + (str[i] - '0');
if(new_number < number)
return std::nullopt; // Overflow
number = new_number;
}
if(negative)
number = -number;
return number;
}
EntryValidateHandler create_entry_validator_integer_in_range(int min, int max) {
return [min, max](Entry &entry, const std::u32string &str) {
if(str.empty())
return EntryValidateHandlerResult::ALLOW;
const std::optional<int> number = to_integer(str);
if(!number)
return EntryValidateHandlerResult::DENY;
if(number.value() < min) {
entry.set_text(std::to_string(min));
return EntryValidateHandlerResult::REPLACED;
} else if(number.value() > max) {
entry.set_text(std::to_string(max));
return EntryValidateHandlerResult::REPLACED;
}
return EntryValidateHandlerResult::ALLOW;
};
void Entry::set_number_mode(bool enabled, int min_val, int max_val) {
text_edit.set_number_mode(enabled, min_val, max_val);
}
}

View File

@@ -54,7 +54,7 @@ namespace gsr {
times_clicked_within_timer = 1;
if(selected_item != -1 && times_clicked_within_timer > 0 && times_clicked_within_timer % 2 == 0) {
file_chooser->open_subdirectory(folders[selected_item].text.get_string().c_str());
file_chooser->open_subdirectory(folders[selected_item].text.get_string());
}
}
return true;
@@ -115,7 +115,7 @@ namespace gsr {
window.draw(folder_sprite);
// TODO: Dont allow text to go further left/right than item_pos (on the left side) and item_pos + item_size (on the right side).
folder.text.set_max_width(item_size.x);
folder.text.set_wrap_width(item_size.x);
folder.text.set_max_rows(2);
folder.text.set_position((folder_sprite.get_position() + mgl::vec2f(folder_sprite.get_size().x * 0.5f - folder.text.get_bounds().size.x * 0.5f, folder_sprite.get_size().y + folder_text_spacing_scale * get_theme().window_height)).floor());
window.draw(folder.text);
@@ -136,14 +136,17 @@ namespace gsr {
inner_size = mgl::vec2f(size.x, folder_pos.y - draw_pos.y);
}
void FileChooserBody::set_current_directory(const char *directory) {
void FileChooserBody::set_current_directory(std::string_view directory) {
folders.clear();
selected_item = -1;
mouse_over_item = -1;
DIR *d = opendir(directory);
char path[4096];
snprintf(path, sizeof(path), "%.*s", (int)directory.size(), directory.data());
DIR *d = opendir(path);
if(!d) {
fprintf(stderr, "gsr-ui error: failed to open directory: %s, error: %s\n", directory, strerror(errno));
fprintf(stderr, "gsr-ui error: failed to open directory: %s, error: %s\n", path, strerror(errno));
return;
}
@@ -154,7 +157,7 @@ namespace gsr {
if(dir->d_name[0] == '.')
continue;
snprintf(filepath, sizeof(filepath), "%s/%s", directory, dir->d_name);
snprintf(filepath, sizeof(filepath), "%s/%s", path, dir->d_name);
struct stat st;
if(stat(filepath, &st) == -1)
@@ -163,7 +166,7 @@ namespace gsr {
if(!S_ISDIR(st.st_mode))
continue;
folders.push_back({mgl::Text(dir->d_name, get_theme().body_font), st.st_mtim.tv_sec});
folders.push_back({mgl::Text(dir->d_name, get_theme().body_font_desc.c_str()), st.st_mtim.tv_sec});
}
closedir(d);
@@ -191,9 +194,9 @@ namespace gsr {
this->size = size;
}
FileChooser::FileChooser(const char *start_directory, mgl::vec2f size) :
FileChooser::FileChooser(std::string_view start_directory, mgl::vec2f size) :
size(size),
current_directory_text(start_directory, get_theme().body_font),
current_directory_text(start_directory, get_theme().body_font_desc.c_str()),
up_arrow_sprite(&get_theme().up_arrow_texture),
scrollable_page(size)
{
@@ -275,18 +278,20 @@ namespace gsr {
return size;
}
void FileChooser::set_current_directory(const char *directory) {
void FileChooser::set_current_directory(std::string_view directory) {
current_directory_text.set_string(directory);
file_chooser_body_ptr->set_current_directory(directory);
scrollable_page.reset_scroll();
}
void FileChooser::open_subdirectory(const char *name) {
void FileChooser::open_subdirectory(std::string_view name) {
char filepath[PATH_MAX];
if(current_directory_text.get_string() == "/")
snprintf(filepath, sizeof(filepath), "/%s", name);
else
snprintf(filepath, sizeof(filepath), "%s/%s", current_directory_text.get_string().c_str(), name);
const std::string_view current_dir = current_directory_text.get_string();
if(current_dir == "/") {
snprintf(filepath, sizeof(filepath), "/%.*s", (int)name.size(), name.data());
} else {
snprintf(filepath, sizeof(filepath), "%.*s/%.*s", (int)current_dir.size(), current_dir.data(), (int)name.size(), name.data());
}
set_current_directory(filepath);
}
@@ -294,7 +299,7 @@ namespace gsr {
set_current_directory(get_parent_directory(current_directory_text.get_string()).c_str());
}
const std::string& FileChooser::get_current_directory() const {
std::string_view FileChooser::get_current_directory() const {
return current_directory_text.get_string();
}
}

View File

@@ -101,9 +101,9 @@ namespace gsr {
if(!configure_hotkey_button)
return;
mgl::Text title_text(TRF("Press a key combination to use for the hotkey: \"%s\"", hotkey_configure_action_name.c_str()), get_theme().title_font);
mgl::Text hotkey_text(configure_hotkey_button->get_text(), get_theme().top_bar_font);
mgl::Text description_text(TR("Alpha-numerical keys can't be used alone in hotkeys, they have to be used one or more of these keys: Alt, Ctrl, Shift and Super.\nPress Esc to cancel or Backspace to remove the hotkey."), get_theme().body_font);
mgl::Text title_text(TRF("Press a key combination to use for the hotkey: \"%s\"", hotkey_configure_action_name.c_str()), get_theme().title_font_desc.c_str());
mgl::Text hotkey_text(configure_hotkey_button->get_text(), get_theme().top_bar_font_desc.c_str());
mgl::Text description_text(TR("Alpha-numerical keys can't be used alone in hotkeys, they have to be used one or more of these keys: Alt, Ctrl, Shift and Super.\nPress Esc to cancel or Backspace to remove the hotkey."), get_theme().body_font_desc.c_str());
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);
@@ -146,13 +146,13 @@ namespace gsr {
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, TR("Accent color"), get_color_theme().text_color));
auto tint_color_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Accent color"), get_color_theme().text_color));
auto tint_color_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
tint_color_radio_button_ptr = tint_color_radio_button.get();
tint_color_radio_button->add_item(TR("Red"), "amd");
tint_color_radio_button->add_item(TR("Green"), "nvidia");
tint_color_radio_button->add_item(TR("Blue"), "intel");
tint_color_radio_button->on_selection_changed = [](const std::string&, const std::string &id) {
tint_color_radio_button->on_selection_changed = [](std::string_view, std::string_view id) {
if(id == "amd")
get_color_theme().tint_color = mgl::Color(221, 0, 49);
else if(id == "nvidia")
@@ -167,12 +167,12 @@ namespace gsr {
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, TR("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);
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Start program on system startup?"), get_color_theme().text_color));
auto startup_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
startup_radio_button_ptr = startup_radio_button.get();
startup_radio_button->add_item(TR("Yes"), "start_on_system_startup");
startup_radio_button->add_item(TR("No"), "dont_start_on_system_startup");
startup_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) {
startup_radio_button->on_selection_changed = [&](std::string_view, std::string_view id) {
bool enable = false;
if(id == "dont_start_on_system_startup")
enable = false;
@@ -191,28 +191,28 @@ namespace gsr {
}
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::VERTICAL);
auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::VERTICAL);
enable_keyboard_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get();
enable_hotkeys_radio_button->add_item(TR("Yes"), "enable_hotkeys");
enable_hotkeys_radio_button->add_item(TR("Yes, but only grab virtual devices (supports some input remapping software)"), "enable_hotkeys_virtual_devices");
enable_hotkeys_radio_button->add_item(TR("Yes, but don't grab devices (supports all input remapping software)"), "enable_hotkeys_no_grab");
enable_hotkeys_radio_button->add_item(TR("No"), "disable_hotkeys");
enable_hotkeys_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) {
enable_hotkeys_radio_button->on_selection_changed = [&](std::string_view, std::string_view id) {
if(on_keyboard_hotkey_changed)
on_keyboard_hotkey_changed(id.c_str());
on_keyboard_hotkey_changed(id);
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);
auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
enable_joystick_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get();
enable_hotkeys_radio_button->add_item(TR("Yes"), "enable_hotkeys");
enable_hotkeys_radio_button->add_item(TR("No"), "disable_hotkeys");
enable_hotkeys_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) {
enable_hotkeys_radio_button->on_selection_changed = [&](std::string_view, std::string_view id) {
if(on_joystick_hotkey_changed)
on_joystick_hotkey_changed(id.c_str());
on_joystick_hotkey_changed(id);
return true;
};
return enable_hotkeys_radio_button;
@@ -221,8 +221,8 @@ namespace gsr {
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, TR("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));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Show/hide UI:"), get_color_theme().text_color));
auto show_hide_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", 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));
@@ -236,13 +236,13 @@ namespace gsr {
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, TR("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));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Turn replay on/off:"), get_color_theme().text_color));
auto turn_replay_on_off_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", 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, TR("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));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Save replay:"), get_color_theme().text_color));
auto save_replay_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", 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));
@@ -260,13 +260,13 @@ namespace gsr {
std::unique_ptr<List> GlobalSettingsPage::create_replay_partial_save_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, TR("Save 1 minute replay:"), get_color_theme().text_color));
auto save_replay_1_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Save 1 minute replay:"), get_color_theme().text_color));
auto save_replay_1_min_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
save_replay_1_min_button_ptr = save_replay_1_min_button.get();
list->add_widget(std::move(save_replay_1_min_button));
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Save 10 minute replay:"), get_color_theme().text_color));
auto save_replay_10_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Save 10 minute replay:"), get_color_theme().text_color));
auto save_replay_10_min_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
save_replay_10_min_button_ptr = save_replay_10_min_button.get();
list->add_widget(std::move(save_replay_10_min_button));
@@ -284,13 +284,13 @@ namespace gsr {
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, TR("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));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Start/stop recording:"), get_color_theme().text_color));
auto start_stop_recording_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", 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, TR("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));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Pause/unpause recording:"), get_color_theme().text_color));
auto pause_unpause_recording_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", 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));
@@ -308,8 +308,8 @@ namespace gsr {
std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_window_region_options() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Start/stop recording a region:"), get_color_theme().text_color));
auto start_stop_recording_region_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Start/stop recording a region:"), get_color_theme().text_color));
auto start_stop_recording_region_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
start_stop_recording_region_button_ptr = start_stop_recording_region_button.get();
list->add_widget(std::move(start_stop_recording_region_button));
@@ -329,8 +329,8 @@ namespace gsr {
else
snprintf(str, sizeof(str), "%s", TR("Start/stop recording with desktop portal:"));
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
auto start_stop_recording_window_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
auto start_stop_recording_window_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
start_stop_recording_window_button_ptr = start_stop_recording_window_button.get();
list->add_widget(std::move(start_stop_recording_window_button));
@@ -344,8 +344,8 @@ namespace gsr {
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, TR("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));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Start/stop streaming:"), get_color_theme().text_color));
auto start_stop_streaming_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", 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));
@@ -359,8 +359,8 @@ namespace gsr {
std::unique_ptr<List> GlobalSettingsPage::create_screenshot_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, TR("Take a screenshot:"), get_color_theme().text_color));
auto take_screenshot_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Take a screenshot:"), get_color_theme().text_color));
auto take_screenshot_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
take_screenshot_button_ptr = take_screenshot_button.get();
list->add_widget(std::move(take_screenshot_button));
@@ -374,8 +374,8 @@ namespace gsr {
std::unique_ptr<List> GlobalSettingsPage::create_screenshot_region_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, TR("Take a screenshot of a region:"), get_color_theme().text_color));
auto take_screenshot_region_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Take a screenshot of a region:"), get_color_theme().text_color));
auto take_screenshot_region_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
take_screenshot_region_button_ptr = take_screenshot_region_button.get();
list->add_widget(std::move(take_screenshot_region_button));
@@ -395,8 +395,8 @@ namespace gsr {
else
snprintf(str, sizeof(str), "%s", TR("Take a screenshot with desktop portal:"));
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
auto take_screenshot_window_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
auto take_screenshot_window_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
take_screenshot_window_button_ptr = take_screenshot_window_button.get();
list->add_widget(std::move(take_screenshot_window_button));
@@ -410,7 +410,7 @@ namespace gsr {
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, TR("Clear hotkeys"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto clear_hotkeys_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Clear hotkeys"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
clear_hotkeys_button->on_click = [this] {
for_each_config_hotkey([&](ConfigHotkey *config_hotkey_item) {
*config_hotkey_item = {mgl::Keyboard::Unknown, 0};
@@ -420,7 +420,7 @@ namespace gsr {
};
list->add_widget(std::move(clear_hotkeys_button));
auto reset_hotkeys_button = std::make_unique<Button>(&get_theme().body_font, TR("Reset hotkeys to default"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto reset_hotkeys_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Reset hotkeys to default"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
reset_hotkeys_button->on_click = [this] {
config.set_hotkeys_to_default();
load_hotkeys();
@@ -433,11 +433,11 @@ namespace gsr {
static std::unique_ptr<List> create_joystick_hotkey_text(mgl::Texture *image1, mgl::Texture *image2, float max_height, const char *suffix) {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Press"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Press"), get_color_theme().text_color));
list->add_widget(std::make_unique<Image>(image1, mgl::vec2f{max_height, 1000.0f}, Image::ScaleBehavior::SCALE));
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("and"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("and"), get_color_theme().text_color));
list->add_widget(std::make_unique<Image>(image2, mgl::vec2f{max_height, 1000.0f}, Image::ScaleBehavior::SCALE));
list->add_widget(std::make_unique<Label>(&get_theme().body_font, suffix, get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), suffix, get_color_theme().text_color));
return list;
}
@@ -446,7 +446,7 @@ namespace gsr {
List *list_ptr = list.get();
auto subsection = std::make_unique<Subsection>(TR("Keyboard 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, TR("Enable keyboard hotkeys?"), get_color_theme().text_color));
list_ptr->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Enable keyboard hotkeys?"), get_color_theme().text_color));
list_ptr->add_widget(create_enable_keyboard_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());
@@ -459,7 +459,7 @@ namespace gsr {
list_ptr->add_widget(create_screenshot_hotkey_options());
list_ptr->add_widget(create_screenshot_region_hotkey_options());
list_ptr->add_widget(create_screenshot_window_hotkey_options());
list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Press ESC to go back to the previous page/close the UI."), get_color_theme().text_color));
list_ptr->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Press ESC to go back to the previous page/close the UI."), get_color_theme().text_color));
list_ptr->add_widget(create_hotkey_control_buttons());
return subsection;
}
@@ -469,21 +469,21 @@ namespace gsr {
List *list_ptr = list.get();
auto subsection = std::make_unique<Subsection>(TR("Controller 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, TR("Enable controller hotkeys?"), get_color_theme().text_color));
list_ptr->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("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_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_options_texture, get_theme().body_font.get_character_size(), TR("to show/hide the UI")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_up_texture, get_theme().body_font.get_character_size(), TR("to take a screenshot")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_down_texture, get_theme().body_font.get_character_size(), TR("to save a replay")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_left_texture, get_theme().body_font.get_character_size(), TR("to start/stop recording")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_right_texture, get_theme().body_font.get_character_size(), TR("to turn replay on/off")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_cross_texture, get_theme().body_font.get_character_size(), TR("to save a 1 minute replay")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_triangle_texture, get_theme().body_font.get_character_size(), TR("to save a 10 minute replay")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_options_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to show/hide the UI")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_up_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to take a screenshot")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_down_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to save a replay")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_left_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to start/stop recording")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_right_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to turn replay on/off")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_cross_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to save a 1 minute replay")));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_triangle_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to save a 10 minute replay")));
return subsection;
}
std::unique_ptr<Button> GlobalSettingsPage::create_exit_program_button() {
auto exit_program_button = std::make_unique<Button>(&get_theme().body_font, TR("Exit program"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto exit_program_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("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");
@@ -492,7 +492,7 @@ namespace gsr {
}
std::unique_ptr<Button> GlobalSettingsPage::create_go_back_to_old_ui_button() {
auto exit_program_button = std::make_unique<Button>(&get_theme().body_font, TR("Go back to the old UI"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto exit_program_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("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");
@@ -502,13 +502,13 @@ namespace gsr {
std::unique_ptr<List> GlobalSettingsPage::create_notification_speed() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Notification speed"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Notification speed"), get_color_theme().text_color));
auto radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
auto radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
notification_speed_button_ptr = radio_button.get();
radio_button->add_item(TR("Normal"), "normal");
radio_button->add_item(TR("Fast"), "fast");
radio_button->on_selection_changed = [this](const std::string&, const std::string &id) {
radio_button->on_selection_changed = [this](std::string_view, std::string_view id) {
if(id == "normal")
overlay->set_notification_speed(NotificationSpeed::NORMAL);
else if(id == "fast")
@@ -522,9 +522,9 @@ namespace gsr {
std::unique_ptr<List> GlobalSettingsPage::create_language() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Language"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Language"), get_color_theme().text_color));
auto combo_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto combo_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
language_combo_box_ptr = combo_box.get();
combo_box->add_item(TR("System language"), "");
combo_box->add_item("English", "en");
@@ -533,8 +533,8 @@ namespace gsr {
combo_box->add_item("Magyar", "hu");
combo_box->add_item("Русский", "ru");
combo_box->add_item("Українська", "uk");
combo_box->on_selection_changed = [](const std::string&, const std::string &id) {
Translation::instance().load_language(id.c_str());
combo_box->on_selection_changed = [](std::string_view, std::string_view id) {
Translation::instance().load_language(id);
return true;
};
list->add_widget(std::move(combo_box));
@@ -573,27 +573,27 @@ namespace gsr {
char str[128];
const std::string gsr_version = gsr_info->system_info.gsr_version.to_string();
snprintf(str, sizeof(str), TR("GSR version: %s"), gsr_version.c_str());
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
snprintf(str, sizeof(str), TR("GSR-UI version: %s"), GSR_UI_VERSION);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
if(inside_flatpak) {
snprintf(str, sizeof(str), TR("Flatpak version: %s"), GSR_FLATPAK_VERSION);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
}
snprintf(str, sizeof(str), TR("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));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
return std::make_unique<Subsection>(TR("Application info"), std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
}
std::unique_ptr<Subsection> GlobalSettingsPage::create_donate_subsection(ScrollablePage *parent_page) {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("If you would like to donate you can do so by donating at https://buymeacoffee.com/dec05eba:"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("If you would like to donate you can do so by donating at https://buymeacoffee.com/dec05eba:"), get_color_theme().text_color));
auto donate_button = std::make_unique<Button>(&get_theme().body_font, TR("Donate"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto donate_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Donate"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
donate_button->on_click = [this] {
const char *args[] = { "xdg-open", "https://buymeacoffee.com/dec05eba", nullptr };
exec_program_daemonized(args);
@@ -601,7 +601,7 @@ namespace gsr {
};
list->add_widget(std::move(donate_button));
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("All donations go toward developing software (including GPU Screen Recorder)\nand buying hardware to test the software."), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("All donations go toward developing software (including GPU Screen Recorder)\nand buying hardware to test the software."), get_color_theme().text_color));
return std::make_unique<Subsection>(TR("Donate"), std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
}

View File

@@ -9,8 +9,8 @@ namespace gsr {
static const float button_spacing_scale = 0.015f;
GsrPage::GsrPage(const char *top_text, const char *bottom_text) :
top_text(top_text, get_theme().title_font),
bottom_text(bottom_text, get_theme().title_font)
top_text(top_text, get_theme().title_font_desc.c_str()),
bottom_text(bottom_text, get_theme().title_font_desc.c_str())
{
const float margin = 0.02f;
set_margins(margin, margin, margin, margin);
@@ -151,7 +151,7 @@ namespace gsr {
}
void GsrPage::add_button(const std::string &text, const std::string &id, mgl::Color color) {
auto button = std::make_unique<Button>(&get_theme().title_font, text.c_str(),
auto button = std::make_unique<Button>(get_theme().title_font_desc.c_str(), text.c_str(),
mgl::vec2f(get_theme().window_width / 10, get_theme().window_height / 15).floor(), color);
button->set_border_scale(0.003f);
button->on_click = [this, id]() {

View File

@@ -2,7 +2,7 @@
#include <mglpp/window/Window.hpp>
namespace gsr {
Label::Label(mgl::Font *font, const char *text, mgl::Color color) : text(text, *font) {
Label::Label(const char *font_desc, const char *text, mgl::Color color) : text(text, font_desc) {
this->text.set_color(color);
}
@@ -18,11 +18,11 @@ namespace gsr {
window.draw(text);
}
void Label::set_text(std::string str) {
text.set_string(std::move(str));
void Label::set_text(std::string_view str) {
text.set_string(str);
}
const std::string& Label::get_text() const {
std::string_view Label::get_text() const {
return text.get_string();
}
@@ -32,4 +32,14 @@ namespace gsr {
return text.get_bounds().size;
}
// Set to 0 to disable
void Label::set_wrap_width(int width) {
text.set_wrap_width(width);
}
// Set to 0 to disable
void Label::set_max_rows(int max_rows) {
text.set_max_rows(max_rows);
}
}

View File

@@ -14,7 +14,7 @@ namespace gsr {
static const float spacing_scale = 0.007f;
static const float border_scale = 0.0015f;
RadioButton::RadioButton(mgl::Font *font, Orientation orientation) : font(font), orientation(orientation) {
RadioButton::RadioButton(const char *font_desc, Orientation orientation) : font_desc(font_desc), orientation(orientation) {
}
@@ -122,7 +122,7 @@ namespace gsr {
break;
case Orientation::HORIZONTAL:
size.x += bounds.x + padding_left + padding_right;
size.y = font->get_character_size() + (float)padding_top + (float)padding_bottom;
size.y = item.text.get_font_size()*2.0f + (float)padding_top + (float)padding_bottom;
break;
}
}
@@ -150,11 +150,11 @@ namespace gsr {
}
void RadioButton::add_item(const std::string &text, const std::string &id) {
items.push_back({mgl::Text(text, *font), id});
items.push_back({mgl::Text(text, font_desc.c_str()), id});
dirty = true;
}
void RadioButton::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
void RadioButton::set_selected_item(std::string_view id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
for(size_t i = 0; i < items.size(); ++i) {
auto &item = items[i];
if(item.id == id) {
@@ -169,19 +169,17 @@ namespace gsr {
}
}
const std::string& RadioButton::get_selected_id() const {
std::string_view RadioButton::get_selected_id() const {
if(items.empty()) {
static std::string dummy;
return dummy;
return "";
} else {
return items[selected_item].id;
}
}
const std::string& RadioButton::get_selected_text() const {
std::string_view RadioButton::get_selected_text() const {
if(items.empty()) {
static std::string dummy;
return dummy;
return "";
} else {
return items[selected_item].text.get_string();
}

View File

@@ -11,7 +11,16 @@
#include "../../include/gui/Subsection.hpp"
#include "../../include/gui/FileChooser.hpp"
#include <charconv>
namespace gsr {
template <typename T>
static T sv_to_int(std::string_view str) {
T result = 0;
std::from_chars(str.data(), str.data() + str.size(), result);
return result;
}
ScreenshotSettingsPage::ScreenshotSettingsPage(const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title) :
StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
config(config),
@@ -35,7 +44,7 @@ namespace gsr {
}
std::unique_ptr<ComboBox> ScreenshotSettingsPage::create_record_area_box() {
auto record_area_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto record_area_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
// TODO: Show options not supported but disable them
if(capture_options.window)
record_area_box->add_item(TR("Window"), "window");
@@ -56,21 +65,21 @@ namespace gsr {
std::unique_ptr<Widget> ScreenshotSettingsPage::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, TR("Capture source:"), get_color_theme().text_color));
record_area_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Capture source:"), get_color_theme().text_color));
record_area_list->add_widget(create_record_area_box());
return record_area_list;
}
std::unique_ptr<Entry> ScreenshotSettingsPage::create_image_width_entry() {
auto image_width_entry = std::make_unique<Entry>(&get_theme().body_font, "1920", get_theme().body_font.get_character_size() * 3);
image_width_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
auto image_width_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "1920", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
image_width_entry->set_number_mode(true, 1, 1 << 15);
image_width_entry_ptr = image_width_entry.get();
return image_width_entry;
}
std::unique_ptr<Entry> ScreenshotSettingsPage::create_image_height_entry() {
auto image_height_entry = std::make_unique<Entry>(&get_theme().body_font, "1080", get_theme().body_font.get_character_size() * 3);
image_height_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
auto image_height_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "1080", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
image_height_entry->set_number_mode(true, 1, 1 << 15);
image_height_entry_ptr = image_height_entry.get();
return image_height_entry;
}
@@ -78,21 +87,21 @@ namespace gsr {
std::unique_ptr<List> ScreenshotSettingsPage::create_image_resolution() {
auto area_size_params_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
area_size_params_list->add_widget(create_image_width_entry());
area_size_params_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "x", get_color_theme().text_color));
area_size_params_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), "x", get_color_theme().text_color));
area_size_params_list->add_widget(create_image_height_entry());
return area_size_params_list;
}
std::unique_ptr<List> ScreenshotSettingsPage::create_image_resolution_section() {
auto image_resolution_list = std::make_unique<List>(List::Orientation::VERTICAL);
image_resolution_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Image resolution limit:"), get_color_theme().text_color));
image_resolution_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Image resolution limit:"), get_color_theme().text_color));
image_resolution_list->add_widget(create_image_resolution());
image_resolution_list_ptr = image_resolution_list.get();
return image_resolution_list;
}
std::unique_ptr<CheckBox> ScreenshotSettingsPage::create_restore_portal_session_checkbox() {
auto restore_portal_session_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Restore portal session"));
auto restore_portal_session_checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Restore portal session"));
restore_portal_session_checkbox->set_checked(true);
restore_portal_session_checkbox_ptr = restore_portal_session_checkbox.get();
return restore_portal_session_checkbox;
@@ -100,14 +109,14 @@ namespace gsr {
std::unique_ptr<List> ScreenshotSettingsPage::create_restore_portal_session_section() {
auto restore_portal_session_list = std::make_unique<List>(List::Orientation::VERTICAL);
restore_portal_session_list->add_widget(std::make_unique<Label>(&get_theme().body_font, " ", get_color_theme().text_color));
restore_portal_session_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), " ", get_color_theme().text_color));
restore_portal_session_list->add_widget(create_restore_portal_session_checkbox());
restore_portal_session_list_ptr = restore_portal_session_list.get();
return restore_portal_session_list;
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_change_image_resolution_section() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Change image resolution"));
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Change image resolution"));
change_image_resolution_checkbox_ptr = checkbox.get();
return checkbox;
}
@@ -127,9 +136,9 @@ namespace gsr {
std::unique_ptr<List> ScreenshotSettingsPage::create_image_quality_section() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Image quality:"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Image quality:"), get_color_theme().text_color));
auto image_quality_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto image_quality_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
image_quality_box->add_item(TR("Medium"), "medium");
image_quality_box->add_item(TR("High"), "high");
image_quality_box->add_item(TR("Very high (Recommended)"), "very_high");
@@ -143,7 +152,7 @@ namespace gsr {
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_record_cursor_section() {
auto record_cursor_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Record cursor"));
auto record_cursor_checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Record cursor"));
record_cursor_checkbox->set_checked(true);
record_cursor_checkbox_ptr = record_cursor_checkbox.get();
return record_cursor_checkbox;
@@ -158,15 +167,15 @@ namespace gsr {
std::unique_ptr<List> ScreenshotSettingsPage::create_save_directory(const char *label) {
auto save_directory_list = std::make_unique<List>(List::Orientation::VERTICAL);
save_directory_list->add_widget(std::make_unique<Label>(&get_theme().body_font, label, get_color_theme().text_color));
auto save_directory_button = std::make_unique<Button>(&get_theme().body_font, get_pictures_dir().c_str(), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
save_directory_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), label, get_color_theme().text_color));
auto save_directory_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), get_pictures_dir().c_str(), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
save_directory_button_ptr = save_directory_button.get();
save_directory_button->on_click = [this]() {
auto select_directory_page = std::make_unique<GsrPage>(TR("File"), TR("Settings"));
select_directory_page->add_button(TR("Save"), "save", get_color_theme().tint_color);
select_directory_page->add_button(TR("Cancel"), "cancel", get_color_theme().page_bg_color);
auto file_chooser = std::make_unique<FileChooser>(save_directory_button_ptr->get_text().c_str(), select_directory_page->get_inner_size());
auto file_chooser = std::make_unique<FileChooser>(save_directory_button_ptr->get_text(), select_directory_page->get_inner_size());
FileChooser *file_chooser_ptr = file_chooser.get();
select_directory_page->add_widget(std::move(file_chooser));
@@ -186,7 +195,7 @@ namespace gsr {
}
std::unique_ptr<ComboBox> ScreenshotSettingsPage::create_image_format_box() {
auto box = std::make_unique<ComboBox>(&get_theme().body_font);
auto box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
if(gsr_info->supported_image_formats.jpeg)
box->add_item("jpg", "jpg");
if(gsr_info->supported_image_formats.png)
@@ -197,7 +206,7 @@ namespace gsr {
std::unique_ptr<List> ScreenshotSettingsPage::create_image_format_section() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Image format:"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Image format:"), get_color_theme().text_color));
list->add_widget(create_image_format_box());
return list;
}
@@ -212,33 +221,33 @@ namespace gsr {
std::unique_ptr<CheckBox> ScreenshotSettingsPage::create_save_screenshot_in_game_folder() {
char text[256];
snprintf(text, sizeof(text), "%s%s", TR("Save screenshot in a folder based on the games name"), supports_window_title ? "" : " (X11 applications only)");
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text);
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), text);
save_screenshot_in_game_folder_checkbox_ptr = checkbox.get();
return checkbox;
}
std::unique_ptr<CheckBox> ScreenshotSettingsPage::create_save_screenshot_to_clipboard() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, gsr_info->system_info.display_server == DisplayServer::X11 ? TR("Save screenshot to clipboard") : TR("Save screenshot to clipboard (Not supported properly by Wayland)"));
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), gsr_info->system_info.display_server == DisplayServer::X11 ? TR("Save screenshot to clipboard") : TR("Save screenshot to clipboard (Not supported properly by Wayland)"));
save_screenshot_to_clipboard_checkbox_ptr = checkbox.get();
return checkbox;
}
std::unique_ptr<CheckBox> ScreenshotSettingsPage::create_save_screenshot_to_disk() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Save screenshot to disk"));
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Save screenshot to disk"));
save_screenshot_to_disk_checkbox_ptr = checkbox.get();
checkbox->set_checked(true);
return checkbox;
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_notifications() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Show screenshot notifications"));
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Show screenshot notifications"));
checkbox->set_checked(true);
show_notification_checkbox_ptr = checkbox.get();
return checkbox;
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_led_indicator() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Blink scroll lock led when taking a screenshot"));
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Blink scroll lock led when taking a screenshot"));
checkbox->set_checked(true);
led_indicator_checkbox_ptr = checkbox.get();
return checkbox;
@@ -262,7 +271,7 @@ namespace gsr {
std::unique_ptr<List> ScreenshotSettingsPage::create_custom_script_screenshot_entry() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL, List::Alignment::CENTER);
auto create_custom_script_screenshot_entry = std::make_unique<Entry>(&get_theme().body_font, "", get_theme().body_font.get_character_size() * 20);
auto create_custom_script_screenshot_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 20);
create_custom_script_screenshot_entry_ptr = create_custom_script_screenshot_entry.get();
list->add_widget(std::move(create_custom_script_screenshot_entry));
@@ -271,7 +280,7 @@ namespace gsr {
std::unique_ptr<List> ScreenshotSettingsPage::create_custom_script_screenshot() {
auto custom_script_screenshot_list = std::make_unique<List>(List::Orientation::VERTICAL);
custom_script_screenshot_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Command to open the screenshot with:"), get_color_theme().text_color));
custom_script_screenshot_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Command to open the screenshot with:"), get_color_theme().text_color));
custom_script_screenshot_list->add_widget(create_custom_script_screenshot_entry());
return custom_script_screenshot_list;
}
@@ -302,7 +311,7 @@ namespace gsr {
void ScreenshotSettingsPage::add_widgets() {
content_page_ptr->add_widget(create_settings());
record_area_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
record_area_box_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
const bool portal_selected = id == "portal";
image_resolution_list_ptr->set_visible(change_image_resolution_checkbox_ptr->is_checked());
restore_portal_session_list_ptr->set_visible(portal_selected);
@@ -362,8 +371,8 @@ namespace gsr {
Config prev_config = config;
config.screenshot_config.record_area_option = record_area_box_ptr->get_selected_id();
config.screenshot_config.image_width = atoi(image_width_entry_ptr->get_text().c_str());
config.screenshot_config.image_height = atoi(image_height_entry_ptr->get_text().c_str());
config.screenshot_config.image_width = sv_to_int<int32_t>(image_width_entry_ptr->get_text());
config.screenshot_config.image_height = sv_to_int<int32_t>(image_height_entry_ptr->get_text());
config.screenshot_config.change_image_resolution = change_image_resolution_checkbox_ptr->is_checked();
config.screenshot_config.image_quality = image_quality_box_ptr->get_selected_id();
config.screenshot_config.image_format = image_format_box_ptr->get_selected_id();

View File

@@ -18,6 +18,7 @@
#include <algorithm>
#include <cmath>
#include <string.h>
#include <charconv>
namespace gsr {
static const char *custom_app_audio_tag = "[custom]";
@@ -37,6 +38,13 @@ namespace gsr {
return "";
}
template <typename T>
static T sv_to_int(std::string_view str) {
T result = 0;
std::from_chars(str.data(), str.data() + str.size(), result);
return result;
}
SettingsPage::SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title, bool supports_window_fullscreen_state) :
StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
type(type),
@@ -65,7 +73,7 @@ namespace gsr {
}
std::unique_ptr<RadioButton> SettingsPage::create_view_radio_button() {
auto view_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
auto view_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
view_radio_button->add_item(TR("Simple view"), "simple");
view_radio_button->add_item(TR("Advanced view"), "advanced");
view_radio_button->set_horizontal_alignment(Widget::Alignment::CENTER);
@@ -74,7 +82,7 @@ namespace gsr {
}
std::unique_ptr<ComboBox> SettingsPage::create_record_area_box() {
auto record_area_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto record_area_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
// TODO: Show options not supported but disable them
if(capture_options.window)
record_area_box->add_item(TR("Window"), "window");
@@ -97,21 +105,21 @@ namespace gsr {
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, TR("Capture source:"), get_color_theme().text_color));
record_area_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Capture source:"), get_color_theme().text_color));
record_area_list->add_widget(create_record_area_box());
return record_area_list;
}
std::unique_ptr<Entry> SettingsPage::create_area_width_entry() {
auto area_width_entry = std::make_unique<Entry>(&get_theme().body_font, "1920", get_theme().body_font.get_character_size() * 3);
area_width_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
auto area_width_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "1920", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
area_width_entry->set_number_mode(true, 1, 1 << 15);
area_width_entry_ptr = area_width_entry.get();
return area_width_entry;
}
std::unique_ptr<Entry> SettingsPage::create_area_height_entry() {
auto area_height_entry = std::make_unique<Entry>(&get_theme().body_font, "1080", get_theme().body_font.get_character_size() * 3);
area_height_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
auto area_height_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "1080", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
area_height_entry->set_number_mode(true, 1, 1 << 15);
area_height_entry_ptr = area_height_entry.get();
return area_height_entry;
}
@@ -119,29 +127,29 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_area_size() {
auto area_size_params_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
area_size_params_list->add_widget(create_area_width_entry());
area_size_params_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "x", get_color_theme().text_color));
area_size_params_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), "x", get_color_theme().text_color));
area_size_params_list->add_widget(create_area_height_entry());
return area_size_params_list;
}
std::unique_ptr<List> SettingsPage::create_area_size_section() {
auto area_size_list = std::make_unique<List>(List::Orientation::VERTICAL);
area_size_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Area size:"), get_color_theme().text_color));
area_size_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Area size:"), get_color_theme().text_color));
area_size_list->add_widget(create_area_size());
area_size_list_ptr = area_size_list.get();
return area_size_list;
}
std::unique_ptr<Entry> SettingsPage::create_video_width_entry() {
auto video_width_entry = std::make_unique<Entry>(&get_theme().body_font, "1920", get_theme().body_font.get_character_size() * 3);
video_width_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
auto video_width_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "1920", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
video_width_entry->set_number_mode(true, 1, 1 << 15);
video_width_entry_ptr = video_width_entry.get();
return video_width_entry;
}
std::unique_ptr<Entry> SettingsPage::create_video_height_entry() {
auto video_height_entry = std::make_unique<Entry>(&get_theme().body_font, "1080", get_theme().body_font.get_character_size() * 3);
video_height_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
auto video_height_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "1080", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
video_height_entry->set_number_mode(true, 1, 1 << 15);
video_height_entry_ptr = video_height_entry.get();
return video_height_entry;
}
@@ -149,21 +157,21 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_video_resolution() {
auto area_size_params_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
area_size_params_list->add_widget(create_video_width_entry());
area_size_params_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "x", get_color_theme().text_color));
area_size_params_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), "x", get_color_theme().text_color));
area_size_params_list->add_widget(create_video_height_entry());
return area_size_params_list;
}
std::unique_ptr<List> SettingsPage::create_video_resolution_section() {
auto video_resolution_list = std::make_unique<List>(List::Orientation::VERTICAL);
video_resolution_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Video resolution limit:"), get_color_theme().text_color));
video_resolution_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Video resolution limit:"), get_color_theme().text_color));
video_resolution_list->add_widget(create_video_resolution());
video_resolution_list_ptr = video_resolution_list.get();
return video_resolution_list;
}
std::unique_ptr<CheckBox> SettingsPage::create_restore_portal_session_checkbox() {
auto restore_portal_session_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Restore portal session"));
auto restore_portal_session_checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Restore portal session"));
restore_portal_session_checkbox->set_checked(true);
restore_portal_session_checkbox_ptr = restore_portal_session_checkbox.get();
return restore_portal_session_checkbox;
@@ -171,14 +179,14 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_restore_portal_session_section() {
auto restore_portal_session_list = std::make_unique<List>(List::Orientation::VERTICAL);
restore_portal_session_list->add_widget(std::make_unique<Label>(&get_theme().body_font, " ", get_color_theme().text_color));
restore_portal_session_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), " ", get_color_theme().text_color));
restore_portal_session_list->add_widget(create_restore_portal_session_checkbox());
restore_portal_session_list_ptr = restore_portal_session_list.get();
return restore_portal_session_list;
}
std::unique_ptr<Widget> SettingsPage::create_change_video_resolution_section() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Change video resolution"));
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Change video resolution"));
change_video_resolution_checkbox_ptr = checkbox.get();
return checkbox;
}
@@ -199,16 +207,16 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_webcam_sources() {
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
ll->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Webcam source:"), get_color_theme().text_color));
ll->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Webcam source:"), get_color_theme().text_color));
auto combobox = std::make_unique<ComboBox>(&get_theme().body_font);
auto combobox = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
combobox->add_item(TR("None"), "");
for(const GsrCamera &camera : capture_options.cameras) {
combobox->add_item(camera.path, camera.path);
}
webcam_sources_box_ptr = combobox.get();
webcam_sources_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
webcam_sources_box_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
selected_camera = std::nullopt;
selected_camera_setup = std::nullopt;
webcam_video_format_box_ptr->clear_items();
@@ -244,16 +252,19 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_webcam_video_setups() {
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
ll->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Video setup:"), get_color_theme().text_color));
ll->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Video setup:"), get_color_theme().text_color));
auto combobox = std::make_unique<ComboBox>(&get_theme().body_font);
auto combobox = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
webcam_video_setup_box_ptr = combobox.get();
webcam_video_setup_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
webcam_video_setup_box_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
int camera_width = 0;
int camera_height = 0;
int camera_fps = 0;
sscanf(id.c_str(), "%dx%d@%dhz", &camera_width, &camera_height, &camera_fps);
char id_str[256];
snprintf(id_str, sizeof(id_str), "%.*s", (int)id.size(), id.data());
sscanf(id_str, "%dx%d@%dhz", &camera_width, &camera_height, &camera_fps);
RecordOptions &current_record_options = get_current_record_options();
current_record_options.webcam_camera_width = camera_width;
@@ -279,12 +290,12 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_webcam_video_format() {
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
ll->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Video format:"), get_color_theme().text_color));
ll->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Video format:"), get_color_theme().text_color));
auto combobox = std::make_unique<ComboBox>(&get_theme().body_font);
auto combobox = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
webcam_video_format_box_ptr = combobox.get();
webcam_video_format_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
webcam_video_format_box_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
get_current_record_options().webcam_video_format = id;
auto it = std::find_if(capture_options.cameras.begin(), capture_options.cameras.end(), [&](const GsrCamera &camera) {
@@ -393,7 +404,7 @@ namespace gsr {
{
draw_rectangle_outline(window, pos, size, mgl::Color(255, 0, 0, 255), screen_border);
mgl::Text screen_text(TR("Screen"), get_theme().camera_setup_font);
mgl::Text screen_text(TR("Screen"), get_theme().camera_setup_font_desc.c_str());
screen_text.set_position((pos + size * 0.5f - screen_text.get_bounds().size * 0.5f).floor());
window.draw(screen_text);
}
@@ -408,7 +419,7 @@ namespace gsr {
// resize_area.set_color(mgl::Color(0, 0, 255, 255));
// window.draw(resize_area);
mgl::Text webcam_text(TR("Webcam"), get_theme().camera_setup_font);
mgl::Text webcam_text(TR("Webcam"), get_theme().camera_setup_font_desc.c_str());
webcam_text.set_position((webcam_box_drawn_pos + webcam_box_drawn_size * 0.5f - webcam_text.get_bounds().size * 0.5f).floor());
window.draw(webcam_text);
}
@@ -468,7 +479,7 @@ namespace gsr {
}
std::unique_ptr<CheckBox> SettingsPage::create_flip_camera_checkbox() {
auto flip_camera_horizontally_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Flip camera horizontally"));
auto flip_camera_horizontally_checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Flip camera horizontally"));
flip_camera_horizontally_checkbox_ptr = flip_camera_horizontally_checkbox.get();
return flip_camera_horizontally_checkbox;
}
@@ -478,7 +489,7 @@ namespace gsr {
webcam_body_list_ptr = body_list.get();
body_list->set_visible(false);
body_list->add_widget(create_webcam_location_widget());
body_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("* Right click in the bottom right corner to resize the webcam"), get_color_theme().text_color));
body_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("* Right click in the bottom right corner to resize the webcam"), get_color_theme().text_color));
body_list->add_widget(create_flip_camera_checkbox());
body_list->add_widget(create_webcam_video_setup_list());
return body_list;
@@ -496,7 +507,7 @@ namespace gsr {
}
std::unique_ptr<ComboBox> SettingsPage::create_audio_device_selection_combobox(AudioDeviceType device_type) {
auto audio_device_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto audio_device_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
for(const auto &audio_device : audio_devices) {
const bool device_is_output = audio_device_is_output(audio_device.name);
if((device_type == AudioDeviceType::OUTPUT && device_is_output) || (device_type == AudioDeviceType::INPUT && !device_is_output)) {
@@ -532,7 +543,7 @@ namespace gsr {
}
std::unique_ptr<Button> SettingsPage::create_remove_audio_device_button(List *audio_input_list_ptr, List *audio_device_list_ptr) {
auto remove_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 0));
auto remove_audio_track_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 0));
remove_audio_track_button->set_icon(&get_theme().trash_texture);
remove_audio_track_button->set_icon_padding_scale(0.75f);
remove_audio_track_button->on_click = [this, audio_input_list_ptr, audio_device_list_ptr]() {
@@ -545,14 +556,14 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_audio_device(AudioDeviceType device_type, List *audio_input_list_ptr) {
auto audio_device_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
audio_device_list->userdata = (void*)(uintptr_t)AudioTrackType::DEVICE;
audio_device_list->add_widget(std::make_unique<Label>(&get_theme().body_font, device_type == AudioDeviceType::OUTPUT ? TR("Output device:") : TR("Input device: "), get_color_theme().text_color));
audio_device_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), device_type == AudioDeviceType::OUTPUT ? TR("Output device:") : TR("Input device: "), get_color_theme().text_color));
audio_device_list->add_widget(create_audio_device_selection_combobox(device_type));
audio_device_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, audio_device_list.get()));
return audio_device_list;
}
std::unique_ptr<Button> SettingsPage::create_add_audio_track_button() {
auto button = std::make_unique<Button>(&get_theme().body_font, TR("Add audio track"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Add audio track"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
button->on_click = [this]() {
audio_track_section_list_ptr->add_widget(create_audio_track_section(audio_section_ptr));
};
@@ -577,7 +588,7 @@ namespace gsr {
switch(audio_track_type) {
case AudioTrackType::DEVICE: {
Label *label = dynamic_cast<Label*>(audio_track_line->get_child_widget_by_index(0));
const bool is_output_device = starts_with(label->get_text().c_str(), TR("Output device"));
const bool is_output_device = starts_with(label->get_text(), TR("Output device"));
if(is_output_device)
num_output_devices++;
break;
@@ -597,7 +608,7 @@ namespace gsr {
}
std::unique_ptr<Button> SettingsPage::create_add_audio_output_device_button(List *audio_input_list_ptr) {
auto button = std::make_unique<Button>(&get_theme().body_font, TR("Add output device"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Add output device"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
button->on_click = [this, audio_input_list_ptr]() {
audio_devices = get_audio_devices();
audio_input_list_ptr->add_widget(create_audio_device(AudioDeviceType::OUTPUT, audio_input_list_ptr));
@@ -607,7 +618,7 @@ namespace gsr {
}
std::unique_ptr<Button> SettingsPage::create_add_audio_input_device_button(List *audio_input_list_ptr) {
auto button = std::make_unique<Button>(&get_theme().body_font, TR("Add input device"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Add input device"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
button->on_click = [this, audio_input_list_ptr]() {
audio_devices = get_audio_devices();
audio_input_list_ptr->add_widget(create_audio_device(AudioDeviceType::INPUT, audio_input_list_ptr));
@@ -616,17 +627,17 @@ namespace gsr {
}
std::unique_ptr<ComboBox> SettingsPage::create_application_audio_selection_combobox(List *application_audio_row) {
auto audio_device_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto audio_device_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
ComboBox *audio_device_box_ptr = audio_device_box.get();
for(const auto &app_audio : application_audio) {
audio_device_box->add_item(app_audio, app_audio);
}
audio_device_box->add_item(TR("Custom..."), custom_app_audio_tag);
audio_device_box->on_selection_changed = [application_audio_row, audio_device_box_ptr](const std::string&, const std::string &id) {
audio_device_box->on_selection_changed = [application_audio_row, audio_device_box_ptr](std::string_view, std::string_view id) {
if(id == custom_app_audio_tag) {
application_audio_row->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION_CUSTOM;
auto custom_app_audio_entry = std::make_unique<Entry>(&get_theme().body_font, "", (int)(get_theme().body_font.get_character_size() * 10.0f));
auto custom_app_audio_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", (int)(2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 10.0f));
application_audio_row->replace_widget(audio_device_box_ptr, std::move(custom_app_audio_entry));
}
};
@@ -637,7 +648,7 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_application_audio(List *audio_input_list_ptr) {
auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
application_audio_list->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION;
application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Application: "), get_color_theme().text_color));
application_audio_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Application: "), get_color_theme().text_color));
application_audio_list->add_widget(create_application_audio_selection_combobox(application_audio_list.get()));
application_audio_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, application_audio_list.get()));
return application_audio_list;
@@ -646,14 +657,14 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_custom_application_audio(List *audio_input_list_ptr) {
auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
application_audio_list->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION_CUSTOM;
application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Application: "), get_color_theme().text_color));
application_audio_list->add_widget(std::make_unique<Entry>(&get_theme().body_font, "", (int)(get_theme().body_font.get_character_size() * 10.0f)));
application_audio_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Application: "), get_color_theme().text_color));
application_audio_list->add_widget(std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", (int)(2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 10.0f)));
application_audio_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, application_audio_list.get()));
return application_audio_list;
}
std::unique_ptr<Button> SettingsPage::create_add_application_audio_button(List *audio_input_list_ptr) {
auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, TR("Add application audio"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
auto add_audio_track_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Add application audio"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
add_audio_track_button->on_click = [this, audio_input_list_ptr]() {
application_audio = get_application_audio();
if(application_audio.empty())
@@ -681,7 +692,7 @@ namespace gsr {
}
std::unique_ptr<CheckBox> SettingsPage::create_application_audio_invert_checkbox() {
auto application_audio_invert_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Record audio from all applications except the selected ones"));
auto application_audio_invert_checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Record audio from all applications except the selected ones"));
application_audio_invert_checkbox->set_checked(false);
application_audio_invert_checkbox->on_changed = [this](bool) {
update_application_audio_warning_visibility();
@@ -694,9 +705,9 @@ namespace gsr {
list->set_spacing(0.003f);
list->set_visible(false);
const int font_character_size = get_theme().body_font.get_character_size();
const int font_character_size = 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str());
list->add_widget(std::make_unique<Image>(&get_theme().warning_texture, mgl::vec2f(font_character_size, font_character_size), Image::ScaleBehavior::SCALE));
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Recording output devices and application audio may record all output audio, which is likely\nnot what you want to do. Remove the output devices."), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Recording output devices and application audio may record all output audio, which is likely\nnot what you want to do. Remove the output devices."), get_color_theme().text_color));
return list;
}
@@ -718,9 +729,9 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_audio_track_title_and_remove(Subsection *audio_track_subsection, const char *title) {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
list->add_widget(std::make_unique<Label>(&get_theme().title_font, title, get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().title_font_desc.c_str(), title, get_color_theme().text_color));
auto remove_track_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 0));
auto remove_track_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 0));
remove_track_button->set_icon(&get_theme().trash_texture);
remove_track_button->set_icon_padding_scale(0.75f);
remove_track_button->on_click = [this, audio_track_subsection]() {
@@ -774,9 +785,9 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_video_quality_box() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Video quality:"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Video quality:"), get_color_theme().text_color));
auto video_quality_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto video_quality_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
if(type == Type::REPLAY || type == Type::STREAM)
video_quality_box->add_item(TR("Constant bitrate (Recommended)"), "custom");
else
@@ -802,18 +813,18 @@ namespace gsr {
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, "8000", (int)(get_theme().body_font.get_character_size() * 4.0f));
video_bitrate_entry->validate_handler = create_entry_validator_integer_in_range(1, 500000);
auto video_bitrate_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "8000", (int)(2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 5));
video_bitrate_entry->set_number_mode(true, 1, 500000);
video_bitrate_entry_ptr = video_bitrate_entry.get();
list->add_widget(std::move(video_bitrate_entry));
if(type == Type::STREAM) {
auto size_mb_label = std::make_unique<Label>(&get_theme().body_font, "", get_color_theme().text_color);
auto size_mb_label = std::make_unique<Label>(get_theme().body_font_desc.c_str(), "", 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_mbits_per_seconds = (double)atoi(text.c_str()) / 1024.0;
video_bitrate_entry_ptr->on_changed = [size_mb_label_ptr](std::string_view text) {
const double video_bitrate_mbits_per_seconds = (double)sv_to_int<int64_t>(text) / 1024.0;
char buffer[32];
snprintf(buffer, sizeof(buffer), "%.2fMbps", video_bitrate_mbits_per_seconds);
size_mb_label_ptr->set_text(buffer);
@@ -825,14 +836,14 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_video_bitrate() {
auto video_bitrate_list = std::make_unique<List>(List::Orientation::VERTICAL);
video_bitrate_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Video bitrate (Kbps):"), get_color_theme().text_color));
video_bitrate_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Video bitrate (Kbps):"), get_color_theme().text_color));
video_bitrate_list->add_widget(create_video_bitrate_entry());
video_bitrate_list_ptr = video_bitrate_list.get();
return video_bitrate_list;
}
std::unique_ptr<ComboBox> SettingsPage::create_color_range_box() {
auto color_range_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto color_range_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
color_range_box->add_item(TR("Limited"), "limited");
color_range_box->add_item(TR("Full"), "full");
color_range_box_ptr = color_range_box.get();
@@ -841,7 +852,7 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_color_range() {
auto color_range_list = std::make_unique<List>(List::Orientation::VERTICAL);
color_range_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Color range:"), get_color_theme().text_color));
color_range_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Color range:"), get_color_theme().text_color));
color_range_list->add_widget(create_color_range_box());
color_range_list_ptr = color_range_list.get();
return color_range_list;
@@ -856,7 +867,7 @@ namespace gsr {
}
std::unique_ptr<ComboBox> SettingsPage::create_video_codec_box() {
auto video_codec_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto video_codec_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
// 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.
@@ -887,14 +898,14 @@ namespace gsr {
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, TR("Video codec:"), get_color_theme().text_color));
video_codec_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Video codec:"), get_color_theme().text_color));
video_codec_list->add_widget(create_video_codec_box());
video_codec_ptr = video_codec_list.get();
return video_codec_list;
}
std::unique_ptr<ComboBox> SettingsPage::create_audio_codec_box() {
auto audio_codec_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto audio_codec_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
audio_codec_box->add_item(TR("Opus (Recommended)"), "opus");
audio_codec_box->add_item(TR("AAC"), "aac");
audio_codec_box_ptr = audio_codec_box.get();
@@ -903,28 +914,28 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_audio_codec() {
auto audio_codec_list = std::make_unique<List>(List::Orientation::VERTICAL);
audio_codec_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Audio codec:"), get_color_theme().text_color));
audio_codec_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Audio codec:"), get_color_theme().text_color));
audio_codec_list->add_widget(create_audio_codec_box());
audio_codec_ptr = audio_codec_list.get();
return audio_codec_list;
}
std::unique_ptr<Entry> SettingsPage::create_framerate_entry() {
auto framerate_entry = std::make_unique<Entry>(&get_theme().body_font, "60", (int)(get_theme().body_font.get_character_size() * 2.5f));
framerate_entry->validate_handler = create_entry_validator_integer_in_range(1, 500);
auto framerate_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "60", (int)(2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 3));
framerate_entry->set_number_mode(true, 1, 500);
framerate_entry_ptr = framerate_entry.get();
return framerate_entry;
}
std::unique_ptr<List> SettingsPage::create_framerate() {
auto framerate_list = std::make_unique<List>(List::Orientation::VERTICAL);
framerate_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Frame rate:"), get_color_theme().text_color));
framerate_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Frame rate:"), get_color_theme().text_color));
framerate_list->add_widget(create_framerate_entry());
return framerate_list;
}
std::unique_ptr<ComboBox> SettingsPage::create_framerate_mode_box() {
auto framerate_mode_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto framerate_mode_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
framerate_mode_box->add_item(TR("Auto (Recommended)"), "auto");
framerate_mode_box->add_item(TR("Constant"), "cfr");
framerate_mode_box->add_item(TR("Variable"), "vfr");
@@ -938,7 +949,7 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_framerate_mode() {
auto framerate_mode_list = std::make_unique<List>(List::Orientation::VERTICAL);
framerate_mode_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Frame rate mode:"), get_color_theme().text_color));
framerate_mode_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Frame rate mode:"), get_color_theme().text_color));
framerate_mode_list->add_widget(create_framerate_mode_box());
framerate_mode_list_ptr = framerate_mode_list.get();
return framerate_mode_list;
@@ -952,7 +963,7 @@ namespace gsr {
}
std::unique_ptr<Widget> SettingsPage::create_record_cursor_section() {
auto record_cursor_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Record cursor"));
auto record_cursor_checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Record cursor"));
record_cursor_checkbox->set_checked(true);
record_cursor_checkbox_ptr = record_cursor_checkbox.get();
return record_cursor_checkbox;
@@ -989,7 +1000,7 @@ namespace gsr {
void SettingsPage::add_widgets() {
content_page_ptr->add_widget(create_settings());
record_area_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
record_area_box_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
const bool focused_selected = id == "focused";
const bool portal_selected = id == "portal";
area_size_list_ptr->set_visible(focused_selected);
@@ -1004,7 +1015,7 @@ namespace gsr {
video_resolution_list_ptr->set_visible(!focused_selected && checked);
};
video_quality_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
video_quality_box_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
const bool custom_selected = id == "custom";
video_bitrate_list_ptr->set_visible(custom_selected);
@@ -1041,15 +1052,15 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_save_directory(const char *label) {
auto save_directory_list = std::make_unique<List>(List::Orientation::VERTICAL);
save_directory_list->add_widget(std::make_unique<Label>(&get_theme().body_font, label, get_color_theme().text_color));
auto save_directory_button = std::make_unique<Button>(&get_theme().body_font, get_videos_dir().c_str(), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
save_directory_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), label, get_color_theme().text_color));
auto save_directory_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), get_videos_dir().c_str(), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
save_directory_button_ptr = save_directory_button.get();
save_directory_button->on_click = [this]() {
auto select_directory_page = std::make_unique<GsrPage>(TR("File"), TR("Settings"));
select_directory_page->add_button(TR("Save"), "save", get_color_theme().tint_color);
select_directory_page->add_button(TR("Cancel"), "cancel", get_color_theme().page_bg_color);
auto file_chooser = std::make_unique<FileChooser>(save_directory_button_ptr->get_text().c_str(), select_directory_page->get_inner_size());
auto file_chooser = std::make_unique<FileChooser>(save_directory_button_ptr->get_text(), select_directory_page->get_inner_size());
FileChooser *file_chooser_ptr = file_chooser.get();
select_directory_page->add_widget(std::move(file_chooser));
@@ -1069,7 +1080,7 @@ namespace gsr {
}
std::unique_ptr<ComboBox> SettingsPage::create_container_box() {
auto container_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto container_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
container_box->add_item("mp4", "mp4");
container_box->add_item("mkv", "matroska");
container_box->add_item("flv", "flv");
@@ -1080,7 +1091,7 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_container_section() {
auto container_list = std::make_unique<List>(List::Orientation::VERTICAL);
container_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Container:"), get_color_theme().text_color));
container_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Container:"), get_color_theme().text_color));
container_list->add_widget(create_container_box());
return container_list;
}
@@ -1088,12 +1099,12 @@ namespace gsr {
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, 86400);
auto replay_time_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "60", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
replay_time_entry->set_number_mode(true, 1, 86400);
replay_time_entry_ptr = replay_time_entry.get();
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);
auto replay_time_label = std::make_unique<Label>(get_theme().body_font_desc.c_str(), "00h:00m:00s", get_color_theme().text_color);
replay_time_label_ptr = replay_time_label.get();
list->add_widget(std::move(replay_time_label));
@@ -1102,20 +1113,20 @@ namespace gsr {
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, TR("Replay duration in seconds:"), get_color_theme().text_color));
replay_time_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("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<List> SettingsPage::create_replay_storage() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Where should temporary replay data be stored?"), get_color_theme().text_color));
auto replay_storage_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Where should temporary replay data be stored?"), get_color_theme().text_color));
auto replay_storage_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
replay_storage_button_ptr = replay_storage_button.get();
replay_storage_button->add_item(TR("RAM"), "ram");
replay_storage_button->add_item(TR("Disk (Not recommended on SSDs)"), "disk");
replay_storage_button->on_selection_changed = [this](const std::string&, const std::string &id) {
replay_storage_button->on_selection_changed = [this](std::string_view, std::string_view id) {
update_estimated_replay_file_size(id);
return true;
};
@@ -1130,7 +1141,7 @@ namespace gsr {
char fullscreen_text[256];
snprintf(fullscreen_text, sizeof(fullscreen_text), TR("Turn on replay when starting a fullscreen application%s"), supports_window_fullscreen_state ? "" : TR(" (X11 applications only)"));
auto radiobutton = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::VERTICAL);
auto radiobutton = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::VERTICAL);
radiobutton->add_item(TR("Don't turn on replay automatically"), "dont_turn_on_automatically");
radiobutton->add_item(TR("Turn on replay when this program starts"), "turn_on_at_system_startup");
radiobutton->add_item(fullscreen_text, "turn_on_at_fullscreen");
@@ -1142,26 +1153,26 @@ namespace gsr {
std::unique_ptr<CheckBox> SettingsPage::create_save_replay_in_game_folder() {
char text[256];
snprintf(text, sizeof(text), TR("Save video in a folder based on the games name%s"), supports_window_title ? "" : TR(" (X11 applications only)"));
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text);
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), text);
save_replay_in_game_folder_ptr = checkbox.get();
return checkbox;
}
std::unique_ptr<CheckBox> SettingsPage::create_restart_replay_on_save() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Restart replay on save"));
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("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);
auto label = std::make_unique<Label>(get_theme().body_font_desc.c_str(), "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_replay_file_size(const std::string &replay_storage_type) {
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;
void SettingsPage::update_estimated_replay_file_size(std::string_view replay_storage_type) {
const int64_t replay_time_seconds = sv_to_int<int64_t>(replay_time_entry_ptr->get_text());
const int64_t video_bitrate_bps = sv_to_int<int64_t>(video_bitrate_entry_ptr->get_text()) * 1000LL / 8LL;
const double video_filesize_mb = ((double)replay_time_seconds * (double)video_bitrate_bps) / 1000.0 / 1000.0 * 1.024;
char buffer[256];
@@ -1170,7 +1181,7 @@ namespace gsr {
}
void SettingsPage::update_replay_time_text() {
int seconds = atoi(replay_time_entry_ptr->get_text().c_str());
int seconds = sv_to_int<int>(replay_time_entry_ptr->get_text());
const int hours = seconds / 60 / 60;
seconds -= (hours * 60 * 60);
@@ -1206,7 +1217,7 @@ namespace gsr {
char label_str[256];
snprintf(label_str, sizeof(label_str), TR("Show %s status with scroll lock LED"), type);
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, label_str);
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), label_str);
checkbox->set_checked(false);
led_indicator_checkbox_ptr = checkbox.get();
return checkbox;
@@ -1215,7 +1226,7 @@ namespace gsr {
std::unique_ptr<CheckBox> SettingsPage::create_notifications(const char *type) {
char label_str[256];
snprintf(label_str, sizeof(label_str), TR("Show %s notifications"), type);
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, label_str);
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), label_str);
checkbox->set_checked(true);
show_notification_checkbox_ptr = checkbox.get();
return checkbox;
@@ -1232,7 +1243,7 @@ namespace gsr {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
list->set_visible(gsr_info->gpu_info.vendor == GpuVendor::AMD);
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Record in low-power mode"));
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Record in low-power mode"));
low_power_mode_checkbox_ptr = checkbox.get();
list->add_widget(std::move(checkbox));
@@ -1276,18 +1287,18 @@ namespace gsr {
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Replay indicator"), create_indicator(TR("replay")), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Autostart"), create_start_replay_automatically(), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
view_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
view_radio_button_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
view_changed(id == "advanced");
return true;
};
view_radio_button_ptr->on_selection_changed(TR("Simple"), "simple");
replay_time_entry_ptr->on_changed = [this](const std::string&) {
replay_time_entry_ptr->on_changed = [this](std::string_view) {
update_estimated_replay_file_size(replay_storage_button_ptr->get_selected_id());
update_replay_time_text();
};
video_bitrate_entry_ptr->on_changed = [this](const std::string&) {
video_bitrate_entry_ptr->on_changed = [this](std::string_view) {
update_estimated_replay_file_size(replay_storage_button_ptr->get_selected_id());
};
}
@@ -1295,19 +1306,19 @@ namespace gsr {
std::unique_ptr<CheckBox> SettingsPage::create_save_recording_in_game_folder() {
char text[256];
snprintf(text, sizeof(text), TR("Save video in a folder based on the games name%s"), supports_window_title ? "" : TR(" (X11 applications only)"));
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text);
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), text);
save_recording_in_game_folder_ptr = checkbox.get();
return checkbox;
}
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);
auto label = std::make_unique<Label>(get_theme().body_font_desc.c_str(), "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 int64_t video_bitrate_bps = sv_to_int<int64_t>(video_bitrate_entry_ptr->get_text()) * 1000LL / 8LL;
const double video_filesize_mb_per_minute = (60.0 * (double)video_bitrate_bps) / 1000.0 / 1000.0 * 1.024;
char buffer[512];
@@ -1332,19 +1343,19 @@ namespace gsr {
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("General"), std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Recording indicator"), create_indicator(TR("recording")), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
view_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
view_radio_button_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
view_changed(id == "advanced");
return true;
};
view_radio_button_ptr->on_selection_changed(TR("Simple"), "simple");
video_bitrate_entry_ptr->on_changed = [this](const std::string&) {
video_bitrate_entry_ptr->on_changed = [this](std::string_view) {
update_estimated_record_file_size();
};
}
std::unique_ptr<ComboBox> SettingsPage::create_streaming_service_box() {
auto streaming_service_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto streaming_service_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
streaming_service_box->add_item(TR("Twitch"), "twitch");
streaming_service_box->add_item(TR("YouTube"), "youtube");
streaming_service_box->add_item(TR("Rumble"), "rumble");
@@ -1356,13 +1367,13 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_streaming_service_section() {
auto streaming_service_list = std::make_unique<List>(List::Orientation::VERTICAL);
streaming_service_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Stream service:"), get_color_theme().text_color));
streaming_service_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Stream service:"), get_color_theme().text_color));
streaming_service_list->add_widget(create_streaming_service_box());
return streaming_service_list;
}
static std::unique_ptr<Button> create_mask_toggle_button(Entry *entry_to_toggle, mgl::vec2f size) {
auto button = std::make_unique<Button>(&get_theme().body_font, "", size, mgl::Color(0, 0, 0, 0));
auto button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", size, mgl::Color(0, 0, 0, 0));
Button *button_ptr = button.get();
button->set_icon(&get_theme().masked_texture);
button->on_click = [entry_to_toggle, button_ptr]() {
@@ -1375,7 +1386,7 @@ namespace gsr {
static Entry* add_stream_key_entry_to_list(List *stream_key_list) {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
auto key_entry = std::make_unique<Entry>(&get_theme().body_font, "", get_theme().body_font.get_character_size() * 20);
auto key_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 20);
key_entry->set_masked(true);
Entry *key_entry_ptr = key_entry.get();
const float mask_icon_size = key_entry_ptr->get_size().y * 0.9f;
@@ -1387,30 +1398,62 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_stream_key_section() {
auto stream_key_list = std::make_unique<List>(List::Orientation::VERTICAL);
stream_key_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Stream key:"), get_color_theme().text_color));
stream_key_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Stream key:"), get_color_theme().text_color));
twitch_stream_key_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
youtube_stream_key_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
rumble_stream_key_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
kick_stream_url_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
kick_stream_key_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
stream_key_list_ptr = stream_key_list.get();
return stream_key_list;
}
std::unique_ptr<List> SettingsPage::create_stream_kick_url() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
auto stream_url_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 20);
kick_stream_url_entry_ptr = stream_url_entry.get();
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Stream URL:"), get_color_theme().text_color));
list->add_widget(std::move(stream_url_entry));
return list;
}
std::unique_ptr<List> SettingsPage::create_stream_kick_key() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
auto stream_key_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 20);
stream_key_entry->set_masked(true);
kick_stream_key_entry_ptr = stream_key_entry.get();
const float mask_icon_size = kick_stream_key_entry_ptr->get_size().y * 1.0f;
list->add_widget(std::move(stream_key_entry));
list->add_widget(create_mask_toggle_button(kick_stream_key_entry_ptr, mgl::vec2f(mask_icon_size, mask_icon_size)));
return list;
}
std::unique_ptr<List> SettingsPage::create_stream_kick_section() {
auto kick_stream_list = std::make_unique<List>(List::Orientation::VERTICAL);
auto stream_url_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
stream_url_list->add_widget(create_stream_kick_url());
kick_stream_list->add_widget(std::move(stream_url_list));
kick_stream_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Stream key:"), get_color_theme().text_color));
kick_stream_list->add_widget(create_stream_kick_key());
kick_stream_list_ptr = kick_stream_list.get();
return kick_stream_list;
}
std::unique_ptr<List> SettingsPage::create_stream_custom_url() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
auto stream_url_entry = std::make_unique<Entry>(&get_theme().body_font, "", get_theme().body_font.get_character_size() * 20);
auto stream_url_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 20);
stream_url_entry_ptr = stream_url_entry.get();
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Stream URL:"), get_color_theme().text_color));
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Stream URL:"), get_color_theme().text_color));
list->add_widget(std::move(stream_url_entry));
return list;
}
std::unique_ptr<List> SettingsPage::create_stream_custom_key() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
auto stream_key_entry = std::make_unique<Entry>(&get_theme().body_font, "", get_theme().body_font.get_character_size() * 20);
auto stream_key_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 20);
stream_key_entry->set_masked(true);
stream_key_entry_ptr = stream_key_entry.get();
const float mask_icon_size = stream_key_entry_ptr->get_size().y * 0.9f;
@@ -1427,7 +1470,7 @@ namespace gsr {
stream_url_list->add_widget(create_stream_container());
custom_stream_list->add_widget(std::move(stream_url_list));
custom_stream_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Stream key:"), get_color_theme().text_color));
custom_stream_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Stream key:"), get_color_theme().text_color));
custom_stream_list->add_widget(create_stream_custom_key());
custom_stream_list_ptr = custom_stream_list.get();
@@ -1435,7 +1478,7 @@ namespace gsr {
}
std::unique_ptr<ComboBox> SettingsPage::create_stream_container_box() {
auto container_box = std::make_unique<ComboBox>(&get_theme().body_font);
auto container_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
container_box->add_item("mp4", "mp4");
container_box->add_item("flv", "flv");
container_box->add_item("ts", "mpegts");
@@ -1446,7 +1489,7 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_stream_container() {
auto container_list = std::make_unique<List>(List::Orientation::VERTICAL);
container_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Container:"), get_color_theme().text_color));
container_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Container:"), get_color_theme().text_color));
container_list->add_widget(create_stream_container_box());
return container_list;
}
@@ -1455,6 +1498,7 @@ namespace gsr {
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());
streaming_info_list->add_widget(create_stream_kick_section());
streaming_info_list->add_widget(create_stream_custom_section());
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Streaming info"), std::move(streaming_info_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
@@ -1465,24 +1509,23 @@ namespace gsr {
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("General"), std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Streaming indicator"), create_indicator(TR("streaming")), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
streaming_service_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
streaming_service_box_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
const bool twitch_option = id == "twitch";
const bool youtube_option = id == "youtube";
const bool rumble_option = id == "rumble";
const bool kick_option = id == "kick";
const bool custom_option = id == "custom";
stream_key_list_ptr->set_visible(!custom_option);
stream_key_list_ptr->set_visible(!custom_option && !kick_option);
custom_stream_list_ptr->set_visible(custom_option);
kick_stream_list_ptr->set_visible(kick_option);
twitch_stream_key_entry_ptr->get_parent_widget()->set_visible(twitch_option);
youtube_stream_key_entry_ptr->get_parent_widget()->set_visible(youtube_option);
rumble_stream_key_entry_ptr->get_parent_widget()->set_visible(rumble_option);
kick_stream_url_entry_ptr->get_parent_widget()->set_visible(kick_option);
kick_stream_key_entry_ptr->get_parent_widget()->set_visible(kick_option);
return true;
};
streaming_service_box_ptr->on_selection_changed("Twitch", "twitch");
view_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
view_radio_button_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
view_changed(id == "advanced");
return true;
};
@@ -1715,17 +1758,17 @@ namespace gsr {
switch(audio_track_type) {
case AudioTrackType::DEVICE: {
ComboBox *audio_device_box = dynamic_cast<ComboBox*>(audio_track_line->get_child_widget_by_index(1));
audio_tracks.back().audio_inputs.push_back("device:" + audio_device_box->get_selected_id());
audio_tracks.back().audio_inputs.push_back("device:" + std::string(audio_device_box->get_selected_id()));
break;
}
case AudioTrackType::APPLICATION: {
ComboBox *application_audio_box = dynamic_cast<ComboBox*>(audio_track_line->get_child_widget_by_index(1));
audio_tracks.back().audio_inputs.push_back("app:" + application_audio_box->get_selected_id());
audio_tracks.back().audio_inputs.push_back("app:" + std::string(application_audio_box->get_selected_id()));
break;
}
case AudioTrackType::APPLICATION_CUSTOM: {
Entry *application_audio_entry = dynamic_cast<Entry*>(audio_track_line->get_child_widget_by_index(1));
audio_tracks.back().audio_inputs.push_back("app:" + application_audio_entry->get_text());
audio_tracks.back().audio_inputs.push_back("app:" + std::string(application_audio_entry->get_text()));
break;
}
}
@@ -1737,13 +1780,14 @@ namespace gsr {
}
void SettingsPage::save_common(RecordOptions &record_options) {
std::stoi("23");
record_options.record_area_option = record_area_box_ptr->get_selected_id();
record_options.record_area_width = atoi(area_width_entry_ptr->get_text().c_str());
record_options.record_area_height = atoi(area_height_entry_ptr->get_text().c_str());
record_options.video_width = atoi(video_width_entry_ptr->get_text().c_str());
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.record_area_width = sv_to_int<int32_t>(area_width_entry_ptr->get_text());
record_options.record_area_height = sv_to_int<int32_t>(area_height_entry_ptr->get_text());
record_options.video_width = sv_to_int<int32_t>(video_width_entry_ptr->get_text());
record_options.video_height = sv_to_int<int32_t>(video_height_entry_ptr->get_text());
record_options.fps = sv_to_int<int32_t>(framerate_entry_ptr->get_text());
record_options.video_bitrate = sv_to_int<int32_t>(video_bitrate_entry_ptr->get_text());
record_options.change_video_resolution = change_video_resolution_checkbox_ptr->is_checked();
save_audio_tracks(record_options.audio_tracks_list, audio_track_section_list_ptr);
record_options.color_range = color_range_box_ptr->get_selected_id();
@@ -1823,7 +1867,7 @@ namespace gsr {
config.replay_config.restart_replay_on_save = restart_replay_on_save->is_checked();
config.replay_config.save_directory = save_directory_button_ptr->get_text();
config.replay_config.container = container_box_ptr->get_selected_id();
config.replay_config.replay_time = atoi(replay_time_entry_ptr->get_text().c_str());
config.replay_config.replay_time = sv_to_int<int32_t>(replay_time_entry_ptr->get_text());
config.replay_config.replay_storage = replay_storage_button_ptr->get_selected_id();
if(config.replay_config.replay_time < 5) {

View File

@@ -12,7 +12,7 @@ namespace gsr {
static const float title_spacing_scale = 0.010f;
Subsection::Subsection(const char *title, std::unique_ptr<Widget> inner_widget, mgl::vec2f size) :
label(&get_theme().title_font, title ? title : "", get_color_theme().text_color),
label(get_theme().title_font_desc.c_str(), title ? title : "", get_color_theme().text_color),
inner_widget(std::move(inner_widget)),
size(size)
{

View File

@@ -12,7 +12,7 @@ namespace gsr {
static const float padding_right_scale = 0.008f;
static const float accent_scale = 0.0025f;
Tooltip::Tooltip(mgl::Font *font) : label("", *font) {}
Tooltip::Tooltip(const char *font_desc) : label("", font_desc) {}
bool Tooltip::on_event(mgl::Event&, mgl::Window&, mgl::vec2f) {
return true;
@@ -27,7 +27,7 @@ namespace gsr {
const int padding_top = get_theme().window_height * padding_top_scale;
const int padding_left = get_theme().window_height * padding_left_scale;
const int accent_height = get_theme().window_height * accent_scale;
const int icon_height = label.get_font()->get_character_size();
const int icon_height = label.get_font_size()*2.0f;
mgl::Rectangle background(get_size());
background.set_position(draw_pos - mgl::vec2f(0.0f, background.get_size().y));
@@ -56,12 +56,12 @@ namespace gsr {
const int padding_right = get_theme().window_height * padding_right_scale;
const int accent_height = get_theme().window_height * accent_scale;
const mgl::vec2f text_size = label.get_bounds().size.floor();
const int icon_height = label.get_font()->get_character_size();
const int icon_height = label.get_font_size()*2.0f;
return mgl::vec2f(padding_left + text_size.x + padding_right, accent_height + padding_top + icon_height + text_size.y + padding_bottom).floor();
}
void Tooltip::set_text(std::string text) {
label.set_string(std::move(text));
void Tooltip::set_text(std::string_view text) {
label.set_string(text);
}
}

View File

@@ -128,7 +128,7 @@ namespace gsr {
if(widget && !widget->get_tooltip_text().empty()) {
current_tooltip_widget = widget;
if(!tooltip)
tooltip = std::make_unique<Tooltip>(&get_theme().body_font);
tooltip = std::make_unique<Tooltip>(get_theme().body_font_desc.c_str());
tooltip->set_text(current_tooltip_widget->get_tooltip_text());
} else {
current_tooltip_widget = nullptr;