diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 355726d0a3..bce53588b7 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -410,6 +410,7 @@ target_sources(rpcs3_emu PRIVATE RSX/Common/texture_cache.cpp RSX/Null/NullGSRender.cpp RSX/Overlays/overlay_animation.cpp + RSX/Overlays/overlay_controls.cpp RSX/Overlays/overlay_edit_text.cpp RSX/Overlays/overlay_fonts.cpp RSX/Overlays/overlay_list_view.cpp diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp b/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp new file mode 100644 index 0000000000..083f71a371 --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp @@ -0,0 +1,831 @@ +#include "stdafx.h" +#include "overlay_controls.h" + +#include "util/types.hpp" +#include "util/logs.hpp" +#include "Utilities/geometry.h" +#include "Utilities/File.h" + +#ifndef _WIN32 +#include +#include + +#ifdef __APPLE__ +#include +#endif + +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) +#include +#endif +#endif + +// Definitions for common UI controls and their routines +namespace rsx +{ + namespace overlays + { + image_info::image_info(const char* filename) + { + if (!fs::is_file(filename)) + { + rsx_log.error("Image resource file `%s' not found", filename); + return; + } + + std::vector bytes; + fs::file f(filename); + f.read(bytes, f.size()); + data = stbi_load_from_memory(bytes.data(), ::narrow(f.size()), &w, &h, &bpp, STBI_rgb_alpha); + } + + image_info::image_info(const std::vector& bytes) + { + data = stbi_load_from_memory(bytes.data(), ::narrow(bytes.size()), &w, &h, &bpp, STBI_rgb_alpha); + } + + image_info::~image_info() + { + stbi_image_free(data); + data = nullptr; + w = h = bpp = 0; + } + + resource_config::resource_config() + { + texture_resource_files.emplace_back("fade_top.png"); + texture_resource_files.emplace_back("fade_bottom.png"); + texture_resource_files.emplace_back("select.png"); + texture_resource_files.emplace_back("start.png"); + texture_resource_files.emplace_back("cross.png"); + texture_resource_files.emplace_back("circle.png"); + texture_resource_files.emplace_back("triangle.png"); + texture_resource_files.emplace_back("square.png"); + texture_resource_files.emplace_back("L1.png"); + texture_resource_files.emplace_back("R1.png"); + texture_resource_files.emplace_back("L2.png"); + texture_resource_files.emplace_back("R2.png"); + texture_resource_files.emplace_back("save.png"); + texture_resource_files.emplace_back("new.png"); + } + + void resource_config::load_files() + { + for (const auto &res : texture_resource_files) + { + // First check the global config dir + auto info = std::make_unique((fs::get_config_dir() + "Icons/ui/" + res).c_str()); + + if (info->data == nullptr) + { + // Resource was not found in config dir, try and grab from relative path (linux) + auto src = "Icons/ui/" + res; + info = std::make_unique(src.c_str()); +#ifndef _WIN32 + // Check for Icons in ../share/rpcs3 for AppImages, + // in rpcs3.app/Contents/Resources for App Bundles, and /usr/bin. + if (info->data == nullptr) + { + char result[ PATH_MAX ]; +#if defined(__APPLE__) + u32 bufsize = PATH_MAX; + bool success = _NSGetExecutablePath( result, &bufsize ) == 0; +#elif defined(KERN_PROC_PATHNAME) + usz bufsize = PATH_MAX; + int mib[] = { + CTL_KERN, +#if defined(__NetBSD__) + KERN_PROC_ARGS, + -1, + KERN_PROC_PATHNAME, +#else + KERN_PROC, + KERN_PROC_PATHNAME, + -1, +#endif + }; + bool success = sysctl(mib, sizeof(mib)/sizeof(mib[0]), result, &bufsize, NULL, 0) >= 0; +#elif defined(__linux__) + bool success = readlink( "/proc/self/exe", result, PATH_MAX ) >= 0; +#elif defined(__sun) + bool success = readlink( "/proc/self/path/a.out", result, PATH_MAX ) >= 0; +#else + bool success = readlink( "/proc/curproc/file", result, PATH_MAX ) >= 0; +#endif + if (success) + { + std::string executablePath = dirname(result); +#ifdef __APPLE__ + src = executablePath + "/../Resources/Icons/ui/" + res; +#else + src = executablePath + "/../share/rpcs3/Icons/ui/" + res; +#endif + info = std::make_unique(src.c_str()); + // Check if the icons are in the same directory as the executable (local builds) + if (info->data == nullptr) + { + src = executablePath + "/Icons/ui/" + res; + info = std::make_unique(src.c_str()); + } + } + } +#endif + if (info->data != nullptr) + { + // Install the image to config dir + auto dst_dir = fs::get_config_dir() + "Icons/ui/"; + auto dst = dst_dir + res; + + if (!fs::is_dir(dst_dir)) + { + auto root_folder = fs::get_config_dir() + "Icons/"; + if (!fs::is_dir(root_folder)) + fs::create_dir(root_folder); + + fs::create_dir(dst_dir); + } + + fs::copy_file(src, dst, true); + } + } + + texture_raw_data.push_back(std::move(info)); + } + } + + void resource_config::free_resources() + { + texture_raw_data.clear(); + } + + void compiled_resource::command_config::set_image_resource(u8 ref) + { + texture_ref = ref; + font_ref = nullptr; + } + + void compiled_resource::command_config::set_font(font *ref) + { + texture_ref = image_resource_id::font_file; + font_ref = ref; + } + + f32 compiled_resource::command_config::get_sinus_value() const + { + return (static_cast(get_system_time() / 1000) * pulse_speed_modifier) - pulse_sinus_offset; + } + + void compiled_resource::add(const compiled_resource& other) + { + auto old_size = draw_commands.size(); + draw_commands.resize(old_size + other.draw_commands.size()); + std::copy(other.draw_commands.begin(), other.draw_commands.end(), draw_commands.begin() + old_size); + } + + void compiled_resource::add(const compiled_resource& other, f32 x_offset, f32 y_offset) + { + auto old_size = draw_commands.size(); + draw_commands.resize(old_size + other.draw_commands.size()); + std::copy(other.draw_commands.begin(), other.draw_commands.end(), draw_commands.begin() + old_size); + + for (usz n = old_size; n < draw_commands.size(); ++n) + { + for (auto &v : draw_commands[n].verts) + { + v += vertex(x_offset, y_offset, 0.f, 0.f); + } + } + } + + void compiled_resource::add(const compiled_resource& other, f32 x_offset, f32 y_offset, const areaf& clip_rect) + { + auto old_size = draw_commands.size(); + draw_commands.resize(old_size + other.draw_commands.size()); + std::copy(other.draw_commands.begin(), other.draw_commands.end(), draw_commands.begin() + old_size); + + for (usz n = old_size; n < draw_commands.size(); ++n) + { + for (auto &v : draw_commands[n].verts) + { + v += vertex(x_offset, y_offset, 0.f, 0.f); + } + + draw_commands[n].config.clip_rect = clip_rect; + draw_commands[n].config.clip_region = true; + } + } + + void compiled_resource::clear() + { + draw_commands.clear(); + } + + compiled_resource::command& compiled_resource::append(const command& new_command) + { + draw_commands.emplace_back(new_command); + return draw_commands.back(); + } + + compiled_resource::command& compiled_resource::prepend(const command& new_command) + { + draw_commands.emplace(draw_commands.begin(), new_command); + return draw_commands.front(); + } + + void overlay_element::set_sinus_offset(f32 sinus_modifier) + { + if (sinus_modifier >= 0) + { + static const f32 PI = 3.14159265f; + const f32 pulse_sinus_x = static_cast(get_system_time() / 1000) * pulse_speed_modifier; + pulse_sinus_offset = fmod(pulse_sinus_x + sinus_modifier * PI, 2.0f * PI); + } + } + + void overlay_element::refresh() + { + // Just invalidate for draw when get_compiled() is called + is_compiled = false; + } + + void overlay_element::translate(s16 _x, s16 _y) + { + x = static_cast(x + _x); + y = static_cast(y + _y); + + is_compiled = false; + } + + void overlay_element::scale(f32 _x, f32 _y, bool origin_scaling) + { + if (origin_scaling) + { + x = static_cast(_x * x); + y = static_cast(_y * y); + } + + w = static_cast(_x * w); + h = static_cast(_y * h); + + is_compiled = false; + } + + void overlay_element::set_pos(u16 _x, u16 _y) + { + x = _x; + y = _y; + + is_compiled = false; + } + + void overlay_element::set_size(u16 _w, u16 _h) + { + w = _w; + h = _h; + + is_compiled = false; + } + + void overlay_element::set_padding(u16 left, u16 right, u16 top, u16 bottom) + { + padding_left = left; + padding_right = right; + padding_top = top; + padding_bottom = bottom; + + is_compiled = false; + } + + void overlay_element::set_padding(u16 padding) + { + padding_left = padding_right = padding_top = padding_bottom = padding; + is_compiled = false; + } + + // NOTE: Functions as a simple position offset. Top left corner is the anchor. + void overlay_element::set_margin(u16 left, u16 top) + { + margin_left = left; + margin_top = top; + + is_compiled = false; + } + + void overlay_element::set_margin(u16 margin) + { + margin_left = margin_top = margin; + is_compiled = false; + } + + void overlay_element::set_text(const std::string& text) + { + this->text = utf8_to_u32string(text); + is_compiled = false; + } + + void overlay_element::set_unicode_text(const std::u32string& text) + { + this->text = text; + is_compiled = false; + } + + void overlay_element::set_text(localized_string_id id) + { + set_unicode_text(get_localized_u32string(id)); + } + + void overlay_element::set_font(const char* font_name, u16 font_size) + { + font_ref = fontmgr::get(font_name, font_size); + is_compiled = false; + } + + void overlay_element::align_text(text_align align) + { + alignment = align; + is_compiled = false; + } + + void overlay_element::set_wrap_text(bool state) + { + wrap_text = state; + is_compiled = false; + } + + font* overlay_element::get_font() const + { + return font_ref ? font_ref : fontmgr::get("Arial", 12); + } + + std::vector overlay_element::render_text(const char32_t *string, f32 x, f32 y) + { + auto renderer = get_font(); + + f32 text_extents_w = 0.f; + u16 clip_width = clip_text ? w : umax; + std::vector result = renderer->render_text(string, clip_width, wrap_text); + + if (!result.empty()) + { + for (auto &v : result) + { + // Check for real text region extent + // TODO: Ellipsis + text_extents_w = std::max(v.values[0], text_extents_w); + + // Apply transform. + // (0, 0) has text sitting one line off the top left corner (text is outside the rect) hence the offset by text height + v.values[0] += x + padding_left; + v.values[1] += y + padding_top + static_cast(renderer->get_size_px()); + } + + if (alignment != text_align::left) + { + // Scan for lines and measure them + // Reposition them to the center or right depending on the alignment + std::vector> lines; + u32 line_begin = 0; + u32 ctr = 0; + + for (auto c : text) + { + switch (c) + { + case '\r': + continue; + case '\n': + lines.emplace_back(line_begin, ctr); + line_begin = ctr; + continue; + default: + ctr += 4; + } + } + + lines.emplace_back(line_begin, ctr); + const auto max_region_w = std::max(text_extents_w, w); + const f32 offset_extent = (alignment == text_align::center ? 0.5f : 1.0f); + + for (auto p : lines) + { + if (p.first >= p.second) + continue; + + const f32 line_length = result[p.second - 1].values[0] - result[p.first].values[0]; + const bool wrapped = std::fabs(result[p.second - 1].values[1] - result[p.first + 3].values[1]) >= (renderer->get_size_px() * 0.5f); + + if (wrapped) + continue; + + if (line_length < max_region_w) + { + const f32 offset = (max_region_w - line_length) * offset_extent; + for (auto n = p.first; n < p.second; ++n) + { + result[n].values[0] += offset; + } + } + } + } + } + + return result; + } + + compiled_resource& overlay_element::get_compiled() + { + if (!is_compiled) + { + compiled_resources.clear(); + + compiled_resource compiled_resources_temp = {}; + auto& cmd_bg = compiled_resources_temp.append({}); + auto& config = cmd_bg.config; + + config.color = back_color; + config.pulse_glow = pulse_effect_enabled; + config.pulse_sinus_offset = pulse_sinus_offset; + config.pulse_speed_modifier = pulse_speed_modifier; + + auto& verts = compiled_resources_temp.draw_commands.front().verts; + verts.resize(4); + + verts[0].vec4(x, y, 0.f, 0.f); + verts[1].vec4(f32(x + w), y, 1.f, 0.f); + verts[2].vec4(x, f32(y + h), 0.f, 1.f); + verts[3].vec4(f32(x + w), f32(y + h), 1.f, 1.f); + + compiled_resources.add(std::move(compiled_resources_temp), margin_left, margin_top); + + if (!text.empty()) + { + compiled_resources_temp.clear(); + auto& cmd_text = compiled_resources_temp.append({}); + + cmd_text.config.set_font(font_ref ? font_ref : fontmgr::get("Arial", 12)); + cmd_text.config.color = fore_color; + cmd_text.verts = render_text(text.c_str(), static_cast(x), static_cast(y)); + + if (!cmd_text.verts.empty()) + compiled_resources.add(std::move(compiled_resources_temp), margin_left, margin_top); + } + + is_compiled = true; + } + + return compiled_resources; + } + + void overlay_element::measure_text(u16& width, u16& height, bool ignore_word_wrap) const + { + if (text.empty()) + { + width = height = 0; + return; + } + + auto renderer = get_font(); + + f32 text_width = 0.f; + f32 unused = 0.f; + f32 max_w = 0.f; + f32 last_word = 0.f; + height = static_cast(renderer->get_size_px()); + + for (auto c : text) + { + if (c == '\n') + { + height += static_cast(renderer->get_size_px() + 2); + max_w = std::max(max_w, text_width); + text_width = 0.f; + last_word = 0.f; + continue; + } + + if (c == ' ') + { + last_word = text_width; + } + + renderer->get_char(c, text_width, unused); + + if (!ignore_word_wrap && wrap_text && text_width >= w) + { + if ((text_width - last_word) < w) + { + max_w = std::max(max_w, last_word); + text_width -= (last_word + renderer->get_em_size()); + height += static_cast(renderer->get_size_px() + 2); + } + } + } + + max_w = std::max(max_w, text_width); + width = static_cast(ceilf(max_w)); + } + + layout_container::layout_container() + { + // Transparent by default + back_color.a = 0.f; + } + + void layout_container::translate(s16 _x, s16 _y) + { + overlay_element::translate(_x, _y); + + for (auto &itm : m_items) + itm->translate(_x, _y); + } + + void layout_container::set_pos(u16 _x, u16 _y) + { + s16 dx = static_cast(_x - x); + s16 dy = static_cast(_y - y); + translate(dx, dy); + } + + compiled_resource& layout_container::get_compiled() + { + if (!is_compiled) + { + compiled_resource result = overlay_element::get_compiled(); + + for (auto &itm : m_items) + result.add(itm->get_compiled()); + + compiled_resources = result; + } + + return compiled_resources; + } + + overlay_element* vertical_layout::add_element(std::unique_ptr& item, int offset) + { + if (auto_resize) + { + item->set_pos(item->x + x, h + pack_padding + y); + h += item->h + pack_padding; + w = std::max(w, item->w); + } + else + { + item->set_pos(item->x + x, advance_pos + pack_padding + y); + advance_pos += item->h + pack_padding; + } + + if (offset < 0) + { + m_items.push_back(std::move(item)); + return m_items.back().get(); + } + else + { + auto result = item.get(); + m_items.insert(m_items.begin() + offset, std::move(item)); + return result; + } + } + + compiled_resource& vertical_layout::get_compiled() + { + if (scroll_offset_value == 0 && auto_resize) + return layout_container::get_compiled(); + + if (!is_compiled) + { + compiled_resource result = overlay_element::get_compiled(); + const f32 global_y_offset = static_cast(-scroll_offset_value); + + for (auto &item : m_items) + { + const s32 item_y_limit = s32{item->y} + item->h - scroll_offset_value - y; + const s32 item_y_base = s32{item->y} - scroll_offset_value - y; + + if (item_y_limit < 0 || item_y_base > h) + { + // Out of bounds + continue; + } + else if (item_y_limit > h || item_y_base < 0) + { + // Partial render + areaf clip_rect = static_cast(areai{x, y, (x + w), (y + h)}); + result.add(item->get_compiled(), 0.f, global_y_offset, clip_rect); + } + else + { + // Normal + result.add(item->get_compiled(), 0.f, global_y_offset); + } + } + + compiled_resources = result; + } + + return compiled_resources; + } + + u16 vertical_layout::get_scroll_offset_px() + { + return scroll_offset_value; + } + + overlay_element* horizontal_layout::add_element(std::unique_ptr& item, int offset) + { + if (auto_resize) + { + item->set_pos(w + pack_padding + x, item->y + y); + w += item->w + pack_padding; + h = std::max(h, item->h); + } + else + { + item->set_pos(advance_pos + pack_padding + x, item->y + y); + advance_pos += item->w + pack_padding; + } + + if (offset < 0) + { + m_items.push_back(std::move(item)); + return m_items.back().get(); + } + else + { + auto result = item.get(); + m_items.insert(m_items.begin() + offset, std::move(item)); + return result; + } + } + + compiled_resource& horizontal_layout::get_compiled() + { + if (scroll_offset_value == 0 && auto_resize) + return layout_container::get_compiled(); + + if (!is_compiled) + { + compiled_resource result = overlay_element::get_compiled(); + const f32 global_x_offset = static_cast(-scroll_offset_value); + + for (auto &item : m_items) + { + const s32 item_x_limit = s32{item->x} + item->w - scroll_offset_value - w; + const s32 item_x_base = s32{item->x} - scroll_offset_value - w; + + if (item_x_limit < 0 || item_x_base > h) + { + // Out of bounds + continue; + } + else if (item_x_limit > h || item_x_base < 0) + { + // Partial render + areaf clip_rect = static_cast(areai{x, y, (x + w), (y + h)}); + result.add(item->get_compiled(), global_x_offset, 0.f, clip_rect); + } + else + { + // Normal + result.add(item->get_compiled(), global_x_offset, 0.f); + } + } + + compiled_resources = result; + } + + return compiled_resources; + } + + u16 horizontal_layout::get_scroll_offset_px() + { + return scroll_offset_value; + } + + compiled_resource& image_view::get_compiled() + { + if (!is_compiled) + { + auto& result = overlay_element::get_compiled(); + auto& cmd_img = result.draw_commands.front(); + + cmd_img.config.set_image_resource(image_resource_ref); + cmd_img.config.color = fore_color; + cmd_img.config.external_data_ref = external_ref; + + cmd_img.config.blur_strength = blur_strength; + + // Make padding work for images (treat them as the content instead of the 'background') + auto& verts = cmd_img.verts; + + verts[0] += vertex(padding_left, padding_bottom, 0, 0); + verts[1] += vertex(-padding_right, padding_bottom, 0, 0); + verts[2] += vertex(padding_left, -padding_top, 0, 0); + verts[3] += vertex(-padding_right, -padding_top, 0, 0); + + is_compiled = true; + } + + return compiled_resources; + } + + void image_view::set_image_resource(u8 resource_id) + { + image_resource_ref = resource_id; + external_ref = nullptr; + } + + void image_view::set_raw_image(image_info* raw_image) + { + image_resource_ref = image_resource_id::raw_image; + external_ref = raw_image; + } + + void image_view::clear_image() + { + image_resource_ref = image_resource_id::none; + external_ref = nullptr; + } + + void image_view::set_blur_strength(u8 strength) + { + blur_strength = strength; + } + + image_button::image_button() + { + // Do not clip text to region extents + // TODO: Define custom clipping region or use two controls to emulate + clip_text = false; + } + + image_button::image_button(u16 _w, u16 _h) + { + clip_text = false; + set_size(_w, _h); + } + + void image_button::set_text_vertical_adjust(s16 offset) + { + m_text_offset_y = offset; + } + + void image_button::set_size(u16 /*w*/, u16 h) + { + image_view::set_size(h, h); + m_text_offset_x = (h / 2) + text_horizontal_offset; // By default text is at the horizontal center + } + + compiled_resource& image_button::get_compiled() + { + if (!is_compiled) + { + auto& compiled = image_view::get_compiled(); + for (auto& cmd : compiled.draw_commands) + { + if (cmd.config.texture_ref == image_resource_id::font_file) + { + // Text, translate geometry to the right + for (auto &v : cmd.verts) + { + v.values[0] += m_text_offset_x; + v.values[1] += m_text_offset_y; + } + } + } + } + + return compiled_resources; + } + + label::label(const std::string& text) + { + set_text(text); + } + + bool label::auto_resize(bool grow_only, u16 limit_w, u16 limit_h) + { + u16 new_width, new_height; + u16 old_width = w, old_height = h; + measure_text(new_width, new_height, true); + + new_width += padding_left + padding_right; + new_height += padding_top + padding_bottom; + + if (new_width > limit_w && wrap_text) + measure_text(new_width, new_height, false); + + if (grow_only) + { + new_width = std::max(w, new_width); + new_height = std::max(h, new_height); + } + + w = std::min(new_width, limit_w); + h = std::min(new_height, limit_h); + + bool size_changed = old_width != new_width || old_height != new_height; + return size_changed; + } + } +} diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.h b/rpcs3/Emu/RSX/Overlays/overlay_controls.h index 7102e0eb60..6570379ba8 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_controls.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.h @@ -1,34 +1,11 @@ #pragma once -#include "util/types.hpp" -#include "util/logs.hpp" -#include "Utilities/geometry.h" -#include "Utilities/File.h" -#include "overlay_utils.h" #include "overlay_fonts.h" #include "Emu/localized_string.h" #include "Emu/Cell/timers.hpp" -#include -#include #include -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#endif - -#ifdef __APPLE__ -#include -#endif - -#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) -#include -#endif // Definitions for common UI controls and their routines namespace rsx @@ -60,32 +37,9 @@ namespace rsx u8* data = nullptr; image_info(image_info&) = delete; - - image_info(const char* filename) - { - if (!fs::is_file(filename)) - { - rsx_log.error("Image resource file `%s' not found", filename); - return; - } - - std::vector bytes; - fs::file f(filename); - f.read(bytes, f.size()); - data = stbi_load_from_memory(bytes.data(), ::narrow(f.size()), &w, &h, &bpp, STBI_rgb_alpha); - } - - image_info(const std::vector& bytes) - { - data = stbi_load_from_memory(bytes.data(), ::narrow(bytes.size()), &w, &h, &bpp, STBI_rgb_alpha); - } - - ~image_info() - { - stbi_image_free(data); - data = nullptr; - w = h = bpp = 0; - } + image_info(const char* filename); + image_info(const std::vector& bytes); + ~image_info(); }; struct resource_config @@ -112,112 +66,10 @@ namespace rsx std::vector texture_resource_files; std::vector> texture_raw_data; - resource_config() - { - texture_resource_files.emplace_back("fade_top.png"); - texture_resource_files.emplace_back("fade_bottom.png"); - texture_resource_files.emplace_back("select.png"); - texture_resource_files.emplace_back("start.png"); - texture_resource_files.emplace_back("cross.png"); - texture_resource_files.emplace_back("circle.png"); - texture_resource_files.emplace_back("triangle.png"); - texture_resource_files.emplace_back("square.png"); - texture_resource_files.emplace_back("L1.png"); - texture_resource_files.emplace_back("R1.png"); - texture_resource_files.emplace_back("L2.png"); - texture_resource_files.emplace_back("R2.png"); - texture_resource_files.emplace_back("save.png"); - texture_resource_files.emplace_back("new.png"); - } + resource_config(); - void load_files() - { - for (const auto &res : texture_resource_files) - { - // First check the global config dir - auto info = std::make_unique((fs::get_config_dir() + "Icons/ui/" + res).c_str()); - - if (info->data == nullptr) - { - // Resource was not found in config dir, try and grab from relative path (linux) - auto src = "Icons/ui/" + res; - info = std::make_unique(src.c_str()); -#ifndef _WIN32 - // Check for Icons in ../share/rpcs3 for AppImages, - // in rpcs3.app/Contents/Resources for App Bundles, and /usr/bin. - if (info->data == nullptr) - { - char result[ PATH_MAX ]; -#if defined(__APPLE__) - u32 bufsize = PATH_MAX; - bool success = _NSGetExecutablePath( result, &bufsize ) == 0; -#elif defined(KERN_PROC_PATHNAME) - usz bufsize = PATH_MAX; - int mib[] = { - CTL_KERN, -#if defined(__NetBSD__) - KERN_PROC_ARGS, - -1, - KERN_PROC_PATHNAME, -#else - KERN_PROC, - KERN_PROC_PATHNAME, - -1, -#endif - }; - bool success = sysctl(mib, sizeof(mib)/sizeof(mib[0]), result, &bufsize, NULL, 0) >= 0; -#elif defined(__linux__) - bool success = readlink( "/proc/self/exe", result, PATH_MAX ) >= 0; -#elif defined(__sun) - bool success = readlink( "/proc/self/path/a.out", result, PATH_MAX ) >= 0; -#else - bool success = readlink( "/proc/curproc/file", result, PATH_MAX ) >= 0; -#endif - if (success) - { - std::string executablePath = dirname(result); -#ifdef __APPLE__ - src = executablePath + "/../Resources/Icons/ui/" + res; -#else - src = executablePath + "/../share/rpcs3/Icons/ui/" + res; -#endif - info = std::make_unique(src.c_str()); - // Check if the icons are in the same directory as the executable (local builds) - if (info->data == nullptr) - { - src = executablePath + "/Icons/ui/" + res; - info = std::make_unique(src.c_str()); - } - } - } -#endif - if (info->data != nullptr) - { - // Install the image to config dir - auto dst_dir = fs::get_config_dir() + "Icons/ui/"; - auto dst = dst_dir + res; - - if (!fs::is_dir(dst_dir)) - { - auto root_folder = fs::get_config_dir() + "Icons/"; - if (!fs::is_dir(root_folder)) - fs::create_dir(root_folder); - - fs::create_dir(dst_dir); - } - - fs::copy_file(src, dst, true); - } - } - - texture_raw_data.push_back(std::move(info)); - } - } - - void free_resources() - { - texture_raw_data.clear(); - } + void load_files(); + void free_resources(); }; struct compiled_resource @@ -242,23 +94,11 @@ namespace rsx command_config() = default; - void set_image_resource(u8 ref) - { - texture_ref = ref; - font_ref = nullptr; - } - - void set_font(font *ref) - { - texture_ref = image_resource_id::font_file; - font_ref = ref; - } + void set_image_resource(u8 ref); + void set_font(font *ref); // Analog to overlay_element::set_sinus_offset - f32 get_sinus_value() const - { - return (static_cast(get_system_time() / 1000) * pulse_speed_modifier) - pulse_sinus_offset; - } + f32 get_sinus_value() const; }; struct command @@ -269,65 +109,18 @@ namespace rsx std::vector draw_commands; - void add(const compiled_resource& other) - { - auto old_size = draw_commands.size(); - draw_commands.resize(old_size + other.draw_commands.size()); - std::copy(other.draw_commands.begin(), other.draw_commands.end(), draw_commands.begin() + old_size); - } - - void add(const compiled_resource& other, f32 x_offset, f32 y_offset) - { - auto old_size = draw_commands.size(); - draw_commands.resize(old_size + other.draw_commands.size()); - std::copy(other.draw_commands.begin(), other.draw_commands.end(), draw_commands.begin() + old_size); - - for (usz n = old_size; n < draw_commands.size(); ++n) - { - for (auto &v : draw_commands[n].verts) - { - v += vertex(x_offset, y_offset, 0.f, 0.f); - } - } - } - - void add(const compiled_resource& other, f32 x_offset, f32 y_offset, const areaf& clip_rect) - { - auto old_size = draw_commands.size(); - draw_commands.resize(old_size + other.draw_commands.size()); - std::copy(other.draw_commands.begin(), other.draw_commands.end(), draw_commands.begin() + old_size); - - for (usz n = old_size; n < draw_commands.size(); ++n) - { - for (auto &v : draw_commands[n].verts) - { - v += vertex(x_offset, y_offset, 0.f, 0.f); - } - - draw_commands[n].config.clip_rect = clip_rect; - draw_commands[n].config.clip_region = true; - } - } + void add(const compiled_resource& other); + void add(const compiled_resource& other, f32 x_offset, f32 y_offset); + void add(const compiled_resource& other, f32 x_offset, f32 y_offset, const areaf& clip_rect); //! Clear commands list - void clear() - { - draw_commands.clear(); - } + void clear(); //! Append the command to the back of the commands list and return it - command& append(const command& new_command) - { - draw_commands.emplace_back(new_command); - return draw_commands.back(); - } + command& append(const command& new_command); //! Prepend the command to the front of the commands list and return it - command& prepend(const command& new_command) - { - draw_commands.emplace(draw_commands.begin(), new_command); - return draw_commands.front(); - } + command& prepend(const command& new_command); }; struct overlay_element @@ -362,15 +155,7 @@ namespace rsx // 0.5 -> reset to 0 // 1 -> reset to 0.5 falling // 1.5 -> reset to 1 - void set_sinus_offset(f32 sinus_modifier) - { - if (sinus_modifier >= 0) - { - static const f32 PI = 3.14159265f; - const f32 pulse_sinus_x = static_cast(get_system_time() / 1000) * pulse_speed_modifier; - pulse_sinus_offset = fmod(pulse_sinus_x + sinus_modifier * PI, 2.0f * PI); - } - } + void set_sinus_offset(f32 sinus_modifier); compiled_resource compiled_resources; bool is_compiled = false; @@ -387,288 +172,26 @@ namespace rsx overlay_element(u16 _w, u16 _h) : w(_w), h(_h) {} virtual ~overlay_element() = default; - virtual void refresh() - { - // Just invalidate for draw when get_compiled() is called - is_compiled = false; - } - - virtual void translate(s16 _x, s16 _y) - { - x = static_cast(x + _x); - y = static_cast(y + _y); - - is_compiled = false; - } - - virtual void scale(f32 _x, f32 _y, bool origin_scaling) - { - if (origin_scaling) - { - x = static_cast(_x * x); - y = static_cast(_y * y); - } - - w = static_cast(_x * w); - h = static_cast(_y * h); - - is_compiled = false; - } - - virtual void set_pos(u16 _x, u16 _y) - { - x = _x; - y = _y; - - is_compiled = false; - } - - virtual void set_size(u16 _w, u16 _h) - { - w = _w; - h = _h; - - is_compiled = false; - } - - virtual void set_padding(u16 left, u16 right, u16 top, u16 bottom) - { - padding_left = left; - padding_right = right; - padding_top = top; - padding_bottom = bottom; - - is_compiled = false; - } - - virtual void set_padding(u16 padding) - { - padding_left = padding_right = padding_top = padding_bottom = padding; - is_compiled = false; - } - + virtual void refresh(); + virtual void translate(s16 _x, s16 _y); + virtual void scale(f32 _x, f32 _y, bool origin_scaling); + virtual void set_pos(u16 _x, u16 _y); + virtual void set_size(u16 _w, u16 _h); + virtual void set_padding(u16 left, u16 right, u16 top, u16 bottom); + virtual void set_padding(u16 padding); // NOTE: Functions as a simple position offset. Top left corner is the anchor. - virtual void set_margin(u16 left, u16 top) - { - margin_left = left; - margin_top = top; - - is_compiled = false; - } - - virtual void set_margin(u16 margin) - { - margin_left = margin_top = margin; - is_compiled = false; - } - - virtual void set_text(const std::string& text) - { - this->text = utf8_to_u32string(text); - is_compiled = false; - } - - virtual void set_unicode_text(const std::u32string& text) - { - this->text = text; - is_compiled = false; - } - - void set_text(localized_string_id id) - { - set_unicode_text(get_localized_u32string(id)); - } - - virtual void set_font(const char* font_name, u16 font_size) - { - font_ref = fontmgr::get(font_name, font_size); - is_compiled = false; - } - - virtual void align_text(text_align align) - { - alignment = align; - is_compiled = false; - } - - virtual void set_wrap_text(bool state) - { - wrap_text = state; - is_compiled = false; - } - - virtual font* get_font() const - { - return font_ref ? font_ref : fontmgr::get("Arial", 12); - } - - virtual std::vector render_text(const char32_t *string, f32 x, f32 y) - { - auto renderer = get_font(); - - f32 text_extents_w = 0.f; - u16 clip_width = clip_text ? w : umax; - std::vector result = renderer->render_text(string, clip_width, wrap_text); - - if (!result.empty()) - { - for (auto &v : result) - { - // Check for real text region extent - // TODO: Ellipsis - text_extents_w = std::max(v.values[0], text_extents_w); - - // Apply transform. - // (0, 0) has text sitting one line off the top left corner (text is outside the rect) hence the offset by text height - v.values[0] += x + padding_left; - v.values[1] += y + padding_top + static_cast(renderer->get_size_px()); - } - - if (alignment != text_align::left) - { - // Scan for lines and measure them - // Reposition them to the center or right depending on the alignment - std::vector> lines; - u32 line_begin = 0; - u32 ctr = 0; - - for (auto c : text) - { - switch (c) - { - case '\r': - continue; - case '\n': - lines.emplace_back(line_begin, ctr); - line_begin = ctr; - continue; - default: - ctr += 4; - } - } - - lines.emplace_back(line_begin, ctr); - const auto max_region_w = std::max(text_extents_w, w); - const f32 offset_extent = (alignment == text_align::center ? 0.5f : 1.0f); - - for (auto p : lines) - { - if (p.first >= p.second) - continue; - - const f32 line_length = result[p.second - 1].values[0] - result[p.first].values[0]; - const bool wrapped = std::fabs(result[p.second - 1].values[1] - result[p.first + 3].values[1]) >= (renderer->get_size_px() * 0.5f); - - if (wrapped) - continue; - - if (line_length < max_region_w) - { - const f32 offset = (max_region_w - line_length) * offset_extent; - for (auto n = p.first; n < p.second; ++n) - { - result[n].values[0] += offset; - } - } - } - } - } - - return result; - } - - virtual compiled_resource& get_compiled() - { - if (!is_compiled) - { - compiled_resources.clear(); - - compiled_resource compiled_resources_temp = {}; - auto& cmd_bg = compiled_resources_temp.append({}); - auto& config = cmd_bg.config; - - config.color = back_color; - config.pulse_glow = pulse_effect_enabled; - config.pulse_sinus_offset = pulse_sinus_offset; - config.pulse_speed_modifier = pulse_speed_modifier; - - auto& verts = compiled_resources_temp.draw_commands.front().verts; - verts.resize(4); - - verts[0].vec4(x, y, 0.f, 0.f); - verts[1].vec4(f32(x + w), y, 1.f, 0.f); - verts[2].vec4(x, f32(y + h), 0.f, 1.f); - verts[3].vec4(f32(x + w), f32(y + h), 1.f, 1.f); - - compiled_resources.add(std::move(compiled_resources_temp), margin_left, margin_top); - - if (!text.empty()) - { - compiled_resources_temp.clear(); - auto& cmd_text = compiled_resources_temp.append({}); - - cmd_text.config.set_font(font_ref ? font_ref : fontmgr::get("Arial", 12)); - cmd_text.config.color = fore_color; - cmd_text.verts = render_text(text.c_str(), static_cast(x), static_cast(y)); - - if (!cmd_text.verts.empty()) - compiled_resources.add(std::move(compiled_resources_temp), margin_left, margin_top); - } - - is_compiled = true; - } - - return compiled_resources; - } - - void measure_text(u16& width, u16& height, bool ignore_word_wrap = false) const - { - if (text.empty()) - { - width = height = 0; - return; - } - - auto renderer = get_font(); - - f32 text_width = 0.f; - f32 unused = 0.f; - f32 max_w = 0.f; - f32 last_word = 0.f; - height = static_cast(renderer->get_size_px()); - - for (auto c : text) - { - if (c == '\n') - { - height += static_cast(renderer->get_size_px() + 2); - max_w = std::max(max_w, text_width); - text_width = 0.f; - last_word = 0.f; - continue; - } - - if (c == ' ') - { - last_word = text_width; - } - - renderer->get_char(c, text_width, unused); - - if (!ignore_word_wrap && wrap_text && text_width >= w) - { - if ((text_width - last_word) < w) - { - max_w = std::max(max_w, last_word); - text_width -= (last_word + renderer->get_em_size()); - height += static_cast(renderer->get_size_px() + 2); - } - } - } - - max_w = std::max(max_w, text_width); - width = static_cast(ceilf(max_w)); - } + virtual void set_margin(u16 left, u16 top); + virtual void set_margin(u16 margin); + virtual void set_text(const std::string& text); + virtual void set_unicode_text(const std::u32string& text); + void set_text(localized_string_id id); + virtual void set_font(const char* font_name, u16 font_size); + virtual void align_text(text_align align); + virtual void set_wrap_text(bool state); + virtual font* get_font() const; + virtual std::vector render_text(const char32_t *string, f32 x, f32 y); + virtual compiled_resource& get_compiled(); + void measure_text(u16& width, u16& height, bool ignore_word_wrap = false) const; }; struct layout_container : public overlay_element @@ -681,191 +204,28 @@ namespace rsx virtual overlay_element* add_element(std::unique_ptr&, int = -1) = 0; - layout_container() - { - // Transparent by default - back_color.a = 0.f; - } + layout_container(); - void translate(s16 _x, s16 _y) override - { - overlay_element::translate(_x, _y); + void translate(s16 _x, s16 _y) override; + void set_pos(u16 _x, u16 _y) override; - for (auto &itm : m_items) - itm->translate(_x, _y); - } - - void set_pos(u16 _x, u16 _y) override - { - s16 dx = static_cast(_x - x); - s16 dy = static_cast(_y - y); - translate(dx, dy); - } - - compiled_resource& get_compiled() override - { - if (!is_compiled) - { - compiled_resource result = overlay_element::get_compiled(); - - for (auto &itm : m_items) - result.add(itm->get_compiled()); - - compiled_resources = result; - } - - return compiled_resources; - } + compiled_resource& get_compiled() override; virtual u16 get_scroll_offset_px() = 0; }; struct vertical_layout : public layout_container { - overlay_element* add_element(std::unique_ptr& item, int offset = -1) override - { - if (auto_resize) - { - item->set_pos(item->x + x, h + pack_padding + y); - h += item->h + pack_padding; - w = std::max(w, item->w); - } - else - { - item->set_pos(item->x + x, advance_pos + pack_padding + y); - advance_pos += item->h + pack_padding; - } - - if (offset < 0) - { - m_items.push_back(std::move(item)); - return m_items.back().get(); - } - else - { - auto result = item.get(); - m_items.insert(m_items.begin() + offset, std::move(item)); - return result; - } - } - - compiled_resource& get_compiled() override - { - if (scroll_offset_value == 0 && auto_resize) - return layout_container::get_compiled(); - - if (!is_compiled) - { - compiled_resource result = overlay_element::get_compiled(); - const f32 global_y_offset = static_cast(-scroll_offset_value); - - for (auto &item : m_items) - { - const s32 item_y_limit = s32{item->y} + item->h - scroll_offset_value - y; - const s32 item_y_base = s32{item->y} - scroll_offset_value - y; - - if (item_y_limit < 0 || item_y_base > h) - { - // Out of bounds - continue; - } - else if (item_y_limit > h || item_y_base < 0) - { - // Partial render - areaf clip_rect = static_cast(areai{x, y, (x + w), (y + h)}); - result.add(item->get_compiled(), 0.f, global_y_offset, clip_rect); - } - else - { - // Normal - result.add(item->get_compiled(), 0.f, global_y_offset); - } - } - - compiled_resources = result; - } - - return compiled_resources; - } - - u16 get_scroll_offset_px() override - { - return scroll_offset_value; - } + overlay_element* add_element(std::unique_ptr& item, int offset = -1) override; + compiled_resource& get_compiled() override; + u16 get_scroll_offset_px() override; }; struct horizontal_layout : public layout_container { - overlay_element* add_element(std::unique_ptr& item, int offset = -1) override - { - if (auto_resize) - { - item->set_pos(w + pack_padding + x, item->y + y); - w += item->w + pack_padding; - h = std::max(h, item->h); - } - else - { - item->set_pos(advance_pos + pack_padding + x, item->y + y); - advance_pos += item->w + pack_padding; - } - - if (offset < 0) - { - m_items.push_back(std::move(item)); - return m_items.back().get(); - } - else - { - auto result = item.get(); - m_items.insert(m_items.begin() + offset, std::move(item)); - return result; - } - } - - compiled_resource& get_compiled() override - { - if (scroll_offset_value == 0 && auto_resize) - return layout_container::get_compiled(); - - if (!is_compiled) - { - compiled_resource result = overlay_element::get_compiled(); - const f32 global_x_offset = static_cast(-scroll_offset_value); - - for (auto &item : m_items) - { - const s32 item_x_limit = s32{item->x} + item->w - scroll_offset_value - w; - const s32 item_x_base = s32{item->x} - scroll_offset_value - w; - - if (item_x_limit < 0 || item_x_base > h) - { - // Out of bounds - continue; - } - else if (item_x_limit > h || item_x_base < 0) - { - // Partial render - areaf clip_rect = static_cast(areai{x, y, (x + w), (y + h)}); - result.add(item->get_compiled(), global_x_offset, 0.f, clip_rect); - } - else - { - // Normal - result.add(item->get_compiled(), global_x_offset, 0.f); - } - } - - compiled_resources = result; - } - - return compiled_resources; - } - - u16 get_scroll_offset_px() override - { - return scroll_offset_value; - } + overlay_element* add_element(std::unique_ptr& item, int offset = -1) override; + compiled_resource& get_compiled() override; + u16 get_scroll_offset_px() override; }; // Controls @@ -891,55 +251,12 @@ namespace rsx public: using overlay_element::overlay_element; - compiled_resource& get_compiled() override - { - if (!is_compiled) - { - auto& result = overlay_element::get_compiled(); - auto& cmd_img = result.draw_commands.front(); + compiled_resource& get_compiled() override; - cmd_img.config.set_image_resource(image_resource_ref); - cmd_img.config.color = fore_color; - cmd_img.config.external_data_ref = external_ref; - - cmd_img.config.blur_strength = blur_strength; - - // Make padding work for images (treat them as the content instead of the 'background') - auto& verts = cmd_img.verts; - - verts[0] += vertex(padding_left, padding_bottom, 0, 0); - verts[1] += vertex(-padding_right, padding_bottom, 0, 0); - verts[2] += vertex(padding_left, -padding_top, 0, 0); - verts[3] += vertex(-padding_right, -padding_top, 0, 0); - - is_compiled = true; - } - - return compiled_resources; - } - - void set_image_resource(u8 resource_id) - { - image_resource_ref = resource_id; - external_ref = nullptr; - } - - void set_raw_image(image_info* raw_image) - { - image_resource_ref = image_resource_id::raw_image; - external_ref = raw_image; - } - - void clear_image() - { - image_resource_ref = image_resource_id::none; - external_ref = nullptr; - } - - void set_blur_strength(u8 strength) - { - blur_strength = strength; - } + void set_image_resource(u8 resource_id); + void set_raw_image(image_info* raw_image); + void clear_image(); + void set_blur_strength(u8 strength); }; struct image_button : public image_view @@ -948,178 +265,21 @@ namespace rsx u16 m_text_offset_x = 0; s16 m_text_offset_y = 0; - image_button() - { - // Do not clip text to region extents - // TODO: Define custom clipping region or use two controls to emulate - clip_text = false; - } + image_button(); + image_button(u16 _w, u16 _h); - image_button(u16 _w, u16 _h) - { - clip_text = false; - set_size(_w, _h); - } + void set_text_vertical_adjust(s16 offset); + void set_size(u16 /*w*/, u16 h) override; - void set_text_vertical_adjust(s16 offset) - { - m_text_offset_y = offset; - } - - void set_size(u16 /*w*/, u16 h) override - { - image_view::set_size(h, h); - m_text_offset_x = (h / 2) + text_horizontal_offset; // By default text is at the horizontal center - } - - compiled_resource& get_compiled() override - { - if (!is_compiled) - { - auto& compiled = image_view::get_compiled(); - for (auto& cmd : compiled.draw_commands) - { - if (cmd.config.texture_ref == image_resource_id::font_file) - { - // Text, translate geometry to the right - for (auto &v : cmd.verts) - { - v.values[0] += m_text_offset_x; - v.values[1] += m_text_offset_y; - } - } - } - } - - return compiled_resources; - } + compiled_resource& get_compiled() override; }; struct label : public overlay_element { label() = default; + label(const std::string& text); - label(const std::string& text) - { - set_text(text); - } - - bool auto_resize(bool grow_only = false, u16 limit_w = -1, u16 limit_h = -1) - { - u16 new_width, new_height; - u16 old_width = w, old_height = h; - measure_text(new_width, new_height, true); - - new_width += padding_left + padding_right; - new_height += padding_top + padding_bottom; - - if (new_width > limit_w && wrap_text) - measure_text(new_width, new_height, false); - - if (grow_only) - { - new_width = std::max(w, new_width); - new_height = std::max(h, new_height); - } - - w = std::min(new_width, limit_w); - h = std::min(new_height, limit_h); - - bool size_changed = old_width != new_width || old_height != new_height; - return size_changed; - } - }; - - struct progress_bar : public overlay_element - { - private: - overlay_element indicator; - label text_view; - - f32 m_limit = 100.f; - f32 m_value = 0.f; - - public: - progress_bar(); - void inc(f32 value); - void dec(f32 value); - void set_limit(f32 limit); - void set_value(f32 value); - void set_pos(u16 _x, u16 _y) override; - void set_size(u16 _w, u16 _h) override; - void translate(s16 dx, s16 dy) override; - void set_text(const std::string& str) override; - - compiled_resource& get_compiled() override; - }; - - struct list_view : public vertical_layout - { - private: - std::unique_ptr m_scroll_indicator_top; - std::unique_ptr m_scroll_indicator_bottom; - std::unique_ptr m_cancel_btn; - std::unique_ptr m_accept_btn; - std::unique_ptr m_highlight_box; - - u16 m_elements_height = 0; - s32 m_selected_entry = -1; - u16 m_elements_count = 0; - - bool m_cancel_only = false; - - public: - list_view(u16 width, u16 height); - - void update_selection(); - - void select_entry(s32 entry); - void select_next(u16 count = 1); - void select_previous(u16 count = 1); - - void add_entry(std::unique_ptr& entry); - - int get_selected_index() const; - - std::u32string get_selected_item(); - - void set_cancel_only(bool cancel_only); - void translate(s16 _x, s16 _y) override; - - compiled_resource& get_compiled() override; - }; - - struct edit_text : public label - { - enum class direction - { - up, - down, - left, - right - }; - - u16 caret_position = 0; - u16 vertical_scroll_offset = 0; - - bool m_reset_caret_pulse = false; - bool password_mode = false; - - std::u32string value; - std::u32string placeholder; - - using label::label; - - void set_text(const std::string& text) override; - void set_unicode_text(const std::u32string& text) override; - - void set_placeholder(const std::u32string& placeholder_text); - - void move_caret(direction dir); - void insert_text(const std::u32string& str); - void erase(); - - compiled_resource& get_compiled() override; + bool auto_resize(bool grow_only = false, u16 limit_w = -1, u16 limit_h = -1); }; struct graph : public overlay_element diff --git a/rpcs3/Emu/RSX/Overlays/overlay_edit_text.cpp b/rpcs3/Emu/RSX/Overlays/overlay_edit_text.cpp index aab3626efa..4447e7a8ad 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_edit_text.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_edit_text.cpp @@ -1,5 +1,5 @@ #include "stdafx.h" -#include "overlay_controls.h" +#include "overlay_edit_text.hpp" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_edit_text.hpp b/rpcs3/Emu/RSX/Overlays/overlay_edit_text.hpp new file mode 100644 index 0000000000..a249343474 --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_edit_text.hpp @@ -0,0 +1,40 @@ +#include "overlay_controls.h" + +namespace rsx +{ + namespace overlays + { + struct edit_text : public label + { + enum class direction + { + up, + down, + left, + right + }; + + u16 caret_position = 0; + u16 vertical_scroll_offset = 0; + + bool m_reset_caret_pulse = false; + bool password_mode = false; + + std::u32string value; + std::u32string placeholder; + + using label::label; + + void set_text(const std::string& text) override; + void set_unicode_text(const std::u32string& text) override; + + void set_placeholder(const std::u32string& placeholder_text); + + void move_caret(direction dir); + void insert_text(const std::u32string& str); + void erase(); + + compiled_resource& get_compiled() override; + }; + } +} diff --git a/rpcs3/Emu/RSX/Overlays/overlay_fonts.cpp b/rpcs3/Emu/RSX/Overlays/overlay_fonts.cpp index c34411b179..0b325fbee7 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_fonts.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_fonts.cpp @@ -2,6 +2,16 @@ #include "overlay_controls.h" #include "Emu/system_config.h" +#ifndef _WIN32 +#include +#include +#include + +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) +#include +#endif +#endif + namespace rsx { namespace overlays diff --git a/rpcs3/Emu/RSX/Overlays/overlay_fonts.h b/rpcs3/Emu/RSX/Overlays/overlay_fonts.h index 246562dc3b..717374a5ac 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_fonts.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_fonts.h @@ -3,6 +3,7 @@ #include "util/types.hpp" #include "overlay_utils.h" +#include #include // STB_IMAGE_IMPLEMENTATION and STB_TRUETYPE_IMPLEMENTATION defined externally diff --git a/rpcs3/Emu/RSX/Overlays/overlay_list_view.cpp b/rpcs3/Emu/RSX/Overlays/overlay_list_view.cpp index 52b46b5f0f..808ba3ad88 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_list_view.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_list_view.cpp @@ -1,5 +1,5 @@ #include "stdafx.h" -#include "overlay_controls.h" +#include "overlay_list_view.hpp" #include "Emu/system_config.h" namespace rsx diff --git a/rpcs3/Emu/RSX/Overlays/overlay_list_view.hpp b/rpcs3/Emu/RSX/Overlays/overlay_list_view.hpp new file mode 100644 index 0000000000..8629b0ef86 --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_list_view.hpp @@ -0,0 +1,43 @@ +#include "overlay_controls.h" + +namespace rsx +{ + namespace overlays + { + struct list_view : public vertical_layout + { + private: + std::unique_ptr m_scroll_indicator_top; + std::unique_ptr m_scroll_indicator_bottom; + std::unique_ptr m_cancel_btn; + std::unique_ptr m_accept_btn; + std::unique_ptr m_highlight_box; + + u16 m_elements_height = 0; + s32 m_selected_entry = -1; + u16 m_elements_count = 0; + + bool m_cancel_only = false; + + public: + list_view(u16 width, u16 height); + + void update_selection(); + + void select_entry(s32 entry); + void select_next(u16 count = 1); + void select_previous(u16 count = 1); + + void add_entry(std::unique_ptr& entry); + + int get_selected_index() const; + + std::u32string get_selected_item(); + + void set_cancel_only(bool cancel_only); + void translate(s16 _x, s16 _y) override; + + compiled_resource& get_compiled() override; + }; + } +} diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h index 1824a25c26..21ebe2ff12 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h @@ -1,6 +1,7 @@ #pragma once #include "overlays.h" +#include "overlay_progress_bar.hpp" #include "Emu/Cell/Modules/cellMsgDialog.h" namespace rsx diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.h b/rpcs3/Emu/RSX/Overlays/overlay_osk.h index 0778d3e44d..7b0e70e707 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.h @@ -1,6 +1,7 @@ #pragma once #include "overlays.h" +#include "overlay_edit_text.hpp" #include "overlay_osk_panel.h" #include "Emu/Cell/Modules/cellOskDialog.h" diff --git a/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.cpp b/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.cpp index f25e934c4e..c21b30c36b 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.cpp @@ -1,5 +1,5 @@ #include "stdafx.h" -#include "overlay_controls.h" +#include "overlay_progress_bar.hpp" namespace rsx { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.hpp b/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.hpp new file mode 100644 index 0000000000..716d601f23 --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_progress_bar.hpp @@ -0,0 +1,30 @@ +#include "overlay_controls.h" + +namespace rsx +{ + namespace overlays + { + struct progress_bar : public overlay_element + { + private: + overlay_element indicator; + label text_view; + + f32 m_limit = 100.f; + f32 m_value = 0.f; + + public: + progress_bar(); + void inc(f32 value); + void dec(f32 value); + void set_limit(f32 limit); + void set_value(f32 value); + void set_pos(u16 _x, u16 _y) override; + void set_size(u16 _w, u16 _h) override; + void translate(s16 dx, s16 dy) override; + void set_text(const std::string& str) override; + + compiled_resource& get_compiled() override; + }; + } +} diff --git a/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.h b/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.h index ea3b1e349b..ef6660c02b 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.h @@ -1,6 +1,7 @@ #pragma once #include "overlays.h" +#include "overlay_list_view.hpp" #include "Emu/Cell/Modules/cellSaveData.h" namespace rsx diff --git a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.h b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.h index d05cd4e066..2bc4cdb948 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.h @@ -1,6 +1,7 @@ #pragma once #include "overlays.h" +#include "overlay_list_view.hpp" #include "Emu/Cell/ErrorCodes.h" namespace rsx diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index d5194e8b1e..b24d4b0c44 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -72,6 +72,7 @@ + @@ -458,6 +459,9 @@ + + + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index d7585c43d0..8c76ee1cec 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -993,6 +993,9 @@ Emu\GPU\RSX\Program + + Emu\GPU\RSX\Overlays + @@ -1950,6 +1953,15 @@ Emu\GPU\RSX\Program + + Emu\GPU\RSX\Overlays + + + Emu\GPU\RSX\Overlays + + + Emu\GPU\RSX\Overlays +