mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
feature: localization (#32)
* build: add nlohmann/json library * build(deps): add harfbuzz dependency * feat(cmake): add guard to reject macOS builds * feat(font): add HarfBuzz shaping and replace font with unifont * feat(localization): add i18n support with en_US and zh_CN translations Implement Localization class to load JSON translation files. Replace hardcoded strings in UI with translation keys and update sliders/combo buttons to use dynamic text. * feat(localization): detect system locale for language default * feat(settings): add language selection option with restart notification * fix: use vckpg to add freetype on windows
This commit is contained in:
@@ -1,4 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
message(FATAL_ERROR "This game does not support macOS")
|
||||||
|
endif()
|
||||||
|
|
||||||
project(Cubed LANGUAGES C CXX)
|
project(Cubed LANGUAGES C CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
@@ -141,6 +146,7 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
absl::flat_hash_map
|
absl::flat_hash_map
|
||||||
zstd::zstd
|
zstd::zstd
|
||||||
OpenAL::OpenAL
|
OpenAL::OpenAL
|
||||||
|
harfbuzz::harfbuzz
|
||||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|||||||
Binary file not shown.
90
assets/fonts/OFL-1.1.txt
Normal file
90
assets/fonts/OFL-1.1.txt
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
The SIL Open Font License version 1.1 is copied below, and is also
|
||||||
|
available with a FAQ at http://scripts.sil.org/OFL.
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
assets/fonts/unifont_t-17.0.05.otf
Normal file
BIN
assets/fonts/unifont_t-17.0.05.otf
Normal file
Binary file not shown.
34
assets/lang/en_US.json
Normal file
34
assets/lang/en_US.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"menu.settings": "Settings",
|
||||||
|
"menu.main.host_game": "Host Game",
|
||||||
|
"menu.main.join_game": "Join Game",
|
||||||
|
"menu.main.credits": "Credits",
|
||||||
|
"menu.main.quit": "Quit",
|
||||||
|
"menu.main.player_name": "Player: {name}",
|
||||||
|
"menu.main.cubed_version": "Cubed: {version}",
|
||||||
|
"menu.pause.pause_menu": "Pause Menu",
|
||||||
|
"menu.pause.back_to_game": "Back To Game",
|
||||||
|
"menu.pause.return_to_main_menu": "Return to Menu",
|
||||||
|
"button.done": "Done",
|
||||||
|
"button.return": "Return",
|
||||||
|
"settings.title": "Settings",
|
||||||
|
"settings.fov": "Fov: {fov}",
|
||||||
|
"settings.mouse_sensitivity": "Sensitivity: {sensitivity}",
|
||||||
|
"settings.distance": "Distance: {distance}",
|
||||||
|
"settings.music_volume": "Music: {music}",
|
||||||
|
"settings.sfx_volume": "SFX: {sfx}",
|
||||||
|
"settings.fullscreen": "fullscreen: {state}",
|
||||||
|
"settings.aniso": "Aniso: {aniso}",
|
||||||
|
"settings.vsync": "V-Sync: {state}",
|
||||||
|
"settings.language": "Language: {lang}",
|
||||||
|
"settings.restart_game_info": "You need to restart the game to apply this changes.",
|
||||||
|
"common.on": "on",
|
||||||
|
"common.off": "off",
|
||||||
|
"hostgame.create_a_new_world": "Create A New World",
|
||||||
|
"hostgame.world_seed": "World Seed",
|
||||||
|
"hostgame.port": "Port",
|
||||||
|
"hostgame.create_world": "Create World",
|
||||||
|
"joingame.join_a_world": "Join A World",
|
||||||
|
"joingame.server_ip": "Server Ip",
|
||||||
|
"joingame.join_world": "Join World"
|
||||||
|
}
|
||||||
34
assets/lang/zh_CN.json
Normal file
34
assets/lang/zh_CN.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"menu.settings": "设置",
|
||||||
|
"menu.main.host_game": "创建游戏",
|
||||||
|
"menu.main.join_game": "加入游戏",
|
||||||
|
"menu.main.credits": "制作人员",
|
||||||
|
"menu.main.quit": "退出",
|
||||||
|
"menu.main.player_name": "玩家:{name}",
|
||||||
|
"menu.main.cubed_version": "Cubed:{version}",
|
||||||
|
"menu.pause.pause_menu": "暂停菜单",
|
||||||
|
"menu.pause.back_to_game": "返回游戏",
|
||||||
|
"menu.pause.return_to_main_menu": "返回主菜单",
|
||||||
|
"button.done": "完成",
|
||||||
|
"button.return": "返回",
|
||||||
|
"settings.title": "设置",
|
||||||
|
"settings.fov": "视野:{fov}",
|
||||||
|
"settings.mouse_sensitivity": "灵敏度:{sensitivity}",
|
||||||
|
"settings.distance": "距离:{distance}",
|
||||||
|
"settings.music_volume": "音乐:{music}",
|
||||||
|
"settings.sfx_volume": "音效:{sfx}",
|
||||||
|
"settings.fullscreen": "全屏:{state}",
|
||||||
|
"settings.aniso": "各向异性过滤:{aniso}",
|
||||||
|
"settings.vsync": "垂直同步: {state}",
|
||||||
|
"settings.language": "语言: {lang}",
|
||||||
|
"settings.restart_game_info": "您需要重启游戏以应用这些更改。",
|
||||||
|
"common.on": "开",
|
||||||
|
"common.off": "关",
|
||||||
|
"hostgame.create_a_new_world": "创建新世界",
|
||||||
|
"hostgame.world_seed": "世界种子",
|
||||||
|
"hostgame.port": "端口",
|
||||||
|
"hostgame.create_world": "创建世界",
|
||||||
|
"joingame.join_a_world": "加入世界",
|
||||||
|
"joingame.server_ip": "服务器IP",
|
||||||
|
"joingame.join_world": "加入世界"
|
||||||
|
}
|
||||||
@@ -6,8 +6,9 @@ find_package(Protobuf REQUIRED)
|
|||||||
find_package(absl REQUIRED)
|
find_package(absl REQUIRED)
|
||||||
find_package(zstd REQUIRED)
|
find_package(zstd REQUIRED)
|
||||||
find_package(OpenAL REQUIRED)
|
find_package(OpenAL REQUIRED)
|
||||||
|
find_package(harfbuzz REQUIRED)
|
||||||
|
find_package(Freetype REQUIRED)
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
find_package(Freetype REQUIRED)
|
|
||||||
find_package(glfw3 REQUIRED)
|
find_package(glfw3 REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -51,15 +52,6 @@ if (WIN32)
|
|||||||
|
|
||||||
FetchContent_MakeAvailable(glfw)
|
FetchContent_MakeAvailable(glfw)
|
||||||
|
|
||||||
FetchContent_Declare(
|
|
||||||
freetype
|
|
||||||
GIT_REPOSITORY https://gitlab.freedesktop.org/freetype/freetype.git
|
|
||||||
GIT_TAG VER-2-14-3
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(freetype)
|
|
||||||
if(TARGET freetype)
|
|
||||||
add_library(Freetype::Freetype ALIAS freetype)
|
|
||||||
endif()
|
|
||||||
set(_BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS})
|
set(_BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS})
|
||||||
set(BUILD_SHARED_LIBS ON)
|
set(BUILD_SHARED_LIBS ON)
|
||||||
|
|
||||||
|
|||||||
49
include/Cubed/localization.hpp
Normal file
49
include/Cubed/localization.hpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
namespace Cubed {
|
||||||
|
|
||||||
|
struct FormatArg {
|
||||||
|
std::string name;
|
||||||
|
std::string value;
|
||||||
|
};
|
||||||
|
template <typename T> FormatArg arg(std::string_view name, T&& value) {
|
||||||
|
return {std::string(name), std::format("{}", std::forward<T>(value))};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Localization {
|
||||||
|
public:
|
||||||
|
Localization();
|
||||||
|
static Localization& instance();
|
||||||
|
|
||||||
|
void load_language(std::string_view language);
|
||||||
|
bool has(const std::string& key) const;
|
||||||
|
template <typename... Args>
|
||||||
|
std::string translate(const std::string& key, Args&&... args) {
|
||||||
|
if constexpr (sizeof...(Args) == 0)
|
||||||
|
return std::string{lookup(key)};
|
||||||
|
std::string fmt{lookup(key)};
|
||||||
|
(replace_all(fmt, "{" + args.name + "}", args.value), ...);
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, std::string> m_translations;
|
||||||
|
std::string m_current_lang{"en_US"};
|
||||||
|
std::string_view lookup(const std::string& key) const;
|
||||||
|
static void replace_all(std::string& str, std::string_view from,
|
||||||
|
std::string_view to);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
std::string tr(const std::string& key, Args&&... args) {
|
||||||
|
return Localization::instance().translate(key, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
FormatArg tr_arg(const std::string& name, const std::string& key) {
|
||||||
|
return {std::string(name), tr(key)};
|
||||||
|
}
|
||||||
|
} // namespace Cubed
|
||||||
@@ -82,7 +82,7 @@ public:
|
|||||||
void set_nearest() const;
|
void set_nearest() const;
|
||||||
void set_repeat(bool r = true, bool s = true, bool t = true) const;
|
void set_repeat(bool r = true, bool s = true, bool t = true) const;
|
||||||
void set_clamp_to_border(bool r = true, bool s = true, bool t = true) const;
|
void set_clamp_to_border(bool r = true, bool s = true, bool t = true) const;
|
||||||
void set_clamp_to_edge(bool r = true, bool s = true, bool t = true) const;
|
void set_clamp_to_edge(bool r = false, bool s = true, bool t = true) const;
|
||||||
|
|
||||||
TextureType type() const;
|
TextureType type() const;
|
||||||
float width() const;
|
float width() const;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <hb-ft.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -22,6 +23,16 @@ struct Character {
|
|||||||
GLuint advance;
|
GLuint advance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Glyph {
|
||||||
|
glm::vec2 uv_min;
|
||||||
|
glm::vec2 uv_max;
|
||||||
|
|
||||||
|
glm::ivec2 size;
|
||||||
|
glm::ivec2 bearing;
|
||||||
|
|
||||||
|
int layer = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct TextMesh {
|
struct TextMesh {
|
||||||
std::vector<Vertex2D> vertices;
|
std::vector<Vertex2D> vertices;
|
||||||
|
|
||||||
@@ -36,27 +47,29 @@ class Shader;
|
|||||||
|
|
||||||
class Font {
|
class Font {
|
||||||
public:
|
public:
|
||||||
|
static constexpr int CELL_SIZE = 64;
|
||||||
Font();
|
Font();
|
||||||
~Font();
|
~Font();
|
||||||
|
static Font& get();
|
||||||
static TextMesh vertices(const std::string& text);
|
TextMesh vertices(const std::string& text);
|
||||||
static const Texture* text_texture();
|
const Texture* text_texture();
|
||||||
static const std::string& font_path();
|
static const std::string& font_path();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FT_Library m_ft;
|
FT_Library m_ft;
|
||||||
FT_Face m_face;
|
FT_Face m_face;
|
||||||
|
hb_font_t* m_hb_font;
|
||||||
|
int m_next_layer = 0;
|
||||||
float m_texture_width = 64;
|
float m_texture_width = 64;
|
||||||
float m_texture_height = 64;
|
float m_texture_height = 64;
|
||||||
|
|
||||||
static inline std::unique_ptr<Texture> m_text_texture;
|
std::unique_ptr<Texture> m_text_texture;
|
||||||
static inline std::string m_font_path{ASSETS_PATH
|
static inline std::string m_font_path{ASSETS_PATH
|
||||||
"fonts/IBMPlexSans-Regular.ttf"};
|
"fonts/unifont_t-17.0.05.otf"};
|
||||||
std::unordered_map<char8_t, Character> m_characters;
|
std::unordered_map<uint32_t, Glyph> m_cache;
|
||||||
|
int m_max_layers = 0;
|
||||||
void load_character(char8_t c);
|
Glyph& load_glyph(uint32_t glyph_index);
|
||||||
void setup_font_character();
|
void upload_glyph(Glyph& glyph, const unsigned char* buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
13
include/Cubed/tools/system_locate.hpp
Normal file
13
include/Cubed/tools/system_locate.hpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Cubed {
|
||||||
|
struct SystemLocale {
|
||||||
|
std::string country; // CN、US、JP...
|
||||||
|
std::string locale; // zh_CN、en_US...
|
||||||
|
};
|
||||||
|
|
||||||
|
SystemLocale get_system_locale();
|
||||||
|
|
||||||
|
} // namespace Cubed
|
||||||
@@ -13,7 +13,9 @@ public:
|
|||||||
ComboButton& operator=(ComboButton&&) = delete;
|
ComboButton& operator=(ComboButton&&) = delete;
|
||||||
|
|
||||||
ComboButton(Widget* parent);
|
ComboButton(Widget* parent);
|
||||||
Button& set_text(const std::string& text) override;
|
ComboButton& set_combo_text(const std::string& key,
|
||||||
|
const std::string& variable);
|
||||||
|
Button& set_text(const std::string& key) override;
|
||||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||||
ComboButton& set_combos(std::span<ComboPair> combos);
|
ComboButton& set_combos(std::span<ComboPair> combos);
|
||||||
ComboButton& set_index(int index);
|
ComboButton& set_index(int index);
|
||||||
@@ -23,7 +25,8 @@ private:
|
|||||||
|
|
||||||
std::vector<std::string> m_suffix;
|
std::vector<std::string> m_suffix;
|
||||||
std::vector<std::function<void()>> m_funs;
|
std::vector<std::function<void()>> m_funs;
|
||||||
std::string m_text;
|
std::string m_key;
|
||||||
|
std::string m_variable;
|
||||||
int m_index = 0;
|
int m_index = 0;
|
||||||
int m_sum = 0;
|
int m_sum = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ public:
|
|||||||
Slider& set_scale(float scale);
|
Slider& set_scale(float scale);
|
||||||
Slider& set_width(float width);
|
Slider& set_width(float width);
|
||||||
Slider& set_height(float h);
|
Slider& set_height(float h);
|
||||||
Slider& set_text(const std::string& text);
|
Slider& set_slider_text(const std::string& key,
|
||||||
|
const std::string& variable);
|
||||||
Slider& set_track_image(const std::string& path,
|
Slider& set_track_image(const std::string& path,
|
||||||
TextureManager& texture_manager);
|
TextureManager& texture_manager);
|
||||||
Slider& set_thumb_image(const std::string& path,
|
Slider& set_thumb_image(const std::string& path,
|
||||||
@@ -57,8 +58,8 @@ private:
|
|||||||
int* m_int_value = nullptr;
|
int* m_int_value = nullptr;
|
||||||
float m_min = 0;
|
float m_min = 0;
|
||||||
float m_max = 0;
|
float m_max = 0;
|
||||||
std::string m_text;
|
std::string m_key;
|
||||||
|
std::string m_variable;
|
||||||
void init_track();
|
void init_track();
|
||||||
void init_thumb();
|
void init_thumb();
|
||||||
|
|
||||||
|
|||||||
2
include/nlohmann/.clang-format
Normal file
2
include/nlohmann/.clang-format
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
DisableFormat: true
|
||||||
|
SortIncludes: false
|
||||||
25526
include/nlohmann/json.hpp
Normal file
25526
include/nlohmann/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -83,4 +83,6 @@ target_sources(${PROJECT_NAME}
|
|||||||
ui/host_game_ui.cpp
|
ui/host_game_ui.cpp
|
||||||
ui/join_game_ui.cpp
|
ui/join_game_ui.cpp
|
||||||
ui/error_ui.cpp
|
ui/error_ui.cpp
|
||||||
|
localization.cpp
|
||||||
|
tools/system_locate.cpp
|
||||||
)
|
)
|
||||||
10
src/app.cpp
10
src/app.cpp
@@ -3,10 +3,12 @@
|
|||||||
#include "Cubed/camera.hpp"
|
#include "Cubed/camera.hpp"
|
||||||
#include "Cubed/config.hpp"
|
#include "Cubed/config.hpp"
|
||||||
#include "Cubed/debug_collector.hpp"
|
#include "Cubed/debug_collector.hpp"
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/tools/arg_parser.hpp"
|
#include "Cubed/tools/arg_parser.hpp"
|
||||||
#include "Cubed/tools/cubed_assert.hpp"
|
#include "Cubed/tools/cubed_assert.hpp"
|
||||||
#include "Cubed/tools/log.hpp"
|
#include "Cubed/tools/log.hpp"
|
||||||
#include "Cubed/tools/system_info.hpp"
|
#include "Cubed/tools/system_info.hpp"
|
||||||
|
#include "Cubed/tools/system_locate.hpp"
|
||||||
#include "Cubed/tools/text_tools.hpp"
|
#include "Cubed/tools/text_tools.hpp"
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
|
|
||||||
@@ -39,6 +41,14 @@ void App::init(int argc, char** argv) {
|
|||||||
handle_toml();
|
handle_toml();
|
||||||
handle_argument(argc, argv);
|
handle_argument(argc, argv);
|
||||||
|
|
||||||
|
auto locate = get_system_locale();
|
||||||
|
std::string default_value = "en_US";
|
||||||
|
if (locate.country == "CN") {
|
||||||
|
default_value = "zh_CN";
|
||||||
|
}
|
||||||
|
Localization::instance().load_language(
|
||||||
|
m_game_config.get("language", default_value));
|
||||||
|
|
||||||
m_window.init();
|
m_window.init();
|
||||||
m_window.imgui_init();
|
m_window.imgui_init();
|
||||||
|
|
||||||
|
|||||||
61
src/localization.cpp
Normal file
61
src/localization.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include "Cubed/localization.hpp"
|
||||||
|
|
||||||
|
#include "Cubed/tools/cubed_assert.hpp"
|
||||||
|
#include "Cubed/tools/log.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
using json = nlohmann::json;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
namespace Cubed {
|
||||||
|
Localization::Localization() {}
|
||||||
|
Localization& Localization::instance() {
|
||||||
|
static Localization s_instance;
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Localization::load_language(std::string_view language) {
|
||||||
|
m_current_lang = language;
|
||||||
|
std::string path =
|
||||||
|
ASSETS_PATH + std::format("lang/{}.json", m_current_lang);
|
||||||
|
std::ifstream file(path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
Logger::error("Can't Open File {}", path);
|
||||||
|
ASSERT(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
json j = json::parse(file);
|
||||||
|
m_translations.clear();
|
||||||
|
j.get_to(m_translations);
|
||||||
|
} catch (const json::parse_error& e) {
|
||||||
|
Logger::error("JSON syntax error: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view Localization::lookup(const std::string& key) const {
|
||||||
|
auto it = m_translations.find(key);
|
||||||
|
if (it != m_translations.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
Logger::error("Can't find key {} in language {}", key, m_current_lang);
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Localization::has(const std::string& key) const {
|
||||||
|
return m_translations.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Localization::replace_all(std::string& str, std::string_view from,
|
||||||
|
std::string_view to) {
|
||||||
|
|
||||||
|
std::size_t pos = 0;
|
||||||
|
|
||||||
|
while ((pos = str.find(from, pos)) != std::string::npos) {
|
||||||
|
str.replace(pos, from.size(), to);
|
||||||
|
pos += to.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Cubed
|
||||||
@@ -187,7 +187,7 @@ void Renderer::render_lable(const Label& label) {
|
|||||||
|
|
||||||
shader.set_loc("projection", m_ui_proj_matrix);
|
shader.set_loc("projection", m_ui_proj_matrix);
|
||||||
|
|
||||||
Font::text_texture()->bind(0);
|
Font::get().text_texture()->bind(0);
|
||||||
auto& data = label.data();
|
auto& data = label.data();
|
||||||
auto pos = label.pos();
|
auto pos = label.pos();
|
||||||
auto& text_style = label.text_style();
|
auto& text_style = label.text_style();
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ void Texture::tex_image_2d(TextureFormat internalformat, TextureFormat format,
|
|||||||
GLenum type, const void* data, GLsizei width,
|
GLenum type, const void* data, GLsizei width,
|
||||||
GLsizei height, GLint level, GLint border) {
|
GLsizei height, GLint level, GLint border) {
|
||||||
bind();
|
bind();
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
m_width = static_cast<float>(width);
|
m_width = static_cast<float>(width);
|
||||||
m_height = static_cast<float>(height);
|
m_height = static_cast<float>(height);
|
||||||
glTexImage2D(get_gl_texture_type(), level,
|
glTexImage2D(get_gl_texture_type(), level,
|
||||||
@@ -62,6 +63,7 @@ void Texture::tex_image_3d(TextureFormat internalformat, TextureFormat format,
|
|||||||
GLsizei height, GLsizei depth, GLint level,
|
GLsizei height, GLsizei depth, GLint level,
|
||||||
GLint border) {
|
GLint border) {
|
||||||
bind();
|
bind();
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
m_width = static_cast<float>(width);
|
m_width = static_cast<float>(width);
|
||||||
m_height = static_cast<float>(height);
|
m_height = static_cast<float>(height);
|
||||||
glTexImage3D(get_gl_texture_type(), level,
|
glTexImage3D(get_gl_texture_type(), level,
|
||||||
@@ -73,6 +75,7 @@ void Texture::tex_sub_image_3d(TextureFormat format, GLenum type,
|
|||||||
GLint zoffset, GLsizei width, GLsizei height,
|
GLint zoffset, GLsizei width, GLsizei height,
|
||||||
GLsizei depth, GLint level) const {
|
GLsizei depth, GLint level) const {
|
||||||
bind();
|
bind();
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
glTexSubImage3D(get_gl_texture_type(), level, xoffset, yoffset, zoffset,
|
glTexSubImage3D(get_gl_texture_type(), level, xoffset, yoffset, zoffset,
|
||||||
width, height, depth, std::to_underlying(format), type,
|
width, height, depth, std::to_underlying(format), type,
|
||||||
data);
|
data);
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
#include "Cubed/tools/font.hpp"
|
#include "Cubed/tools/font.hpp"
|
||||||
|
|
||||||
#include "Cubed/constants.hpp"
|
#include "Cubed/tools/cubed_assert.hpp"
|
||||||
#include "Cubed/tools/log.hpp"
|
#include "Cubed/tools/log.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
|
|
||||||
Font::Font() {
|
Font::Font() {
|
||||||
|
|
||||||
if (FT_Init_FreeType(&m_ft)) {
|
if (FT_Init_FreeType(&m_ft)) {
|
||||||
Logger::error("FREETYPE: Could not init FreeType Library");
|
Logger::error("FREETYPE: Could not init FreeType Library");
|
||||||
}
|
}
|
||||||
@@ -17,54 +15,84 @@ Font::Font() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FT_Set_Pixel_Sizes(m_face, 0, 48);
|
FT_Set_Pixel_Sizes(m_face, 0, 48);
|
||||||
setup_font_character();
|
|
||||||
|
GLint max_layers;
|
||||||
|
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &max_layers);
|
||||||
|
|
||||||
|
m_max_layers = std::min(max_layers, 2048);
|
||||||
|
Logger::info("Font Max Layers {}", m_max_layers);
|
||||||
|
m_hb_font = hb_ft_font_create_referenced(m_face);
|
||||||
|
m_text_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D_ARRAY);
|
||||||
|
m_text_texture->tex_image_3d(TextureFormat::R8, TextureFormat::RED,
|
||||||
|
GL_UNSIGNED_BYTE, nullptr, CELL_SIZE,
|
||||||
|
CELL_SIZE, m_max_layers);
|
||||||
|
m_text_texture->set_linear();
|
||||||
|
m_text_texture->set_clamp_to_edge();
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::~Font() {
|
Font::~Font() {
|
||||||
|
hb_font_destroy(m_hb_font);
|
||||||
FT_Done_Face(m_face);
|
FT_Done_Face(m_face);
|
||||||
FT_Done_FreeType(m_ft);
|
FT_Done_FreeType(m_ft);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::load_character(char8_t c) {
|
Glyph& Font::load_glyph(uint32_t glyph_index) {
|
||||||
if (FT_Load_Char(m_face, c, FT_LOAD_RENDER)) {
|
auto it = m_cache.find(glyph_index);
|
||||||
Logger::error("FREETYTPE: Failed to load Glyph");
|
if (it != m_cache.end()) {
|
||||||
return;
|
return it->second;
|
||||||
|
}
|
||||||
|
if (FT_Load_Glyph(m_face, glyph_index, FT_LOAD_RENDER)) {
|
||||||
|
Logger::error("Failed to load glyph {}", glyph_index);
|
||||||
}
|
}
|
||||||
const auto& width = m_face->glyph->bitmap.width;
|
|
||||||
const auto& height = m_face->glyph->bitmap.rows;
|
|
||||||
m_text_texture->tex_sub_image_3d(TextureFormat::RED, GL_UNSIGNED_BYTE,
|
|
||||||
m_face->glyph->bitmap.buffer, 0, 0,
|
|
||||||
static_cast<int>(c), width, height);
|
|
||||||
|
|
||||||
Character character = {
|
Glyph glyph;
|
||||||
glm::vec2{0.5f / m_texture_width, 0.5f / m_texture_height},
|
|
||||||
glm::vec2{(width - 0.5f) / m_texture_width,
|
|
||||||
(height - 0.5f) / m_texture_height},
|
|
||||||
glm::ivec2(m_face->glyph->bitmap.width, m_face->glyph->bitmap.rows),
|
|
||||||
glm::ivec2(m_face->glyph->bitmap_left, m_face->glyph->bitmap_top),
|
|
||||||
static_cast<GLuint>(m_face->glyph->advance.x)};
|
|
||||||
|
|
||||||
m_characters.insert({c, std::move(character)});
|
glyph.size.x = m_face->glyph->bitmap.width;
|
||||||
|
glyph.size.y = m_face->glyph->bitmap.rows;
|
||||||
|
glyph.bearing.x = m_face->glyph->bitmap_left;
|
||||||
|
glyph.bearing.y = m_face->glyph->bitmap_top;
|
||||||
|
|
||||||
|
upload_glyph(glyph, m_face->glyph->bitmap.buffer);
|
||||||
|
auto [iter, _] = m_cache.try_emplace(glyph_index, std::move(glyph));
|
||||||
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::setup_font_character() {
|
void Font::upload_glyph(Glyph& glyph, const unsigned char* buffer) {
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
ASSERT(glyph.size.x <= CELL_SIZE);
|
||||||
m_text_texture = std::make_unique<Texture>(TextureType::TEXTURE_2D_ARRAY);
|
ASSERT(glyph.size.y <= CELL_SIZE);
|
||||||
m_text_texture->tex_image_3d(TextureFormat::R8, TextureFormat::RED,
|
ASSERT(m_next_layer < m_max_layers);
|
||||||
GL_UNSIGNED_BYTE, nullptr, m_texture_width,
|
|
||||||
m_texture_height, MAX_CHARACTER);
|
|
||||||
|
|
||||||
for (char8_t c = 0; c < 128; c++) {
|
m_text_texture->tex_sub_image_3d(RED, GL_UNSIGNED_BYTE, buffer, 0, 0,
|
||||||
load_character(c);
|
m_next_layer, glyph.size.x, glyph.size.y);
|
||||||
}
|
|
||||||
m_text_texture->set_linear();
|
glyph.layer = m_next_layer++;
|
||||||
m_text_texture->set_clamp_to_edge(false, true, true);
|
|
||||||
|
glyph.uv_min = {0, 0};
|
||||||
|
glyph.uv_max = {glyph.size.x / float(CELL_SIZE),
|
||||||
|
glyph.size.y / float(CELL_SIZE)};
|
||||||
}
|
}
|
||||||
|
|
||||||
TextMesh Font::vertices(const std::string& text) {
|
Font& Font::get() {
|
||||||
static Font font;
|
static Font font;
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
TextMesh Font::vertices(const std::string& text) {
|
||||||
|
|
||||||
|
hb_buffer_t* buffer = hb_buffer_create();
|
||||||
|
hb_buffer_add_utf8(buffer, text.c_str(), text.size(), 0, text.size());
|
||||||
|
hb_buffer_guess_segment_properties(buffer);
|
||||||
|
hb_shape(m_hb_font, buffer, nullptr, 0);
|
||||||
|
|
||||||
|
unsigned int count;
|
||||||
|
|
||||||
|
hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &count);
|
||||||
|
|
||||||
|
hb_glyph_position_t* positions =
|
||||||
|
hb_buffer_get_glyph_positions(buffer, &count);
|
||||||
|
if (count == 0) {
|
||||||
|
hb_buffer_destroy(buffer);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
std::vector<Vertex2D> vertices;
|
std::vector<Vertex2D> vertices;
|
||||||
|
|
||||||
float min_x = std::numeric_limits<float>::max();
|
float min_x = std::numeric_limits<float>::max();
|
||||||
@@ -75,20 +103,18 @@ TextMesh Font::vertices(const std::string& text) {
|
|||||||
float pen_x = 0.0f;
|
float pen_x = 0.0f;
|
||||||
float pen_y = 0.0f;
|
float pen_y = 0.0f;
|
||||||
|
|
||||||
for (char8_t c : text) {
|
for (unsigned i = 0; i < count; i++) {
|
||||||
auto it = font.m_characters.find(c);
|
|
||||||
if (it == font.m_characters.end()) {
|
|
||||||
Logger::error("Can't find character {}", static_cast<char>(c));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Character& ch = it->second;
|
uint32_t glyph_index = infos[i].codepoint;
|
||||||
|
|
||||||
float xpos = pen_x + ch.bearing.x;
|
Glyph& glyph = load_glyph(glyph_index);
|
||||||
float ypos = pen_y - ch.bearing.y;
|
|
||||||
|
|
||||||
float w = ch.size.x;
|
float xpos = pen_x + positions[i].x_offset / 64.0f + glyph.bearing.x;
|
||||||
float h = ch.size.y;
|
|
||||||
|
float ypos = pen_y - positions[i].y_offset / 64.0f - glyph.bearing.y;
|
||||||
|
|
||||||
|
float w = glyph.size.x;
|
||||||
|
float h = glyph.size.y;
|
||||||
|
|
||||||
min_x = std::min(min_x, xpos);
|
min_x = std::min(min_x, xpos);
|
||||||
min_y = std::min(min_y, ypos);
|
min_y = std::min(min_y, ypos);
|
||||||
@@ -96,28 +122,29 @@ TextMesh Font::vertices(const std::string& text) {
|
|||||||
max_x = std::max(max_x, xpos + w);
|
max_x = std::max(max_x, xpos + w);
|
||||||
max_y = std::max(max_y, ypos + h);
|
max_y = std::max(max_y, ypos + h);
|
||||||
|
|
||||||
vertices.emplace_back(xpos, ypos + h, ch.uv_min.x, ch.uv_max.y,
|
vertices.emplace_back(xpos, ypos + h, glyph.uv_min.x, glyph.uv_max.y,
|
||||||
static_cast<float>(c));
|
static_cast<float>(glyph.layer));
|
||||||
vertices.emplace_back(xpos, ypos, ch.uv_min.x, ch.uv_min.y,
|
vertices.emplace_back(xpos, ypos, glyph.uv_min.x, glyph.uv_min.y,
|
||||||
static_cast<float>(c));
|
static_cast<float>(glyph.layer));
|
||||||
vertices.emplace_back(xpos + w, ypos, ch.uv_max.x, ch.uv_min.y,
|
vertices.emplace_back(xpos + w, ypos, glyph.uv_max.x, glyph.uv_min.y,
|
||||||
static_cast<float>(c));
|
static_cast<float>(glyph.layer));
|
||||||
|
|
||||||
vertices.emplace_back(xpos, ypos + h, ch.uv_min.x, ch.uv_max.y,
|
vertices.emplace_back(xpos, ypos + h, glyph.uv_min.x, glyph.uv_max.y,
|
||||||
static_cast<float>(c));
|
static_cast<float>(glyph.layer));
|
||||||
vertices.emplace_back(xpos + w, ypos, ch.uv_max.x, ch.uv_min.y,
|
vertices.emplace_back(xpos + w, ypos, glyph.uv_max.x, glyph.uv_min.y,
|
||||||
static_cast<float>(c));
|
static_cast<float>(glyph.layer));
|
||||||
vertices.emplace_back(xpos + w, ypos + h, ch.uv_max.x, ch.uv_max.y,
|
vertices.emplace_back(xpos + w, ypos + h, glyph.uv_max.x,
|
||||||
static_cast<float>(c));
|
glyph.uv_max.y, static_cast<float>(glyph.layer));
|
||||||
|
|
||||||
pen_x += (ch.advance >> 6);
|
pen_x += positions[i].x_advance / 64.0f;
|
||||||
|
pen_y += positions[i].y_advance / 64.0f;
|
||||||
}
|
}
|
||||||
// Top-left anchor point
|
// Top-left anchor point
|
||||||
for (auto& v : vertices) {
|
for (auto& v : vertices) {
|
||||||
v.x -= min_x;
|
v.x -= min_x;
|
||||||
v.y -= min_y;
|
v.y -= min_y;
|
||||||
}
|
}
|
||||||
|
hb_buffer_destroy(buffer);
|
||||||
return {std::move(vertices), max_x - min_x, max_y - min_y, 0, 0};
|
return {std::move(vertices), max_x - min_x, max_y - min_y, 0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
75
src/tools/system_locate.cpp
Normal file
75
src/tools/system_locate.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#include "Cubed/tools/system_locate.hpp"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
namespace Cubed {
|
||||||
|
|
||||||
|
SystemLocale get_system_locale() {
|
||||||
|
wchar_t locale_name[LOCALE_NAME_MAX_LENGTH]{};
|
||||||
|
|
||||||
|
if (GetUserDefaultLocaleName(locale_name, LOCALE_NAME_MAX_LENGTH) == 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
char utf8[LOCALE_NAME_MAX_LENGTH * 4]{};
|
||||||
|
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, locale_name, -1, utf8, sizeof(utf8),
|
||||||
|
nullptr, nullptr);
|
||||||
|
|
||||||
|
SystemLocale result;
|
||||||
|
result.locale = utf8;
|
||||||
|
|
||||||
|
auto pos = result.locale.find('-');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
result.country = result.locale.substr(pos + 1);
|
||||||
|
result.locale[pos] = '_'; // zh-CN -> zh_CN
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Cubed
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Cubed {
|
||||||
|
|
||||||
|
SystemLocale get_system_locale() {
|
||||||
|
SystemLocale result;
|
||||||
|
|
||||||
|
const char* vars[] = {std::getenv("LC_ALL"), std::getenv("LC_MESSAGES"),
|
||||||
|
std::getenv("LANG")};
|
||||||
|
|
||||||
|
for (const char* s : vars) {
|
||||||
|
if (s && *s) {
|
||||||
|
result.locale = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.locale.empty())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
auto dot = result.locale.find('.');
|
||||||
|
if (dot != std::string::npos)
|
||||||
|
result.locale.erase(dot);
|
||||||
|
|
||||||
|
auto at = result.locale.find('@');
|
||||||
|
if (at != std::string::npos)
|
||||||
|
result.locale.erase(at);
|
||||||
|
|
||||||
|
auto pos = result.locale.find('_');
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
result.country = result.locale.substr(pos + 1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Cubed
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,13 +1,23 @@
|
|||||||
#include "Cubed/ui/combo_button.hpp"
|
#include "Cubed/ui/combo_button.hpp"
|
||||||
|
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
|
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
ComboButton::ComboButton(Widget* parent) : Button(parent) {}
|
ComboButton::ComboButton(Widget* parent) : Button(parent) {}
|
||||||
|
|
||||||
Button& ComboButton::set_text(const std::string& text) {
|
ComboButton& ComboButton::set_combo_text(const std::string& key,
|
||||||
m_text = text;
|
const std::string& variable) {
|
||||||
|
m_key = key;
|
||||||
|
m_variable = variable;
|
||||||
update_text();
|
update_text();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
Button& ComboButton::set_text(const std::string&) {
|
||||||
|
ASSERT_MSG(false,
|
||||||
|
"don't use this function, use, set_combo_button instead.");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
bool ComboButton::handle_mouse_button_event(const MouseButtonEvent& e) {
|
bool ComboButton::handle_mouse_button_event(const MouseButtonEvent& e) {
|
||||||
if (e.action == KeyAction::PRESS && e.key == MouseKey::LEFT_BUTTON) {
|
if (e.action == KeyAction::PRESS && e.key == MouseKey::LEFT_BUTTON) {
|
||||||
if (m_hovered && m_enable) {
|
if (m_hovered && m_enable) {
|
||||||
@@ -47,8 +57,7 @@ void ComboButton::update_text() {
|
|||||||
if (m_suffix.empty()) {
|
if (m_suffix.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto text = m_text + ": " + m_suffix[m_index];
|
m_foreground->set_text(tr(m_key, arg(m_variable, m_suffix[m_index])));
|
||||||
m_foreground->set_text(text);
|
|
||||||
update_text_scale();
|
update_text_scale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Cubed/ui/credits_ui.hpp"
|
#include "Cubed/ui/credits_ui.hpp"
|
||||||
|
|
||||||
#include "Cubed/app.hpp"
|
#include "Cubed/app.hpp"
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/scene/credits_scene.hpp"
|
#include "Cubed/scene/credits_scene.hpp"
|
||||||
#include "Cubed/scene/scene_manager.hpp"
|
#include "Cubed/scene/scene_manager.hpp"
|
||||||
#include "Cubed/ui/button.hpp"
|
#include "Cubed/ui/button.hpp"
|
||||||
@@ -30,7 +31,7 @@ void CreditsUI::init() {
|
|||||||
"texture/ui/button001.png",
|
"texture/ui/button001.png",
|
||||||
m_scene.scene_manager().app().texture_manager());
|
m_scene.scene_manager().app().texture_manager());
|
||||||
|
|
||||||
button.set_text("Return");
|
button.set_text(tr("button.return"));
|
||||||
button.set_anchor(Anchor::BOTTOM_CENTER).set_offset({0, -20});
|
button.set_anchor(Anchor::BOTTOM_CENTER).set_offset({0, -20});
|
||||||
button.set_clicked([this]() { m_scene.scene_manager().request_pop(); });
|
button.set_clicked([this]() { m_scene.scene_manager().request_pop(); });
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,8 @@ void CreditsUI::init() {
|
|||||||
add_text("zstd");
|
add_text("zstd");
|
||||||
add_text("OpenAl Soft");
|
add_text("OpenAl Soft");
|
||||||
add_text("dr_libs");
|
add_text("dr_libs");
|
||||||
|
add_text("nlohmann/json");
|
||||||
|
add_text("HarfBuzz");
|
||||||
add_text("Music", 0.8f);
|
add_text("Music", 0.8f);
|
||||||
add_text("'Find a Peaceful Place' by ROZKOL (Free Music Archive), CC "
|
add_text("'Find a Peaceful Place' by ROZKOL (Free Music Archive), CC "
|
||||||
"BY 4.0.");
|
"BY 4.0.");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Cubed/ui/error_ui.hpp"
|
#include "Cubed/ui/error_ui.hpp"
|
||||||
|
|
||||||
#include "Cubed/app.hpp"
|
#include "Cubed/app.hpp"
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/scene/world_scene.hpp"
|
#include "Cubed/scene/world_scene.hpp"
|
||||||
#include "Cubed/ui/column_layout.hpp"
|
#include "Cubed/ui/column_layout.hpp"
|
||||||
#include "Cubed/ui/image.hpp"
|
#include "Cubed/ui/image.hpp"
|
||||||
@@ -38,7 +39,7 @@ void ErrorUI::init() {
|
|||||||
{
|
{
|
||||||
auto& button = layout.add_child<Button>();
|
auto& button = layout.add_child<Button>();
|
||||||
button.set_default_image(texture_manager);
|
button.set_default_image(texture_manager);
|
||||||
button.set_text("Return");
|
button.set_text(tr("button.return"));
|
||||||
button.set_clicked([this, &button]() {
|
button.set_clicked([this, &button]() {
|
||||||
button.set_enable(false);
|
button.set_enable(false);
|
||||||
m_scene.scene_manager().request_pop();
|
m_scene.scene_manager().request_pop();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Cubed/ui/host_game_ui.hpp"
|
#include "Cubed/ui/host_game_ui.hpp"
|
||||||
|
|
||||||
#include "Cubed/app.hpp"
|
#include "Cubed/app.hpp"
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/scene/host_game_scene.hpp"
|
#include "Cubed/scene/host_game_scene.hpp"
|
||||||
#include "Cubed/scene/scene_manager.hpp"
|
#include "Cubed/scene/scene_manager.hpp"
|
||||||
#include "Cubed/ui/button.hpp"
|
#include "Cubed/ui/button.hpp"
|
||||||
@@ -32,7 +33,7 @@ void HostGameUI::init() {
|
|||||||
param.host_game = true;
|
param.host_game = true;
|
||||||
{
|
{
|
||||||
auto& label = layout.add_child<Label>();
|
auto& label = layout.add_child<Label>();
|
||||||
label.set_text("Create A New World");
|
label.set_text(tr("hostgame.create_a_new_world"));
|
||||||
label.set_scale(0.7f);
|
label.set_scale(0.7f);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -45,7 +46,7 @@ void HostGameUI::init() {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto& text_seed = layout.add_child<TextField>();
|
auto& text_seed = layout.add_child<TextField>();
|
||||||
text_seed.set_show_text("WorldSeed");
|
text_seed.set_show_text(tr("hostgame.world_seed"));
|
||||||
text_seed.set_app(&m_scene.scene_manager().app());
|
text_seed.set_app(&m_scene.scene_manager().app());
|
||||||
text_seed.set_default_image(texture_manager);
|
text_seed.set_default_image(texture_manager);
|
||||||
text_seed.set_on_finish([this, &text_seed]() {
|
text_seed.set_on_finish([this, &text_seed]() {
|
||||||
@@ -67,7 +68,7 @@ void HostGameUI::init() {
|
|||||||
{
|
{
|
||||||
auto& text_port = layout.add_child<TextField>();
|
auto& text_port = layout.add_child<TextField>();
|
||||||
text_port.set_default_image(texture_manager);
|
text_port.set_default_image(texture_manager);
|
||||||
text_port.set_show_text("Port: 25530");
|
text_port.set_show_text(tr("hostgame.port"));
|
||||||
text_port.set_app(&m_scene.scene_manager().app());
|
text_port.set_app(&m_scene.scene_manager().app());
|
||||||
text_port.set_on_finish([this, &text_port]() {
|
text_port.set_on_finish([this, &text_port]() {
|
||||||
int port = 25530;
|
int port = 25530;
|
||||||
@@ -93,7 +94,7 @@ void HostGameUI::init() {
|
|||||||
{
|
{
|
||||||
auto& button = layout.add_child<Button>();
|
auto& button = layout.add_child<Button>();
|
||||||
button.set_default_image(texture_manager);
|
button.set_default_image(texture_manager);
|
||||||
button.set_text("Start Game");
|
button.set_text(tr("hostgame.create_world"));
|
||||||
button.set_clicked([this, &button]() {
|
button.set_clicked([this, &button]() {
|
||||||
button.set_enable(false);
|
button.set_enable(false);
|
||||||
m_scene.scene_manager().request_change(SceneType::WORLD);
|
m_scene.scene_manager().request_change(SceneType::WORLD);
|
||||||
@@ -102,7 +103,7 @@ void HostGameUI::init() {
|
|||||||
{
|
{
|
||||||
auto& button = layout.add_child<Button>();
|
auto& button = layout.add_child<Button>();
|
||||||
button.set_default_image(texture_manager);
|
button.set_default_image(texture_manager);
|
||||||
button.set_text("Return");
|
button.set_text(tr("button.return"));
|
||||||
button.set_clicked([this, &button]() {
|
button.set_clicked([this, &button]() {
|
||||||
button.set_enable(false);
|
button.set_enable(false);
|
||||||
m_scene.scene_manager().request_pop();
|
m_scene.scene_manager().request_pop();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Cubed/ui/join_game_ui.hpp"
|
#include "Cubed/ui/join_game_ui.hpp"
|
||||||
|
|
||||||
#include "Cubed/app.hpp"
|
#include "Cubed/app.hpp"
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/scene/join_game_scene.hpp"
|
#include "Cubed/scene/join_game_scene.hpp"
|
||||||
#include "Cubed/ui/button.hpp"
|
#include "Cubed/ui/button.hpp"
|
||||||
#include "Cubed/ui/column_layout.hpp"
|
#include "Cubed/ui/column_layout.hpp"
|
||||||
@@ -30,7 +31,7 @@ void JoinGameUI::init() {
|
|||||||
param.host_game = false;
|
param.host_game = false;
|
||||||
{
|
{
|
||||||
auto& label = layout.add_child<Label>();
|
auto& label = layout.add_child<Label>();
|
||||||
label.set_text("Join A World");
|
label.set_text(tr("joingame.join_a_world"));
|
||||||
label.set_scale(0.7f);
|
label.set_scale(0.7f);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -43,7 +44,7 @@ void JoinGameUI::init() {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto& text_ip = layout.add_child<TextField>();
|
auto& text_ip = layout.add_child<TextField>();
|
||||||
text_ip.set_show_text("Server Ip");
|
text_ip.set_show_text(tr("joingame.server_ip"));
|
||||||
text_ip.set_default_image(texture_manager);
|
text_ip.set_default_image(texture_manager);
|
||||||
text_ip.set_app(&m_scene.scene_manager().app());
|
text_ip.set_app(&m_scene.scene_manager().app());
|
||||||
text_ip.set_on_finish([this, &text_ip]() {
|
text_ip.set_on_finish([this, &text_ip]() {
|
||||||
@@ -96,7 +97,7 @@ void JoinGameUI::init() {
|
|||||||
{
|
{
|
||||||
auto& button = layout.add_child<Button>();
|
auto& button = layout.add_child<Button>();
|
||||||
button.set_default_image(texture_manager);
|
button.set_default_image(texture_manager);
|
||||||
button.set_text("Join Game");
|
button.set_text(tr("joingame.join_world"));
|
||||||
button.set_clicked([this, &button]() {
|
button.set_clicked([this, &button]() {
|
||||||
button.set_enable(false);
|
button.set_enable(false);
|
||||||
m_scene.scene_manager().request_change(SceneType::WORLD);
|
m_scene.scene_manager().request_change(SceneType::WORLD);
|
||||||
@@ -105,7 +106,7 @@ void JoinGameUI::init() {
|
|||||||
{
|
{
|
||||||
auto& button = layout.add_child<Button>();
|
auto& button = layout.add_child<Button>();
|
||||||
button.set_default_image(texture_manager);
|
button.set_default_image(texture_manager);
|
||||||
button.set_text("Return");
|
button.set_text(tr("button.return"));
|
||||||
button.set_clicked([this, &button]() {
|
button.set_clicked([this, &button]() {
|
||||||
button.set_enable(false);
|
button.set_enable(false);
|
||||||
m_scene.scene_manager().request_pop();
|
m_scene.scene_manager().request_pop();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ void Label::on_update(float dt) { (void)dt; }
|
|||||||
void Label::on_render(Renderer& renderer) { renderer.render_lable(*this); }
|
void Label::on_render(Renderer& renderer) { renderer.render_lable(*this); }
|
||||||
|
|
||||||
void Label::update_vertices() {
|
void Label::update_vertices() {
|
||||||
auto textmesh = Font::vertices(m_text.text);
|
auto textmesh = Font::get().vertices(m_text.text);
|
||||||
m_data.m_vertices = std::move(textmesh.vertices);
|
m_data.m_vertices = std::move(textmesh.vertices);
|
||||||
|
|
||||||
m_offset_x = textmesh.min_x;
|
m_offset_x = textmesh.min_x;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Cubed/ui/main_menu_ui_manager.hpp"
|
#include "Cubed/ui/main_menu_ui_manager.hpp"
|
||||||
|
|
||||||
#include "Cubed/app.hpp"
|
#include "Cubed/app.hpp"
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/render/renderer.hpp"
|
#include "Cubed/render/renderer.hpp"
|
||||||
#include "Cubed/scene/main_menu_scene.hpp"
|
#include "Cubed/scene/main_menu_scene.hpp"
|
||||||
#include "Cubed/scene/scene_manager.hpp"
|
#include "Cubed/scene/scene_manager.hpp"
|
||||||
@@ -32,7 +33,7 @@ void MainMenuUIManager::init() {
|
|||||||
|
|
||||||
start_game_button.set_background_image("texture/ui/button001.png",
|
start_game_button.set_background_image("texture/ui/button001.png",
|
||||||
texture_manager);
|
texture_manager);
|
||||||
start_game_button.set_text("Host Game");
|
start_game_button.set_text(tr("menu.main.host_game"));
|
||||||
start_game_button.set_clicked([this, &start_game_button]() {
|
start_game_button.set_clicked([this, &start_game_button]() {
|
||||||
start_game_button.set_enable(false);
|
start_game_button.set_enable(false);
|
||||||
m_scene.scene_manager().request_push(SceneType::HOST_GAME);
|
m_scene.scene_manager().request_push(SceneType::HOST_GAME);
|
||||||
@@ -44,7 +45,7 @@ void MainMenuUIManager::init() {
|
|||||||
|
|
||||||
start_game_button.set_background_image("texture/ui/button001.png",
|
start_game_button.set_background_image("texture/ui/button001.png",
|
||||||
texture_manager);
|
texture_manager);
|
||||||
start_game_button.set_text("Join Game");
|
start_game_button.set_text(tr("menu.main.join_game"));
|
||||||
start_game_button.set_clicked([this, &start_game_button]() {
|
start_game_button.set_clicked([this, &start_game_button]() {
|
||||||
start_game_button.set_enable(false);
|
start_game_button.set_enable(false);
|
||||||
m_scene.scene_manager().request_push(SceneType::JOIN_GAME);
|
m_scene.scene_manager().request_push(SceneType::JOIN_GAME);
|
||||||
@@ -54,7 +55,7 @@ void MainMenuUIManager::init() {
|
|||||||
{
|
{
|
||||||
auto& button = layout.add_child<Button>();
|
auto& button = layout.add_child<Button>();
|
||||||
button.set_default_image(texture_manager);
|
button.set_default_image(texture_manager);
|
||||||
button.set_text("Settings");
|
button.set_text(tr("menu.settings"));
|
||||||
button.set_clicked([this, &button]() {
|
button.set_clicked([this, &button]() {
|
||||||
button.set_enable(false);
|
button.set_enable(false);
|
||||||
m_scene.scene_manager().request_push(SceneType::SETTINGS);
|
m_scene.scene_manager().request_push(SceneType::SETTINGS);
|
||||||
@@ -67,7 +68,7 @@ void MainMenuUIManager::init() {
|
|||||||
|
|
||||||
button.set_background_image("texture/ui/button001.png",
|
button.set_background_image("texture/ui/button001.png",
|
||||||
texture_manager);
|
texture_manager);
|
||||||
button.set_text("Credits");
|
button.set_text(tr("menu.main.credits"));
|
||||||
button.set_clicked([this, &button]() {
|
button.set_clicked([this, &button]() {
|
||||||
button.set_enable(false);
|
button.set_enable(false);
|
||||||
m_scene.scene_manager().request_push(SceneType::CREDITS);
|
m_scene.scene_manager().request_push(SceneType::CREDITS);
|
||||||
@@ -78,7 +79,7 @@ void MainMenuUIManager::init() {
|
|||||||
auto& exit_game = layout.add_child<Button>();
|
auto& exit_game = layout.add_child<Button>();
|
||||||
exit_game.set_background_image("texture/ui/button001.png",
|
exit_game.set_background_image("texture/ui/button001.png",
|
||||||
texture_manager);
|
texture_manager);
|
||||||
exit_game.set_text("Quit");
|
exit_game.set_text(tr("menu.main.quit"));
|
||||||
|
|
||||||
exit_game.set_clicked([this]() {
|
exit_game.set_clicked([this]() {
|
||||||
m_scene.scene_manager().app().window().should_close_window();
|
m_scene.scene_manager().app().window().should_close_window();
|
||||||
@@ -94,18 +95,21 @@ void MainMenuUIManager::init() {
|
|||||||
info_layout.set_child_anchor(ColumnLayoutAnchor::LEFT);
|
info_layout.set_child_anchor(ColumnLayoutAnchor::LEFT);
|
||||||
auto& player_name = info_layout.add_child<Label>();
|
auto& player_name = info_layout.add_child<Label>();
|
||||||
player_name.set_scale(SCALE);
|
player_name.set_scale(SCALE);
|
||||||
player_name.set_text(std::format(
|
|
||||||
"Player: {}", m_scene.scene_manager().app().argument().player));
|
player_name.set_text(
|
||||||
|
tr("menu.main.player_name",
|
||||||
|
arg("name", m_scene.scene_manager().app().argument().player)));
|
||||||
|
|
||||||
auto& version = info_layout.add_child<Label>();
|
auto& version = info_layout.add_child<Label>();
|
||||||
version.set_scale(SCALE);
|
version.set_scale(SCALE);
|
||||||
std::string version_str = "Cubed: " CUBED_VERSION;
|
std::string version_str = CUBED_VERSION;
|
||||||
#ifdef DEBUG_MODE
|
#ifdef DEBUG_MODE
|
||||||
version_str.append("-debug");
|
version_str.append("-debug");
|
||||||
#else
|
#else
|
||||||
version_str.append("-release");
|
version_str.append("-release");
|
||||||
#endif
|
#endif
|
||||||
version.set_text(version_str);
|
version.set_text(
|
||||||
|
tr("menu.main.cubed_version", arg("version", version_str)));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_root_widget = std::move(image);
|
m_root_widget = std::move(image);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "Cubed/app.hpp"
|
#include "Cubed/app.hpp"
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/render/renderer.hpp"
|
#include "Cubed/render/renderer.hpp"
|
||||||
#include "Cubed/scene/scene_manager.hpp"
|
#include "Cubed/scene/scene_manager.hpp"
|
||||||
#include "Cubed/scene/world_scene.hpp"
|
#include "Cubed/scene/world_scene.hpp"
|
||||||
@@ -23,20 +24,20 @@ void PauseMenuUIManager::init() {
|
|||||||
layout.set_spacing(20);
|
layout.set_spacing(20);
|
||||||
{
|
{
|
||||||
auto& title = layout.add_child<Label>();
|
auto& title = layout.add_child<Label>();
|
||||||
title.set_text("Pause Menu");
|
title.set_text(tr("menu.pause.pause_menu"));
|
||||||
title.set_scale(0.75f);
|
title.set_scale(0.75f);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto& button = layout.add_child<Button>();
|
auto& button = layout.add_child<Button>();
|
||||||
button.set_default_image(texture_manager);
|
button.set_default_image(texture_manager);
|
||||||
button.set_text("Back to Game");
|
button.set_text(tr("menu.pause.back_to_game"));
|
||||||
button.set_clicked([this]() { m_scene.set_pause(false); });
|
button.set_clicked([this]() { m_scene.set_pause(false); });
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto& button = layout.add_child<Button>();
|
auto& button = layout.add_child<Button>();
|
||||||
button.set_default_image(texture_manager);
|
button.set_default_image(texture_manager);
|
||||||
button.set_text("Settings");
|
button.set_text(tr("menu.settings"));
|
||||||
button.set_clicked([this, &button]() {
|
button.set_clicked([this, &button]() {
|
||||||
button.set_enable(false);
|
button.set_enable(false);
|
||||||
m_scene.scene_manager().request_push(SceneType::SETTINGS);
|
m_scene.scene_manager().request_push(SceneType::SETTINGS);
|
||||||
@@ -49,7 +50,7 @@ void PauseMenuUIManager::init() {
|
|||||||
|
|
||||||
back_main.set_background_image("texture/ui/button001.png",
|
back_main.set_background_image("texture/ui/button001.png",
|
||||||
texture_manager);
|
texture_manager);
|
||||||
back_main.set_text("Return to Menu");
|
back_main.set_text(tr("menu.pause.return_to_main_menu"));
|
||||||
back_main.set_clicked([this, &back_main]() {
|
back_main.set_clicked([this, &back_main]() {
|
||||||
back_main.set_enable(false);
|
back_main.set_enable(false);
|
||||||
m_scene.scene_manager().request_pop();
|
m_scene.scene_manager().request_pop();
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#include "Cubed/ui/settings_ui.hpp"
|
#include "Cubed/ui/settings_ui.hpp"
|
||||||
|
|
||||||
#include "Cubed/app.hpp"
|
#include "Cubed/app.hpp"
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/render/renderer.hpp"
|
#include "Cubed/render/renderer.hpp"
|
||||||
#include "Cubed/scene/scene_manager.hpp"
|
#include "Cubed/scene/scene_manager.hpp"
|
||||||
#include "Cubed/scene/settings_scene.hpp"
|
#include "Cubed/scene/settings_scene.hpp"
|
||||||
|
#include "Cubed/tools/system_locate.hpp"
|
||||||
#include "Cubed/ui/button.hpp"
|
#include "Cubed/ui/button.hpp"
|
||||||
#include "Cubed/ui/column_layout.hpp"
|
#include "Cubed/ui/column_layout.hpp"
|
||||||
#include "Cubed/ui/combo_button.hpp"
|
#include "Cubed/ui/combo_button.hpp"
|
||||||
@@ -39,33 +41,34 @@ void SettingsUI::init() {
|
|||||||
};
|
};
|
||||||
{
|
{
|
||||||
auto& title = layout.add_child<Label>();
|
auto& title = layout.add_child<Label>();
|
||||||
title.set_text("Setting");
|
title.set_text(tr("settings.title"));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto& fov = layout.add_child<Slider>();
|
auto& fov = layout.add_child<Slider>();
|
||||||
fov.set_slider(&v.fov, 20.0f, 120.0f);
|
fov.set_slider(&v.fov, 20.0f, 120.0f);
|
||||||
set_default_slider_image(fov);
|
set_default_slider_image(fov);
|
||||||
fov.set_text("Fov");
|
fov.set_slider_text("settings.fov", "fov");
|
||||||
|
|
||||||
auto& sensitivity = layout.add_child<Slider>();
|
auto& sensitivity = layout.add_child<Slider>();
|
||||||
sensitivity.set_slider(&v.mouse_sensitivity, 0.01f, 1.0f);
|
sensitivity.set_slider(&v.mouse_sensitivity, 0.01f, 1.0f);
|
||||||
set_default_slider_image(sensitivity);
|
set_default_slider_image(sensitivity);
|
||||||
sensitivity.set_text("Sensitivity");
|
sensitivity.set_slider_text("settings.mouse_sensitivity",
|
||||||
|
"sensitivity");
|
||||||
|
|
||||||
auto& distance = layout.add_child<Slider>();
|
auto& distance = layout.add_child<Slider>();
|
||||||
distance.set_slider(&v.rendering_distance, 2, 64);
|
distance.set_slider(&v.rendering_distance, 2, 64);
|
||||||
set_default_slider_image(distance);
|
set_default_slider_image(distance);
|
||||||
distance.set_text("Distance");
|
distance.set_slider_text("settings.distance", "distance");
|
||||||
|
|
||||||
auto& music = layout.add_child<Slider>();
|
auto& music = layout.add_child<Slider>();
|
||||||
music.set_slider(&v.music, 0.0f, 1.0f);
|
music.set_slider(&v.music, 0.0f, 1.0f);
|
||||||
set_default_slider_image(music);
|
set_default_slider_image(music);
|
||||||
music.set_text("Music");
|
music.set_slider_text("settings.music_volume", "music");
|
||||||
|
|
||||||
auto& sfx = layout.add_child<Slider>();
|
auto& sfx = layout.add_child<Slider>();
|
||||||
sfx.set_slider(&v.sfx, 0.0f, 1.0f);
|
sfx.set_slider(&v.sfx, 0.0f, 1.0f);
|
||||||
set_default_slider_image(sfx);
|
set_default_slider_image(sfx);
|
||||||
sfx.set_text("SFX");
|
sfx.set_slider_text("settings.sfx_volume", "sfx");
|
||||||
}
|
}
|
||||||
auto& config = m_scene.scene_manager().app().config();
|
auto& config = m_scene.scene_manager().app().config();
|
||||||
{
|
{
|
||||||
@@ -73,13 +76,13 @@ void SettingsUI::init() {
|
|||||||
bool full = config.get("window.fullscreen", false);
|
bool full = config.get("window.fullscreen", false);
|
||||||
auto& fullscreen = layout.add_child<ComboButton>();
|
auto& fullscreen = layout.add_child<ComboButton>();
|
||||||
fullscreen.set_index(!full ? 0 : 1);
|
fullscreen.set_index(!full ? 0 : 1);
|
||||||
fullscreen.set_text("Fullscreen");
|
fullscreen.set_combo_text("settings.fullscreen", "state");
|
||||||
std::vector<std::pair<std::string, std::function<void()>>> comb;
|
std::vector<std::pair<std::string, std::function<void()>>> comb;
|
||||||
comb.emplace_back("off", [&config, this]() {
|
comb.emplace_back(tr("common.off"), [&config, this]() {
|
||||||
config.set("window.fullscreen", false);
|
config.set("window.fullscreen", false);
|
||||||
m_scene.scene_manager().app().window().reload_config();
|
m_scene.scene_manager().app().window().reload_config();
|
||||||
});
|
});
|
||||||
comb.emplace_back("on", [&config, this]() {
|
comb.emplace_back(tr("common.on"), [&config, this]() {
|
||||||
config.set("window.fullscreen", true);
|
config.set("window.fullscreen", true);
|
||||||
m_scene.scene_manager().app().window().reload_config();
|
m_scene.scene_manager().app().window().reload_config();
|
||||||
});
|
});
|
||||||
@@ -91,13 +94,13 @@ void SettingsUI::init() {
|
|||||||
bool enable_vsync = config.get("window.V-Sync", true);
|
bool enable_vsync = config.get("window.V-Sync", true);
|
||||||
auto& vsync = layout.add_child<ComboButton>();
|
auto& vsync = layout.add_child<ComboButton>();
|
||||||
vsync.set_default_image(texture_manager);
|
vsync.set_default_image(texture_manager);
|
||||||
vsync.set_text("V-Sync");
|
vsync.set_combo_text("settings.vsync", "state");
|
||||||
std::vector<ComboPair> combos;
|
std::vector<ComboPair> combos;
|
||||||
combos.emplace_back("off", [this, &config]() {
|
combos.emplace_back(tr("common.off"), [this, &config]() {
|
||||||
config.set("window.V-Sync", false);
|
config.set("window.V-Sync", false);
|
||||||
m_scene.scene_manager().app().window().reload_config();
|
m_scene.scene_manager().app().window().reload_config();
|
||||||
});
|
});
|
||||||
combos.emplace_back("on", [this, &config]() {
|
combos.emplace_back(tr("common.on"), [this, &config]() {
|
||||||
config.set("window.V-Sync", true);
|
config.set("window.V-Sync", true);
|
||||||
m_scene.scene_manager().app().window().reload_config();
|
m_scene.scene_manager().app().window().reload_config();
|
||||||
});
|
});
|
||||||
@@ -124,12 +127,12 @@ void SettingsUI::init() {
|
|||||||
|
|
||||||
auto& aniso_button = layout.add_child<ComboButton>();
|
auto& aniso_button = layout.add_child<ComboButton>();
|
||||||
aniso_button.set_default_image(texture_manager);
|
aniso_button.set_default_image(texture_manager);
|
||||||
aniso_button.set_text("Aniso");
|
aniso_button.set_combo_text("settings.aniso", "aniso");
|
||||||
aniso_button.set_index(cur_index);
|
aniso_button.set_index(cur_index);
|
||||||
std::vector<ComboPair> combos;
|
std::vector<ComboPair> combos;
|
||||||
auto get_suffix = [](int i) -> std::pair<std::string, int> {
|
auto get_suffix = [](int i) -> std::pair<std::string, int> {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
return {"off", 1};
|
return {tr("common.off"), 1};
|
||||||
}
|
}
|
||||||
int n = 1;
|
int n = 1;
|
||||||
for (int j = 0; j < i; j++) {
|
for (int j = 0; j < i; j++) {
|
||||||
@@ -147,11 +150,47 @@ void SettingsUI::init() {
|
|||||||
}
|
}
|
||||||
aniso_button.set_combos(combos);
|
aniso_button.set_combos(combos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& lang_button = layout.add_child<ComboButton>();
|
||||||
|
auto& label = layout.add_child<Label>();
|
||||||
|
label.set_text(tr("settings.restart_game_info"));
|
||||||
|
label.set_scale(0.7f);
|
||||||
|
label.set_color(Color::RED);
|
||||||
|
label.set_visible(false);
|
||||||
|
lang_button.set_default_image(texture_manager);
|
||||||
|
lang_button.set_combo_text("settings.language", "lang");
|
||||||
|
auto locate = get_system_locale();
|
||||||
|
std::string default_value = "en_US";
|
||||||
|
if (locate.country == "CN") {
|
||||||
|
default_value = "zh_CN";
|
||||||
|
}
|
||||||
|
auto lang = config.get("language", default_value);
|
||||||
|
int index = 0;
|
||||||
|
if (lang == "en_US") {
|
||||||
|
index = 0;
|
||||||
|
} else if (lang == "zh_CN") {
|
||||||
|
index = 1;
|
||||||
|
}
|
||||||
|
lang_button.set_index(index);
|
||||||
|
std::vector<ComboPair> combos;
|
||||||
|
|
||||||
|
combos.emplace_back("English", [&label, &config]() {
|
||||||
|
config.set("language", std::string("en_US"));
|
||||||
|
label.set_visible(true);
|
||||||
|
});
|
||||||
|
combos.emplace_back("简体中文", [&label, &config]() {
|
||||||
|
config.set("language", std::string("zh_CN"));
|
||||||
|
label.set_visible(true);
|
||||||
|
});
|
||||||
|
lang_button.set_combos(combos);
|
||||||
|
}
|
||||||
|
|
||||||
auto& return_button = layout.add_child<Button>();
|
auto& return_button = layout.add_child<Button>();
|
||||||
return_button.set_background_image("texture/ui/button001.png",
|
return_button.set_background_image("texture/ui/button001.png",
|
||||||
texture_manager);
|
texture_manager);
|
||||||
|
|
||||||
return_button.set_text("Save and Return");
|
return_button.set_text(tr("button.done"));
|
||||||
return_button.set_clicked(
|
return_button.set_clicked(
|
||||||
[this]() { m_scene.scene_manager().request_pop(); });
|
[this]() { m_scene.scene_manager().request_pop(); });
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "Cubed/ui/slider.hpp"
|
#include "Cubed/ui/slider.hpp"
|
||||||
|
|
||||||
|
#include "Cubed/localization.hpp"
|
||||||
#include "Cubed/tools/cubed_assert.hpp"
|
#include "Cubed/tools/cubed_assert.hpp"
|
||||||
#include "Cubed/tools/log.hpp"
|
#include "Cubed/tools/log.hpp"
|
||||||
|
|
||||||
@@ -84,8 +85,10 @@ Slider& Slider::set_height(float h) {
|
|||||||
update_text_scale();
|
update_text_scale();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
Slider& Slider::set_text(const std::string& text) {
|
Slider& Slider::set_slider_text(const std::string& key,
|
||||||
m_text = text;
|
const std::string& variable) {
|
||||||
|
m_key = key;
|
||||||
|
m_variable = variable;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
Slider& Slider::set_track_image(const std::string& path,
|
Slider& Slider::set_track_image(const std::string& path,
|
||||||
@@ -164,7 +167,7 @@ void Slider::on_update(float dt) {
|
|||||||
}
|
}
|
||||||
float range = m_max - m_min;
|
float range = m_max - m_min;
|
||||||
if (range <= 0.0f) {
|
if (range <= 0.0f) {
|
||||||
Logger::error("Slider {} Range {} is <= 0.0f", m_text, range);
|
Logger::error("Slider {} Range {} is <= 0.0f", m_key, range);
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -184,11 +187,12 @@ void Slider::on_update(float dt) {
|
|||||||
m_thumb->set_offset({offset, 0});
|
m_thumb->set_offset({offset, 0});
|
||||||
if (m_label) {
|
if (m_label) {
|
||||||
if (m_type == ValueType::FLOAT && m_float_value) {
|
if (m_type == ValueType::FLOAT && m_float_value) {
|
||||||
m_label->set_text(
|
|
||||||
std::format("{}: {:.2f}", m_text, *m_float_value));
|
m_label->set_text(tr(
|
||||||
|
m_key, arg(m_variable, std::format("{:.2f}", *m_float_value))));
|
||||||
}
|
}
|
||||||
if (m_type == ValueType::INT && m_int_value) {
|
if (m_type == ValueType::INT && m_int_value) {
|
||||||
m_label->set_text(std::format("{}: {}", m_text, *m_int_value));
|
m_label->set_text(tr(m_key, arg(m_variable, *m_int_value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_track) {
|
if (m_track) {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
"dependencies": [
|
"dependencies": [
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"zstd",
|
"zstd",
|
||||||
"openal-soft"
|
"openal-soft",
|
||||||
|
"harfbuzz",
|
||||||
|
"freetype"
|
||||||
],
|
],
|
||||||
"builtin-baseline": "a0400024711b283056538ac19ced80b91a83c24c"
|
"builtin-baseline": "a0400024711b283056538ac19ced80b91a83c24c"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user