mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
feat(screenshot): add screenshot viewer scene
Add SCREENSHOT scene and UI to browse screenshots from the main menu, with localization for English and Chinese. Also allow image loading by full path and replace scene-operation asserts with error logs.
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
"menu.main.join_game": "Join Game",
|
||||
"menu.main.credits": "Credits",
|
||||
"menu.main.quit": "Quit",
|
||||
"menu.screenshot": "Screenshots",
|
||||
"menu.main.player_name": "Player: {name}",
|
||||
"menu.main.cubed_version": "Cubed: {version}",
|
||||
"menu.pause.pause_menu": "Pause Menu",
|
||||
@@ -46,5 +47,6 @@
|
||||
"item.snowy_grass_block.name": "snowy grass block",
|
||||
"item.stone.name": "stone",
|
||||
"item.water.name": "water",
|
||||
"item.pig_spawn_egg.name": "pig spawn egg"
|
||||
"item.pig_spawn_egg.name": "pig spawn egg",
|
||||
"screenshot.no_screenshot": "No Screenshot"
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
"menu.main.join_game": "加入游戏",
|
||||
"menu.main.credits": "制作人员",
|
||||
"menu.main.quit": "退出",
|
||||
"menu.screenshot": "屏幕截图",
|
||||
"menu.main.player_name": "玩家:{name}",
|
||||
"menu.main.cubed_version": "Cubed:{version}",
|
||||
"menu.pause.pause_menu": "暂停菜单",
|
||||
@@ -46,5 +47,6 @@
|
||||
"item.snowy_grass_block.name": "覆雪草方块",
|
||||
"item.stone.name": "石头",
|
||||
"item.water.name": "水",
|
||||
"item.pig_spawn_egg.name": "猪猪刷怪蛋"
|
||||
"item.pig_spawn_egg.name": "猪猪刷怪蛋",
|
||||
"screenshot.no_screenshot": "没有截图"
|
||||
}
|
||||
@@ -9,7 +9,8 @@ enum class SceneType {
|
||||
CREDITS,
|
||||
SETTINGS,
|
||||
HOST_GAME,
|
||||
JOIN_GAME
|
||||
JOIN_GAME,
|
||||
SCREENSHOT
|
||||
};
|
||||
|
||||
class Scene {
|
||||
|
||||
29
include/Cubed/scene/screenshot_scene.hpp
Normal file
29
include/Cubed/scene/screenshot_scene.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "Cubed/scene/scene.hpp"
|
||||
#include "Cubed/ui/screenshot_ui.hpp"
|
||||
namespace Cubed {
|
||||
class SceneManager;
|
||||
class ScreenshotScene : public Scene {
|
||||
public:
|
||||
ScreenshotScene(SceneManager& scene_manager);
|
||||
~ScreenshotScene();
|
||||
|
||||
ScreenshotScene(const ScreenshotScene&) = delete;
|
||||
ScreenshotScene(ScreenshotScene&&) = delete;
|
||||
ScreenshotScene& operator=(const ScreenshotScene&) = delete;
|
||||
ScreenshotScene& operator=(ScreenshotScene&&) = delete;
|
||||
|
||||
void update(float dt) override;
|
||||
void render(Renderer& renderer) override;
|
||||
bool handle_event(const Event& e) override;
|
||||
void on_enter() override;
|
||||
void on_leave() override;
|
||||
void on_re_enter() override;
|
||||
|
||||
SceneManager& scene_manager();
|
||||
|
||||
private:
|
||||
SceneManager& m_scene_manager;
|
||||
ScreenshotUI m_ui;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -20,7 +20,8 @@ public:
|
||||
const Texture* get_block_status_array() const;
|
||||
const Texture* get_texture_array() const;
|
||||
const Texture* get_cross_plane_array() const;
|
||||
const Texture* get_image_texture(const std::string& path);
|
||||
const Texture* get_image_texture(const std::string& path,
|
||||
bool full_path = false);
|
||||
const Texture* get_pbr_texture() const;
|
||||
const ItemTextureMap& get_item_textures() const;
|
||||
const Texture* get_skin() const;
|
||||
@@ -49,7 +50,8 @@ private:
|
||||
void load_block_texture(unsigned block_id);
|
||||
void init_item_texture();
|
||||
void load_cross_plane_texture(unsigned id);
|
||||
const Texture* load_image_texture(const std::string& path);
|
||||
const Texture* load_image_texture(const std::string& path,
|
||||
bool full_path = false);
|
||||
void load_pbr_texture(unsigned id);
|
||||
void init_item();
|
||||
void init_block();
|
||||
|
||||
23
include/Cubed/ui/screenshot_ui.hpp
Normal file
23
include/Cubed/ui/screenshot_ui.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/ui/ui_manager.hpp"
|
||||
namespace Cubed {
|
||||
class ScreenshotScene;
|
||||
class ScreenshotUI : public UIManager {
|
||||
public:
|
||||
ScreenshotUI(ScreenshotScene& m_scene);
|
||||
~ScreenshotUI();
|
||||
|
||||
ScreenshotUI(const ScreenshotUI&) = delete;
|
||||
ScreenshotUI(ScreenshotUI&&) = delete;
|
||||
ScreenshotUI& operator=(const ScreenshotUI&) = delete;
|
||||
ScreenshotUI& operator=(ScreenshotUI&&) = delete;
|
||||
|
||||
void init();
|
||||
|
||||
void update_layout(int width, int height);
|
||||
|
||||
private:
|
||||
ScreenshotScene& m_scene;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -106,4 +106,6 @@ target_sources(${PROJECT_NAME}
|
||||
tools/json_utils.cpp
|
||||
gameplay/item_manager.cpp
|
||||
gameplay/block_manager.cpp
|
||||
ui/screenshot_ui.cpp
|
||||
scene/screenshot_scene.cpp
|
||||
)
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Cubed/scene/host_game_scene.hpp"
|
||||
#include "Cubed/scene/join_game_scene.hpp"
|
||||
#include "Cubed/scene/main_menu_scene.hpp"
|
||||
#include "Cubed/scene/screenshot_scene.hpp"
|
||||
#include "Cubed/scene/settings_scene.hpp"
|
||||
#include "Cubed/scene/world_scene.hpp"
|
||||
|
||||
@@ -33,15 +34,24 @@ bool SceneManager::handle_event(const Event& e) {
|
||||
}
|
||||
|
||||
void SceneManager::request_change(SceneType type) {
|
||||
ASSERT(!m_operation.has_value());
|
||||
if (m_operation.has_value()) {
|
||||
Logger::error("Scene operation has_value");
|
||||
return;
|
||||
}
|
||||
m_operation = {OperationType::CHANGE, type};
|
||||
}
|
||||
void SceneManager::request_push(SceneType type) {
|
||||
ASSERT(!m_operation.has_value());
|
||||
if (m_operation.has_value()) {
|
||||
Logger::error("Scene operation has_value");
|
||||
return;
|
||||
}
|
||||
m_operation = {OperationType::PUSH, type};
|
||||
}
|
||||
void SceneManager::request_pop() {
|
||||
ASSERT(!m_operation.has_value());
|
||||
if (m_operation.has_value()) {
|
||||
Logger::error("Scene operation has_value");
|
||||
return;
|
||||
}
|
||||
m_operation = {OperationType::POP, std::nullopt};
|
||||
}
|
||||
|
||||
@@ -103,6 +113,8 @@ std::unique_ptr<Scene> SceneManager::create_scene(SceneType type) {
|
||||
return std::make_unique<HostGameScene>(*this);
|
||||
case SceneType::JOIN_GAME:
|
||||
return std::make_unique<JoinGameScene>(*this);
|
||||
case SceneType::SCREENSHOT:
|
||||
return std::make_unique<ScreenshotScene>(*this);
|
||||
}
|
||||
|
||||
std::string err = std::format("Unknown Scene");
|
||||
|
||||
20
src/scene/screenshot_scene.cpp
Normal file
20
src/scene/screenshot_scene.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "Cubed/scene/screenshot_scene.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
ScreenshotScene::ScreenshotScene(SceneManager& scene_manager)
|
||||
: m_scene_manager(scene_manager), m_ui(*this) {}
|
||||
|
||||
ScreenshotScene::~ScreenshotScene() {}
|
||||
|
||||
void ScreenshotScene::update(float dt) { m_ui.update(dt); }
|
||||
void ScreenshotScene::render(Renderer& renderer) { m_ui.render(renderer); }
|
||||
bool ScreenshotScene::handle_event(const Event& e) {
|
||||
return m_ui.handle_event(e);
|
||||
}
|
||||
void ScreenshotScene::on_enter() { m_ui.init(); }
|
||||
void ScreenshotScene::on_leave() {}
|
||||
void ScreenshotScene::on_re_enter() {}
|
||||
|
||||
SceneManager& ScreenshotScene::scene_manager() { return m_scene_manager; }
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -57,12 +57,13 @@ const Texture* TextureManager::get_texture_array() const {
|
||||
const Texture* TextureManager::get_cross_plane_array() const {
|
||||
return m_cross_plane_array.get();
|
||||
}
|
||||
const Texture* TextureManager::get_image_texture(const std::string& path) {
|
||||
const Texture* TextureManager::get_image_texture(const std::string& path,
|
||||
bool full_path) {
|
||||
auto it = m_ui_map.find(path);
|
||||
if (it != m_ui_map.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
return load_image_texture(path);
|
||||
return load_image_texture(path, full_path);
|
||||
}
|
||||
|
||||
const Texture* TextureManager::get_pbr_texture() const {
|
||||
@@ -151,9 +152,10 @@ void TextureManager::load_cross_plane_texture(unsigned id) {
|
||||
CROSS_PLANE_SIZE, CROSS_PLANE_SIZE);
|
||||
}
|
||||
|
||||
const Texture* TextureManager::load_image_texture(const std::string& path) {
|
||||
const Texture* TextureManager::load_image_texture(const std::string& path,
|
||||
bool full_path) {
|
||||
|
||||
auto image_data = (Tools::load_image_data(path));
|
||||
auto image_data = (Tools::load_image_data(path, true, full_path));
|
||||
std::unique_ptr<Texture> image =
|
||||
std::make_unique<Texture>(TextureType::TEXTURE_2D);
|
||||
image->tex_image_2d(RGBA, RGBA, GL_UNSIGNED_BYTE, image_data.data,
|
||||
|
||||
@@ -58,7 +58,16 @@ void MainMenuUIManager::init() {
|
||||
});
|
||||
m_pending_enable.emplace_back(&button);
|
||||
}
|
||||
|
||||
{
|
||||
auto& button = layout.add_child<Button>();
|
||||
button.set_default_image(texture_manager);
|
||||
button.set_text(tr("menu.screenshot"));
|
||||
button.set_clicked([this, &button]() {
|
||||
button.set_enable(false);
|
||||
m_scene.scene_manager().request_push(SceneType::SCREENSHOT);
|
||||
});
|
||||
m_pending_enable.emplace_back(&button);
|
||||
}
|
||||
{
|
||||
auto& button = layout.add_child<Button>();
|
||||
|
||||
|
||||
117
src/ui/screenshot_ui.cpp
Normal file
117
src/ui/screenshot_ui.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "Cubed/ui/screenshot_ui.hpp"
|
||||
|
||||
#include "Cubed/app.hpp"
|
||||
#include "Cubed/localization.hpp"
|
||||
#include "Cubed/render/renderer.hpp"
|
||||
#include "Cubed/scene/screenshot_scene.hpp"
|
||||
#include "Cubed/ui/button.hpp"
|
||||
#include "Cubed/ui/column_layout.hpp"
|
||||
#include "Cubed/ui/row_layout.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
namespace {
|
||||
constexpr std::array<std::string_view, 4> IMAGE_EXT{".jpg", ".jpeg", ".png",
|
||||
".bmp"};
|
||||
}
|
||||
namespace Cubed {
|
||||
ScreenshotUI::ScreenshotUI(ScreenshotScene& scene) : m_scene(scene) {}
|
||||
ScreenshotUI::~ScreenshotUI() {}
|
||||
void ScreenshotUI::init() {
|
||||
|
||||
auto& renderer = m_scene.scene_manager().app().renderer();
|
||||
|
||||
update_layout(renderer.window_width(), renderer.window_height());
|
||||
}
|
||||
void ScreenshotUI::update_layout(int width, int height) {
|
||||
auto& texture_manager = m_scene.scene_manager().app().texture_manager();
|
||||
m_root_widget.reset();
|
||||
auto bg = std::make_unique<Rect>(nullptr);
|
||||
|
||||
bg->set_color(Color::BLACK).set_fill_parent(true);
|
||||
|
||||
auto& title = bg->add_child<Label>();
|
||||
|
||||
title.set_text(tr("menu.screenshot")).set_anchor(Anchor::TOP_CENTER);
|
||||
title.set_offset({0, 5});
|
||||
struct ImageInfo {
|
||||
const Texture* texture = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
fs::path path = Renderer::SCREENSHOT_PATH;
|
||||
|
||||
std::vector<ImageInfo> image_infos;
|
||||
constexpr float IMAGE_SCALE = 0.25;
|
||||
if (fs::exists(path)) {
|
||||
for (auto& entry : fs::recursive_directory_iterator(path)) {
|
||||
if (!fs::is_regular_file(entry)) {
|
||||
continue;
|
||||
}
|
||||
if (!std::ranges::find(IMAGE_EXT, entry.path().extension())) {
|
||||
continue;
|
||||
}
|
||||
ImageInfo info;
|
||||
Logger::info("Path: {}", entry.path().string());
|
||||
info.texture =
|
||||
texture_manager.get_image_texture(entry.path().string(), true);
|
||||
info.height = info.texture->height() * IMAGE_SCALE;
|
||||
info.width = info.texture->width() * IMAGE_SCALE;
|
||||
image_infos.emplace_back(std::move(info));
|
||||
}
|
||||
constexpr int PADDING = 50;
|
||||
constexpr int SPACING = 10;
|
||||
|
||||
const int WIDGET_WIDTH = std::min(width, width - 2 * PADDING);
|
||||
// const int WIDGET_HEIGHT = std::max(height, height - 2 * PADDING);
|
||||
|
||||
float cur_width = 0.0f;
|
||||
// float cur_height = 0.0f;
|
||||
auto& root_column = bg->add_child<ColumnLayout>();
|
||||
root_column.set_child_anchor(ColumnLayoutAnchor::LEFT)
|
||||
.set_spacing(10)
|
||||
.set_anchor(Anchor::TOP_CENTER)
|
||||
.set_offset({0, 10 + title.height()});
|
||||
RowLayout* row = nullptr;
|
||||
auto create_row = [&row, &root_column]() {
|
||||
row = &root_column.add_child<RowLayout>();
|
||||
row->set_anchor(Anchor::TOP_LEFT);
|
||||
row->set_spacing(SPACING);
|
||||
};
|
||||
|
||||
for (auto& image : image_infos) {
|
||||
if (!row) {
|
||||
create_row();
|
||||
}
|
||||
|
||||
if (cur_width + image.width > WIDGET_WIDTH && cur_width > 0) {
|
||||
cur_width = 0.0f;
|
||||
create_row();
|
||||
}
|
||||
cur_width += image.width;
|
||||
auto& im = row->add_child<Image>();
|
||||
im.set_texture(image.texture, true).set_scale(IMAGE_SCALE);
|
||||
}
|
||||
}
|
||||
if (!fs::exists(path) || image_infos.empty()) {
|
||||
auto& label = bg->add_child<Label>();
|
||||
label.set_text(tr("screenshot.no_screenshot"))
|
||||
.set_color(Color::WHITE)
|
||||
.set_anchor(Anchor::CENTER);
|
||||
}
|
||||
|
||||
{
|
||||
auto& button = bg->add_child<Button>();
|
||||
button.set_default_image(texture_manager);
|
||||
button.set_anchor(Anchor::BOTTOM_CENTER);
|
||||
button.set_offset({0, -50});
|
||||
button.set_text(tr("button.return"));
|
||||
button.set_clicked([this]() { m_scene.scene_manager().request_pop(); });
|
||||
}
|
||||
|
||||
m_root_widget = std::move(bg);
|
||||
}
|
||||
} // namespace Cubed
|
||||
Reference in New Issue
Block a user