mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-09 02:07:04 +08:00
feat(ui): add lightbox viewer for screenshots
Add a lightbox to preview screenshots on click. Introduce Button::set_texture for image-backed buttons, add widget getters for window size and visibility, and block background input while the lightbox is open. Close the lightbox with Escape or a left-click.
This commit is contained in:
@@ -28,6 +28,7 @@ public:
|
|||||||
Button& set_background_image(const std::string& path,
|
Button& set_background_image(const std::string& path,
|
||||||
TextureManager& texture_manager);
|
TextureManager& texture_manager);
|
||||||
Button& set_default_image(TextureManager& texture_manager);
|
Button& set_default_image(TextureManager& texture_manager);
|
||||||
|
Button& set_texture(const Texture* texture, bool change_button_size);
|
||||||
virtual Button& set_text(const std::string& text);
|
virtual Button& set_text(const std::string& text);
|
||||||
|
|
||||||
Button& set_auto_scale(bool auto_scale);
|
Button& set_auto_scale(bool auto_scale);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Cubed/ui/image.hpp"
|
||||||
#include "Cubed/ui/ui_manager.hpp"
|
#include "Cubed/ui/ui_manager.hpp"
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
|
class Texture;
|
||||||
class ScreenshotScene;
|
class ScreenshotScene;
|
||||||
class ScreenshotUI : public UIManager {
|
class ScreenshotUI : public UIManager {
|
||||||
public:
|
public:
|
||||||
@@ -17,9 +19,23 @@ public:
|
|||||||
|
|
||||||
void update_layout(int width, int height);
|
void update_layout(int width, int height);
|
||||||
|
|
||||||
|
void open_lightbox(const Texture* image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handle_window_resize_event(const WindowResizeEvent& e) override;
|
struct ImageInfo {
|
||||||
|
const Texture* texture = nullptr;
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
};
|
||||||
|
|
||||||
ScreenshotScene& m_scene;
|
ScreenshotScene& m_scene;
|
||||||
|
std::vector<ImageInfo> m_image_infos;
|
||||||
|
Widget* m_lightbox = nullptr;
|
||||||
|
Image* m_lightbox_image = nullptr;
|
||||||
|
bool handle_window_resize_event(const WindowResizeEvent& e) override;
|
||||||
|
bool handle_key_event(const KeyEvent& e) override;
|
||||||
|
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||||
|
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||||
|
bool handle_mouse_wheel_event(const MouseWheelEvent& e) override;
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -28,6 +28,11 @@ public:
|
|||||||
Widget& operator=(Widget&&) = delete;
|
Widget& operator=(Widget&&) = delete;
|
||||||
|
|
||||||
Widget(Widget* parent);
|
Widget(Widget* parent);
|
||||||
|
|
||||||
|
static void set_window_size(int width, int height);
|
||||||
|
static int get_window_height();
|
||||||
|
static int get_window_width();
|
||||||
|
|
||||||
virtual ~Widget() = default;
|
virtual ~Widget() = default;
|
||||||
virtual void update(float dt);
|
virtual void update(float dt);
|
||||||
virtual void render(Renderer& renderer);
|
virtual void render(Renderer& renderer);
|
||||||
@@ -35,7 +40,7 @@ public:
|
|||||||
virtual Widget& set_offset(glm::vec2 offset);
|
virtual Widget& set_offset(glm::vec2 offset);
|
||||||
virtual Widget& add_offset(glm::vec2 offset);
|
virtual Widget& add_offset(glm::vec2 offset);
|
||||||
virtual glm::vec2 get_offset() const;
|
virtual glm::vec2 get_offset() const;
|
||||||
static void set_window_size(int width, int height);
|
|
||||||
virtual Widget& set_visible(bool visible);
|
virtual Widget& set_visible(bool visible);
|
||||||
// Returns the final display size
|
// Returns the final display size
|
||||||
|
|
||||||
@@ -58,6 +63,8 @@ public:
|
|||||||
virtual bool handle_window_resize_event(const WindowResizeEvent& e);
|
virtual bool handle_window_resize_event(const WindowResizeEvent& e);
|
||||||
virtual bool handle_mouse_move_event(const MouseMoveEvent& e);
|
virtual bool handle_mouse_move_event(const MouseMoveEvent& e);
|
||||||
virtual bool handle_text_input_event(const TextInputEvent& e);
|
virtual bool handle_text_input_event(const TextInputEvent& e);
|
||||||
|
|
||||||
|
virtual bool is_visible() const;
|
||||||
void set_order(TraversalOrder order);
|
void set_order(TraversalOrder order);
|
||||||
|
|
||||||
template <typename T, typename... Args> T& add_child(Args&&... args) {
|
template <typename T, typename... Args> T& add_child(Args&&... args) {
|
||||||
|
|||||||
@@ -103,7 +103,18 @@ Button& Button::set_background_image(const std::string& path,
|
|||||||
Button& Button::set_default_image(TextureManager& texture_manager) {
|
Button& Button::set_default_image(TextureManager& texture_manager) {
|
||||||
return set_background_image(DEFAULT_BUTTON_IMAGE, texture_manager);
|
return set_background_image(DEFAULT_BUTTON_IMAGE, texture_manager);
|
||||||
}
|
}
|
||||||
|
Button& Button::set_texture(const Texture* texture, bool change_button_size) {
|
||||||
|
if (!texture) {
|
||||||
|
Logger::error("Button Background Image is nullptr");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
m_background->set_texture(texture, false);
|
||||||
|
if (change_button_size) {
|
||||||
|
set_width(texture->width());
|
||||||
|
set_height(texture->height());
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
Button& Button::set_text(const std::string& text) {
|
Button& Button::set_text(const std::string& text) {
|
||||||
m_foreground->set_text(text);
|
m_foreground->set_text(text);
|
||||||
update_text_scale();
|
update_text_scale();
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ void ScreenshotUI::init() {
|
|||||||
update_layout(renderer.window_width(), renderer.window_height());
|
update_layout(renderer.window_width(), renderer.window_height());
|
||||||
}
|
}
|
||||||
void ScreenshotUI::update_layout(int width, int height) {
|
void ScreenshotUI::update_layout(int width, int height) {
|
||||||
|
|
||||||
|
m_image_infos.clear();
|
||||||
|
|
||||||
auto& texture_manager = m_scene.scene_manager().app().texture_manager();
|
auto& texture_manager = m_scene.scene_manager().app().texture_manager();
|
||||||
m_root_widget.reset();
|
m_root_widget.reset();
|
||||||
auto bg = std::make_unique<Rect>(nullptr);
|
auto bg = std::make_unique<Rect>(nullptr);
|
||||||
@@ -37,25 +40,11 @@ void ScreenshotUI::update_layout(int width, int height) {
|
|||||||
|
|
||||||
title.set_text(tr("menu.screenshot")).set_anchor(Anchor::TOP_CENTER);
|
title.set_text(tr("menu.screenshot")).set_anchor(Anchor::TOP_CENTER);
|
||||||
title.set_offset({0, 5});
|
title.set_offset({0, 5});
|
||||||
struct ImageInfo {
|
|
||||||
const Texture* texture = nullptr;
|
|
||||||
int width = 0;
|
|
||||||
int height = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto& return_button = bg->add_child<Button>();
|
|
||||||
return_button.set_default_image(texture_manager);
|
|
||||||
return_button.set_anchor(Anchor::BOTTOM_CENTER);
|
|
||||||
return_button.set_offset({0, -30});
|
|
||||||
return_button.set_text(tr("button.return"));
|
|
||||||
return_button.set_clicked(
|
|
||||||
[this]() { m_scene.scene_manager().request_pop(); });
|
|
||||||
|
|
||||||
fs::path path = Renderer::SCREENSHOT_PATH;
|
fs::path path = Renderer::SCREENSHOT_PATH;
|
||||||
|
|
||||||
std::vector<ImageInfo> image_infos;
|
|
||||||
constexpr float IMAGE_SCALE = 0.25;
|
constexpr float IMAGE_SCALE = 0.25;
|
||||||
|
ScrollView* sv = nullptr;
|
||||||
if (fs::exists(path)) {
|
if (fs::exists(path)) {
|
||||||
for (auto& entry : fs::recursive_directory_iterator(path)) {
|
for (auto& entry : fs::recursive_directory_iterator(path)) {
|
||||||
if (!fs::is_regular_file(entry)) {
|
if (!fs::is_regular_file(entry)) {
|
||||||
@@ -65,12 +54,11 @@ void ScreenshotUI::update_layout(int width, int height) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ImageInfo info;
|
ImageInfo info;
|
||||||
Logger::info("Path: {}", entry.path().string());
|
|
||||||
info.texture =
|
info.texture =
|
||||||
texture_manager.get_image_texture(entry.path().string(), true);
|
texture_manager.get_image_texture(entry.path().string(), true);
|
||||||
info.height = info.texture->height() * IMAGE_SCALE;
|
info.height = info.texture->height() * IMAGE_SCALE;
|
||||||
info.width = info.texture->width() * IMAGE_SCALE;
|
info.width = info.texture->width() * IMAGE_SCALE;
|
||||||
image_infos.emplace_back(std::move(info));
|
m_image_infos.emplace_back(std::move(info));
|
||||||
}
|
}
|
||||||
constexpr int PADDING = 50;
|
constexpr int PADDING = 50;
|
||||||
constexpr int SPACING = 10;
|
constexpr int SPACING = 10;
|
||||||
@@ -81,14 +69,11 @@ void ScreenshotUI::update_layout(int width, int height) {
|
|||||||
|
|
||||||
auto& scroll = bg->add_child<ScrollView>();
|
auto& scroll = bg->add_child<ScrollView>();
|
||||||
|
|
||||||
|
sv = &scroll;
|
||||||
|
|
||||||
scroll.set_anchor(Anchor::TOP_CENTER)
|
scroll.set_anchor(Anchor::TOP_CENTER)
|
||||||
.set_offset({0, 10 + title.height()});
|
.set_offset({0, 10 + title.height()});
|
||||||
|
|
||||||
float scroll_height = height - scroll.get_offset().y +
|
|
||||||
return_button.get_offset().y -
|
|
||||||
return_button.height();
|
|
||||||
|
|
||||||
scroll.set_height(std::max(0.0f, scroll_height));
|
|
||||||
auto root_column = std::make_unique<ColumnLayout>(&scroll);
|
auto root_column = std::make_unique<ColumnLayout>(&scroll);
|
||||||
root_column->set_child_anchor(ColumnLayoutAnchor::LEFT)
|
root_column->set_child_anchor(ColumnLayoutAnchor::LEFT)
|
||||||
.set_spacing(10)
|
.set_spacing(10)
|
||||||
@@ -100,7 +85,7 @@ void ScreenshotUI::update_layout(int width, int height) {
|
|||||||
row->set_spacing(SPACING);
|
row->set_spacing(SPACING);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto& image : image_infos) {
|
for (auto& image : m_image_infos) {
|
||||||
if (!row) {
|
if (!row) {
|
||||||
create_row();
|
create_row();
|
||||||
}
|
}
|
||||||
@@ -110,24 +95,105 @@ void ScreenshotUI::update_layout(int width, int height) {
|
|||||||
create_row();
|
create_row();
|
||||||
}
|
}
|
||||||
cur_width += image.width;
|
cur_width += image.width;
|
||||||
auto& im = row->add_child<Image>();
|
auto& button = row->add_child<Button>();
|
||||||
im.set_texture(image.texture, true).set_scale(IMAGE_SCALE);
|
button.set_texture(image.texture, true).set_scale(IMAGE_SCALE);
|
||||||
|
button.set_clicked(
|
||||||
|
[image, this]() { open_lightbox(image.texture); });
|
||||||
}
|
}
|
||||||
scroll.set_child(std::move(root_column));
|
scroll.set_child(std::move(root_column));
|
||||||
}
|
}
|
||||||
if (!fs::exists(path) || image_infos.empty()) {
|
if (!fs::exists(path) || m_image_infos.empty()) {
|
||||||
auto& label = bg->add_child<Label>();
|
auto& label = bg->add_child<Label>();
|
||||||
label.set_text(tr("screenshot.no_screenshot"))
|
label.set_text(tr("screenshot.no_screenshot"))
|
||||||
.set_color(Color::WHITE)
|
.set_color(Color::WHITE)
|
||||||
.set_anchor(Anchor::CENTER);
|
.set_anchor(Anchor::CENTER);
|
||||||
}
|
}
|
||||||
|
auto& return_button = bg->add_child<Button>();
|
||||||
|
return_button.set_default_image(texture_manager);
|
||||||
|
return_button.set_anchor(Anchor::BOTTOM_CENTER);
|
||||||
|
return_button.set_offset({0, -30});
|
||||||
|
return_button.set_text(tr("button.return"));
|
||||||
|
return_button.set_clicked(
|
||||||
|
[this]() { m_scene.scene_manager().request_pop(); });
|
||||||
|
if (sv) {
|
||||||
|
float scroll_height = height - sv->get_offset().y +
|
||||||
|
return_button.get_offset().y -
|
||||||
|
return_button.height();
|
||||||
|
|
||||||
|
sv->set_height(std::max(0.0f, scroll_height));
|
||||||
|
}
|
||||||
|
auto& rect = bg->add_child<Rect>();
|
||||||
|
rect.set_fill_parent(true);
|
||||||
|
rect.set_color(Color::BLACK).set_alpha(0.5f);
|
||||||
|
m_lightbox = ▭
|
||||||
|
auto& lightbox_image = rect.add_child<Image>();
|
||||||
|
m_lightbox_image = &lightbox_image;
|
||||||
|
m_lightbox->set_visible(false);
|
||||||
m_root_widget = std::move(bg);
|
m_root_widget = std::move(bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScreenshotUI::open_lightbox(const Texture* image) {
|
||||||
|
if (image) {
|
||||||
|
constexpr float PADDING = 50.0f;
|
||||||
|
m_lightbox_image->set_texture(image, true);
|
||||||
|
|
||||||
|
float target_height =
|
||||||
|
std::max(50.0f, Widget::get_window_height() - PADDING * 2);
|
||||||
|
float target_width =
|
||||||
|
std::max(50.0f, Widget::get_window_width() - PADDING * 2);
|
||||||
|
|
||||||
|
float image_width = image->width();
|
||||||
|
float image_height = image->height();
|
||||||
|
|
||||||
|
float scale_w = target_width / image_width;
|
||||||
|
float scale_h = target_height / image_height;
|
||||||
|
float scale = std::min(scale_h, scale_w);
|
||||||
|
m_lightbox_image->set_scale(scale);
|
||||||
|
m_lightbox_image->set_anchor(Anchor::CENTER);
|
||||||
|
}
|
||||||
|
m_lightbox->set_visible(true);
|
||||||
|
}
|
||||||
|
|
||||||
bool ScreenshotUI::handle_window_resize_event(const WindowResizeEvent& e) {
|
bool ScreenshotUI::handle_window_resize_event(const WindowResizeEvent& e) {
|
||||||
update_layout(e.width, e.height);
|
update_layout(e.width, e.height);
|
||||||
return UIManager::handle_window_resize_event(e);
|
return UIManager::handle_window_resize_event(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScreenshotUI::handle_key_event(const KeyEvent& e) {
|
||||||
|
if (UIManager::handle_key_event(e)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (e.key == Key::ESCAPE && e.action == KeyAction::PRESS) {
|
||||||
|
if (m_lightbox && m_lightbox->is_visible()) {
|
||||||
|
m_lightbox->set_visible(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_scene.scene_manager().request_pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenshotUI::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||||
|
if (m_lightbox && m_lightbox->is_visible()) {
|
||||||
|
if (e.key == MouseKey::LEFT_BUTTON && e.action == KeyAction::PRESS) {
|
||||||
|
m_lightbox->set_visible(false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return UIManager::handle_mouse_button_event(e);
|
||||||
|
}
|
||||||
|
bool ScreenshotUI::handle_mouse_move_event(const MouseMoveEvent& e) {
|
||||||
|
if (m_lightbox && m_lightbox->is_visible()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return UIManager::handle_mouse_move_event(e);
|
||||||
|
}
|
||||||
|
bool ScreenshotUI::handle_mouse_wheel_event(const MouseWheelEvent& e) {
|
||||||
|
if (m_lightbox && m_lightbox->is_visible()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return UIManager::handle_mouse_wheel_event(e);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -174,6 +174,9 @@ void Widget::set_window_size(int width, int height) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Widget::get_window_height() { return m_window_height; }
|
||||||
|
int Widget::get_window_width() { return m_window_width; }
|
||||||
|
|
||||||
Widget& Widget::set_visible(bool visible) {
|
Widget& Widget::set_visible(bool visible) {
|
||||||
m_visible = visible;
|
m_visible = visible;
|
||||||
return *this;
|
return *this;
|
||||||
@@ -335,4 +338,5 @@ bool Widget::handle_text_input_event(const TextInputEvent& e) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void Widget::set_order(TraversalOrder order) { m_order = order; }
|
void Widget::set_order(TraversalOrder order) { m_order = order; }
|
||||||
|
bool Widget::is_visible() const { return m_visible; }
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
Reference in New Issue
Block a user