Merge pull request #502 from MihailRis/devel

begin 0.28 development
This commit is contained in:
MihailRis 2025-04-02 20:09:43 +03:00 committed by GitHub
commit 531334f059
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
154 changed files with 3767 additions and 2810 deletions

View File

@ -1,6 +1,6 @@
# Documentation
Documentation for release 0.27.
Documentation for release 0.28.
## Sections

View File

@ -11,7 +11,7 @@ Subsections:
- [Libraries](#)
- [app](scripting/builtins/libapp.md)
- [base64](scripting/builtins/libbase64.md)
- [bjson, json, toml](scripting/filesystem.md)
- [bjson, json, toml, yaml](scripting/filesystem.md)
- [block](scripting/builtins/libblock.md)
- [byteutil](scripting/builtins/libbyteutil.md)
- [cameras](scripting/builtins/libcameras.md)

View File

@ -36,6 +36,22 @@ toml.parse(code: str) -> table
Parses a TOML string into a table.
## *yaml* library
The library contains functions for serializing and deserializing tables:
```python
yaml.tostring(object: table) -> str
```
Serializes an object into a YAML string.
```python
yaml.parse(code: str) -> table
```
Parses a YAML string into a table.
## *bjson* library
The library contains functions for working with the binary data exchange format [vcbjson](../../specs/binary_json_spec.md).

View File

@ -1,6 +1,6 @@
# Документация
Документация версии 0.27.
Документация версии 0.28.
## Разделы

View File

@ -11,7 +11,7 @@
- [Библиотеки](#)
- [app](scripting/builtins/libapp.md)
- [base64](scripting/builtins/libbase64.md)
- [bjson, json, toml](scripting/filesystem.md)
- [bjson, json, toml, yaml](scripting/filesystem.md)
- [block](scripting/builtins/libblock.md)
- [byteutil](scripting/builtins/libbyteutil.md)
- [cameras](scripting/builtins/libcameras.md)

View File

@ -36,6 +36,22 @@ toml.parse(code: str) -> table
Парсит TOML строку в таблицу.
## Библиотека yaml
Библиотека содержит функции для сериализации и десериализации таблиц:
```python
yaml.tostring(object: table) -> str
```
Сериализует объект в YAML строку.
```python
yaml.parse(code: str) -> table
```
Парсит YAML строку в таблицу.
## Библиотека bjson
Библиотека содержит функции для работы с двоичным форматом обмена данными [vcbjson](../../specs/binary_json_spec.md).

View File

@ -1,6 +1,6 @@
{
"id": "base",
"title": "Base",
"version": "0.27",
"version": "0.28",
"description": "basic content package"
}

View File

@ -19,13 +19,14 @@
#include "items/ItemDef.hpp"
#include "Assets.hpp"
#include "assetload_funcs.hpp"
#include "engine/Engine.hpp"
namespace fs = std::filesystem;
static debug::Logger logger("assets-loader");
AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths)
: assets(assets), paths(paths) {
AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths)
: engine(engine), assets(assets), paths(paths) {
addLoader(AssetType::SHADER, assetload::shader);
addLoader(AssetType::TEXTURE, assetload::texture);
addLoader(AssetType::FONT, assetload::font);
@ -73,7 +74,7 @@ void AssetsLoader::loadNext() {
aloader_func loader = getLoader(entry.tag);
auto postfunc =
loader(this, paths, entry.filename, entry.alias, entry.config);
postfunc(assets);
postfunc(&assets);
entries.pop();
} catch (std::runtime_error& err) {
logger.error() << err.what();
@ -101,7 +102,7 @@ static void add_layouts(
AssetType::LAYOUT,
file.string(),
name,
std::make_shared<LayoutCfg>(env)
std::make_shared<LayoutCfg>(&loader.getEngine().getGUI(), env)
);
}
}
@ -199,7 +200,7 @@ void AssetsLoader::processPreloadConfig(const io::path& file) {
}
void AssetsLoader::processPreloadConfigs(const Content* content) {
auto preloadFile = paths->getMainRoot() / "preload.json";
io::path preloadFile = "res:preload.json";
if (io::exists(preloadFile)) {
processPreloadConfig(preloadFile);
}
@ -211,7 +212,7 @@ void AssetsLoader::processPreloadConfigs(const Content* content) {
continue;
}
const auto& pack = entry.second;
auto preloadFile = pack->getInfo().folder / "preload.json";
preloadFile = pack->getInfo().folder / "preload.json";
if (io::exists(preloadFile)) {
processPreloadConfig(preloadFile);
}
@ -296,7 +297,11 @@ bool AssetsLoader::loadExternalTexture(
return false;
}
const ResPaths* AssetsLoader::getPaths() const {
Engine& AssetsLoader::getEngine() {
return engine;
}
const ResPaths& AssetsLoader::getPaths() const {
return paths;
}
@ -324,7 +329,7 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone) {
std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>(
"assets-loader-pool",
[=]() { return std::make_shared<LoaderWorker>(this); },
[=](const assetload::postfunc& func) { func(assets); }
[this](const assetload::postfunc& func) { func(&assets); }
);
pool->setOnComplete(std::move(onDone));
while (!entries.empty()) {

View File

@ -18,6 +18,11 @@
class ResPaths;
class AssetsLoader;
class Content;
class Engine;
namespace gui {
class GUI;
}
struct AssetCfg {
virtual ~AssetCfg() {
@ -25,9 +30,10 @@ struct AssetCfg {
};
struct LayoutCfg : AssetCfg {
gui::GUI* gui;
scriptenv env;
LayoutCfg(scriptenv env) : env(std::move(env)) {
LayoutCfg(gui::GUI* gui, scriptenv env) : gui(gui), env(std::move(env)) {
}
};
@ -51,7 +57,7 @@ struct AtlasCfg : AssetCfg {
using aloader_func = std::function<
assetload::
postfunc(AssetsLoader*, const ResPaths*, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
postfunc(AssetsLoader*, const ResPaths&, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
struct aloader_entry {
AssetType tag;
@ -61,11 +67,12 @@ struct aloader_entry {
};
class AssetsLoader {
Assets* assets;
Engine& engine;
Assets& assets;
std::map<AssetType, aloader_func> loaders;
std::queue<aloader_entry> entries;
std::set<std::pair<AssetType, std::string>> enqueued;
const ResPaths* paths;
const ResPaths& paths;
void tryAddSound(const std::string& name);
@ -76,7 +83,7 @@ class AssetsLoader {
void processPreloadConfig(const io::path& file);
void processPreloadConfigs(const Content* content);
public:
AssetsLoader(Assets* assets, const ResPaths* paths);
AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths);
void addLoader(AssetType tag, aloader_func func);
/// @brief Enqueue asset load
@ -98,7 +105,7 @@ public:
std::shared_ptr<Task> startTask(runnable onDone);
const ResPaths* getPaths() const;
const ResPaths& getPaths() const;
aloader_func getLoader(AssetType tag);
/// @brief Enqueue core and content assets
@ -111,4 +118,6 @@ public:
const std::string& name,
const std::vector<io::path>& alternatives
);
Engine& getEngine();
};

View File

@ -34,7 +34,7 @@ namespace fs = std::filesystem;
static bool load_animation(
Assets* assets,
const ResPaths* paths,
const ResPaths& paths,
const std::string& atlasName,
const std::string& directory,
const std::string& name,
@ -43,14 +43,14 @@ static bool load_animation(
assetload::postfunc assetload::texture(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
auto actualFile = paths->find(filename + ".png");
auto actualFile = paths.find(filename + ".png");
try {
std::shared_ptr<ImageData> image(imageio::read(actualFile).release());
std::shared_ptr<ImageData> image(imageio::read(actualFile));
return [name, image, actualFile](auto assets) {
assets->store(Texture::from(image.get()), name);
};
@ -62,13 +62,13 @@ assetload::postfunc assetload::texture(
assetload::postfunc assetload::shader(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
io::path vertexFile = paths->find(filename + ".glslv");
io::path fragmentFile = paths->find(filename + ".glslf");
io::path vertexFile = paths.find(filename + ".glslv");
io::path fragmentFile = paths.find(filename + ".glslf");
std::string vertexSource = io::read_string(vertexFile);
std::string fragmentSource = io::read_string(fragmentFile);
@ -104,14 +104,14 @@ static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
assetload::postfunc assetload::atlas(
AssetsLoader* loader,
const ResPaths* paths,
const ResPaths& paths,
const std::string& directory,
const std::string& name,
const std::shared_ptr<AssetCfg>& config
) {
auto atlasConfig = std::dynamic_pointer_cast<AtlasCfg>(config);
if (atlasConfig && atlasConfig->type == AtlasType::SEPARATE) {
for (const auto& file : paths->listdir(directory)) {
for (const auto& file : paths.listdir(directory)) {
if (!imageio::is_read_supported(file.extension()))
continue;
loader->add(
@ -123,7 +123,7 @@ assetload::postfunc assetload::atlas(
return [](auto){};
}
AtlasBuilder builder;
for (const auto& file : paths->listdir(directory)) {
for (const auto& file : paths.listdir(directory)) {
if (!imageio::is_read_supported(file.extension())) continue;
if (!append_atlas(builder, file)) continue;
}
@ -140,7 +140,7 @@ assetload::postfunc assetload::atlas(
assetload::postfunc assetload::font(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>&
@ -148,7 +148,7 @@ assetload::postfunc assetload::font(
auto pages = std::make_shared<std::vector<std::unique_ptr<ImageData>>>();
for (size_t i = 0; i <= 1024; i++) {
std::string pagefile = filename + "_" + std::to_string(i) + ".png";
auto file = paths->find(pagefile);
auto file = paths.find(pagefile);
if (io::exists(file)) {
pages->push_back(imageio::read(file));
} else if (i == 0) {
@ -177,7 +177,7 @@ assetload::postfunc assetload::font(
assetload::postfunc assetload::layout(
AssetsLoader*,
const ResPaths* paths,
const ResPaths&,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& config
@ -189,6 +189,7 @@ assetload::postfunc assetload::layout(
auto prefix = name.substr(0, pos);
assets->store(
UiDocument::read(
*cfg->gui,
cfg->env,
name,
file,
@ -205,7 +206,7 @@ assetload::postfunc assetload::layout(
}
assetload::postfunc assetload::sound(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& config
@ -219,13 +220,13 @@ assetload::postfunc assetload::sound(
for (size_t i = 0; i < extensions.size(); i++) {
extension = extensions[i];
// looking for 'sound_name' as base sound
auto soundFile = paths->find(file + extension);
auto soundFile = paths.find(file + extension);
if (io::exists(soundFile)) {
baseSound = audio::load_sound(soundFile, keepPCM);
break;
}
// looking for 'sound_name_0' as base sound
auto variantFile = paths->find(file + "_0" + extension);
auto variantFile = paths.find(file + "_0" + extension);
if (io::exists(variantFile)) {
baseSound = audio::load_sound(variantFile, keepPCM);
break;
@ -238,7 +239,7 @@ assetload::postfunc assetload::sound(
// loading sound variants
for (uint i = 1;; i++) {
auto variantFile =
paths->find(file + "_" + std::to_string(i) + extension);
paths.find(file + "_" + std::to_string(i) + extension);
if (!io::exists(variantFile)) {
break;
}
@ -264,12 +265,12 @@ static void request_textures(AssetsLoader* loader, const model::Model& model) {
assetload::postfunc assetload::model(
AssetsLoader* loader,
const ResPaths* paths,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
auto path = paths->find(file + ".vec3");
auto path = paths.find(file + ".vec3");
if (io::exists(path)) {
auto bytes = io::read_bytes_buffer(path);
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.string(), bytes));
@ -289,7 +290,7 @@ assetload::postfunc assetload::model(
}
};
}
path = paths->find(file + ".obj");
path = paths.find(file + ".obj");
auto text = io::read_string(path);
try {
auto model = obj::parse(path.string(), text).release();
@ -383,7 +384,7 @@ inline bool contains(
static bool load_animation(
Assets* assets,
const ResPaths* paths,
const ResPaths& paths,
const std::string& atlasName,
const std::string& directory,
const std::string& name,
@ -391,20 +392,20 @@ static bool load_animation(
) {
std::string animsDir = directory + "/animation";
for (const auto& folder : paths->listdir(animsDir)) {
for (const auto& folder : paths.listdir(animsDir)) {
if (!io::is_directory(folder)) continue;
if (folder.name() != name) continue;
//FIXME: if (fs::is_empty(folder)) continue;
AtlasBuilder builder;
append_atlas(builder, paths->find(directory + "/" + name + ".png"));
append_atlas(builder, paths.find(directory + "/" + name + ".png"));
std::vector<std::pair<std::string, int>> frameList;
std::string animFile = folder.string() + "/animation.json";
if (io::exists(animFile)) {
read_anim_file(animFile, frameList);
}
for (const auto& file : paths->listdir(animsDir + "/" + name)) {
for (const auto& file : paths.listdir(animsDir + "/" + name)) {
if (!frameList.empty() &&
!contains(frameList, file.stem())) {
continue;

View File

@ -15,49 +15,49 @@ struct AssetCfg;
namespace assetload {
postfunc texture(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc shader(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc atlas(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& directory,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc font(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc layout(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc sound(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc model(
AssetsLoader*,
const ResPaths* paths,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings

View File

@ -17,12 +17,12 @@ static debug::Logger logger("audio");
using namespace audio;
namespace {
static speakerid_t nextId = 1;
static Backend* backend;
static std::unordered_map<speakerid_t, std::unique_ptr<Speaker>> speakers;
static std::unordered_map<speakerid_t, std::shared_ptr<Stream>> streams;
static std::vector<std::unique_ptr<Channel>> channels;
static util::ObjectsKeeper objects_keeper {};
speakerid_t nextId = 1;
Backend* backend;
std::unordered_map<speakerid_t, std::unique_ptr<Speaker>> speakers;
std::unordered_map<speakerid_t, std::shared_ptr<Stream>> streams;
std::vector<std::unique_ptr<Channel>> channels;
util::ObjectsKeeper objects_keeper {};
}
Channel::Channel(std::string name) : name(std::move(name)) {

View File

@ -7,16 +7,20 @@ template <typename CharT>
class BasicParser {
using StringT = std::basic_string<CharT>;
using StringViewT = std::basic_string_view<CharT>;
void skipWhitespaceHashComment(bool newline = true);
protected:
std::string_view filename;
StringViewT source;
uint pos = 0;
uint line = 1;
uint linestart = 0;
bool hashComment = false;
virtual void skipWhitespace();
void skipWhitespace(bool newline = true);
void skip(size_t n);
void skipLine();
void skipEmptyLines();
bool skipTo(const StringT& substring);
void expect(CharT expected);
void expect(const StringT& substring);

View File

@ -31,10 +31,17 @@ namespace {
}
template<typename CharT>
void BasicParser<CharT>::skipWhitespace() {
void BasicParser<CharT>::skipWhitespace(bool newline) {
if (hashComment) {
skipWhitespaceHashComment(newline);
return;
}
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
if (!newline) {
break;
}
line++;
linestart = ++pos;
continue;
@ -47,6 +54,36 @@ void BasicParser<CharT>::skipWhitespace() {
}
}
template<typename CharT>
void BasicParser<CharT>::skipWhitespaceHashComment(bool newline) {
while (hasNext()) {
char next = source[pos];
if (next == '\n') {
if (!newline) {
break;
}
line++;
linestart = ++pos;
continue;
}
if (is_whitespace(next)) {
pos++;
} else {
break;
}
}
if (hasNext() && source[pos] == '#') {
if (!newline) {
readUntilEOL();
return;
}
skipLine();
if (hasNext() && (is_whitespace(source[pos]) || source[pos] == '#')) {
skipWhitespaceHashComment(newline);
}
}
}
template<typename CharT>
void BasicParser<CharT>::skip(size_t n) {
n = std::min(n, source.length() - pos);
@ -73,6 +110,12 @@ void BasicParser<CharT>::skipLine() {
}
}
template<typename CharT>
void BasicParser<CharT>::skipEmptyLines() {
skipWhitespace();
pos = linestart;
}
template<typename CharT>
bool BasicParser<CharT>::skipTo(const std::basic_string<CharT>& substring) {
size_t idx = source.find(substring, pos);
@ -240,9 +283,12 @@ std::basic_string_view<CharT> BasicParser<CharT>::readUntilWhitespace() {
template <typename CharT>
std::basic_string_view<CharT> BasicParser<CharT>::readUntilEOL() {
int start = pos;
while (hasNext() && source[pos] != '\r' && source[pos] != '\n') {
while (hasNext() && source[pos] != '\n') {
pos++;
}
if (pos > start && source[pos - 1] == '\r') {
return source.substr(start, pos - start - 1);
}
return source.substr(start, pos - start);
}

View File

@ -13,13 +13,14 @@ using namespace json;
namespace {
class Parser : BasicParser<char> {
dv::value parseList();
dv::value parseObject();
dv::value parseValue();
public:
public:
Parser(std::string_view filename, std::string_view source);
dv::value parse();
private:
dv::value parseList();
dv::value parseObject();
dv::value parseValue();
};
}

View File

@ -16,16 +16,6 @@ using namespace toml;
class TomlReader : BasicParser<char> {
dv::value root;
void skipWhitespace() override {
BasicParser::skipWhitespace();
if (hasNext() && source[pos] == '#') {
skipLine();
if (hasNext() && is_whitespace(peek())) {
skipWhitespace();
}
}
}
// modified version of BaseParser.parseString
// todo: extract common part
std::string parseMultilineString() {
@ -214,6 +204,7 @@ class TomlReader : BasicParser<char> {
public:
TomlReader(std::string_view file, std::string_view source)
: BasicParser(file, source), root(dv::object()) {
hashComment = true;
}
dv::value read() {

459
src/coders/yaml.cpp Normal file
View File

@ -0,0 +1,459 @@
#include "yaml.hpp"
#include "BasicParser.hpp"
#include <iomanip>
using namespace yaml;
namespace {
enum Chomping {
CLIP, STRIP, KEEP
};
class Parser : BasicParser<char> {
public:
Parser(std::string_view filename, std::string_view source);
dv::value parseValue();
dv::value parseFullValue(int indent);
dv::value parseArray(int indent = 0);
dv::value parseObject(dv::value&& object, int indent = 0);
dv::value parseInlineArray();
dv::value parseInlineObject();
private:
int countIndent();
bool expectIndent(int indent);
std::string_view readYamlIdentifier();
std::string readMultilineString(int indent, bool eols, Chomping chomp);
};
}
inline bool is_yaml_identifier_char(int c) {
return c > 20 && c != ':' && c != ' ' && c != '\n' && c != '\r' &&
c != '\t' && c != '\f' && c != '\v';
}
static dv::value perform_literal(std::string_view literal) {
if (literal == "true" || literal == "True" ||
literal == "false" || literal == "False") {
return literal[0] == 't';
}
if (literal == "null" || literal == "Null") {
return nullptr;
}
return std::string(literal);
}
Parser::Parser(std::string_view filename, std::string_view source)
: BasicParser(filename, source) {
hashComment = true;
}
bool Parser::expectIndent(int required) {
int indent = 0;
while (hasNext() && source[pos] == ' ' && indent < required) {
indent++;
pos++;
}
return indent >= required;
}
std::string Parser::readMultilineString(int indent, bool eols, Chomping chomp) {
int next_indent = countIndent();
if (next_indent <= indent) {
throw error("indentation error");
}
std::stringstream ss;
ss << readUntilEOL();
if (hasNext()) {
skip(1);
}
int trailingEmpties = 0;
while (true) {
while (expectIndent(next_indent)) {
trailingEmpties = 0;
ss << (eols ? '\n' : ' ');
ss << readUntilEOL();
if (hasNext()) {
skip(1);
}
}
while (true) {
skipWhitespace(false);
if (!hasNext() || source[pos] != '\n') {
break;
}
skip(1);
trailingEmpties++;
}
if (!expectIndent(next_indent)) {
break;
}
pos = linestart;
}
if (chomp == KEEP) {
for (int i = 0; i < trailingEmpties - 1; i++) {
ss << (eols ? '\n' : ' ');
}
}
ss << '\n';
pos = linestart;
auto string = ss.str();
if (chomp == STRIP) {
util::trim(string);
}
return string;
}
std::string_view Parser::readYamlIdentifier() {
char c = peek();
if (!is_yaml_identifier_char(c)) {
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_yaml_identifier_char(source[pos])) {
pos++;
}
return source.substr(start, pos - start);
}
int Parser::countIndent() {
int indent = 0;
while (hasNext() && source[pos] == ' ') {
indent++;
pos++;
}
return indent;
}
dv::value Parser::parseValue() {
char c = peek();
if (is_digit(c)) {
return parseNumber(1);
} else if (c == '-' || c == '+') {
skip(1);
return parseNumber(c == '-' ? -1 : 1);
} else if (c == '"' || c == '\'') {
skip(1);
return parseString(c, true);
} else if (c == '[') {
return parseInlineArray();
} else if (c == '{') {
return parseInlineObject();
} else {
return perform_literal(readUntilEOL());
}
throw error("unexpected character");
}
dv::value Parser::parseInlineArray() {
expect('[');
auto list = dv::list();
while (peek() != ']') {
if (peek() == '#') {
skipLine();
continue;
}
list.add(parseValue());
char next = peek();
if (next == ',') {
pos++;
} else if (next == ']') {
break;
} else {
throw error("',' expected");
}
}
pos++;
return list;
}
dv::value Parser::parseInlineObject() {
expect('{');
dv::value object = dv::object();
while (peek() != '}') {
if (peek() == '#') {
skipLine();
continue;
}
auto name = readYamlIdentifier();
expect(':');
object[std::string(name)] = parseValue();
char next = peek();
if (next == ',') {
pos++;
} else if (next == '}') {
break;
} else {
throw error("',' expected");
}
}
pos++;
return object;
}
dv::value Parser::parseFullValue(int indent) {
dv::value value;
char c = source[pos];
if (c == '\n') {
skip(1);
skipEmptyLines();
int init_pos = pos;
int next_indent = countIndent();
if (next_indent < indent) {
throw error("indentation error");
}
if (source[pos] == '-') {
pos = init_pos;
return parseArray(next_indent);
} else {
pos = init_pos;
return parseObject(dv::object(), next_indent);
}
} else if (is_digit(c)) {
return parseNumber(1);
} else if (c == '-' || c == '+') {
skip(1);
return parseNumber(c == '-' ? -1 : 1);
} else if (c == '"' || c == '\'') {
skip(1);
return parseString(c, true);
} else if (c == '[') {
return parseInlineArray();
} else if (c == '{') {
return parseInlineObject();
} else if (c == '|' || c == '>') {
skip(1);
Chomping chomp = CLIP;
if (source[pos] == '-' || source[pos] == '+') {
chomp = source[pos] == '-' ? STRIP : KEEP;
skip(1);
}
skipWhitespace(false);
expectNewLine();
return readMultilineString(indent, c == '|', chomp);
} else {
return perform_literal(readUntilEOL());
}
}
dv::value Parser::parseArray(int indent) {
dv::value list = dv::list();
while (hasNext()) {
skipEmptyLines();
int next_indent = countIndent();
if (next_indent < indent) {
pos = linestart;
break;
}
expect('-');
skipWhitespace();
size_t nlpos = source.find('\n', pos);
size_t colonpos = source.find(':', pos);
if (nlpos == std::string::npos && colonpos == std::string::npos) {
list.add(perform_literal(readUntilEOL()));
break;
}
if (nlpos < colonpos) {
list.add(parseFullValue(next_indent));
skipLine();
} else {
auto name = readYamlIdentifier();
expect(':');
skipWhitespace(false);
dv::value object = dv::object();
object[std::string(name)] = parseFullValue(next_indent);
skipEmptyLines();
next_indent = countIndent();
if (next_indent > indent) {
pos = linestart;
object = parseObject(std::move(object), next_indent);
} else {
pos = linestart;
}
list.add(std::move(object));
}
}
return list;
}
dv::value Parser::parseObject(dv::value&& object, int indent) {
skipEmptyLines();
while (hasNext()) {
size_t prev_pos = pos;
int next_indent = countIndent();
if (source[pos] == '\n') {
skip(1);
continue;
}
if (next_indent < indent) {
pos = prev_pos;
break;
}
char c = peek();
if (!is_yaml_identifier_char(c)) {
if (!is_whitespace(c)) {
throw error("invalid character");
}
continue;
}
auto name = readYamlIdentifier();
expect(':');
skipWhitespace(false);
object[std::string(name)] = parseFullValue(indent);
skipEmptyLines();
}
return object;
}
dv::value yaml::parse(std::string_view filename, std::string_view source) {
return Parser(filename, source).parseObject(dv::object());
}
dv::value yaml::parse(std::string_view source) {
return parse("[string]", source);
}
static void add_indent(std::stringstream& ss, int indent) {
for (int i = 0; i < indent; i++) {
ss << " ";
}
}
static void insert_string(
std::stringstream& ss, const std::string& string, int indent
) {
bool has_spec_chars = false;
bool multiline = false;
for (char c : string) {
if (c < ' ' || c == '"' || c == '\'') {
has_spec_chars = true;
if (multiline) {
break;
}
}
if (c == '\n') {
multiline = true;
break;
}
}
if (multiline) {
ss << "|-\n";
size_t offset = 0;
size_t newoffset = 0;
do {
offset = newoffset;
if (offset == string.length() - 1 && string[offset] == '\n') {
break;
}
add_indent(ss, indent);
newoffset = string.find('\n', offset + 1);
if (newoffset == std::string::npos) {
ss << string.substr(offset);
break;
} else {
ss << string.substr(offset + 1, newoffset - offset - 1);
}
ss << '\n';
} while (true);
} else {
if (has_spec_chars || string.empty()) {
ss << util::escape(string, false);
} else {
ss << string;
}
}
}
static void to_string(
std::stringstream& ss,
const dv::value& value,
int indent,
bool eliminateIndent = false
) {
using dv::value_type;
switch (value.getType()) {
case value_type::string:
insert_string(ss, value.asString(), indent);
break;
case value_type::number:
ss << std::setprecision(15) << value.asNumber();
break;
case value_type::integer:
ss << value.asInteger();
break;
case value_type::boolean:
ss << (value.asBoolean() ? "true" : "false");
break;
case value_type::none:
ss << "null";
break;
case value_type::object: {
if (value.empty()) {
ss << "{}";
break;
}
bool first = true;
for (const auto& [key, elem] : value.asObject()) {
if (!first) {
ss << '\n';
}
if (!eliminateIndent) {
add_indent(ss, indent);
} else {
eliminateIndent = false;
}
ss << key << ": ";
if ((elem.isObject() || elem.isList()) && !elem.empty()) {
ss << "\n";
to_string(ss, elem, indent + 1);
} else {
to_string(ss, elem, indent + 1);
}
first = false;
}
break;
}
case value_type::list: {
if (value.empty()) {
ss << "[]";
break;
}
bool first = true;
for (const auto& elem : value) {
if (!first) {
ss << '\n';
}
if (!eliminateIndent) {
add_indent(ss, indent);
} else {
eliminateIndent = false;
}
ss << "- ";
to_string(ss, elem, indent + 1, true);
first = false;
}
break;
}
case value_type::bytes: {
const auto& bytes = value.asBytes();
auto b64 = util::base64_encode(bytes.data(), bytes.size());
b64 = util::join(util::split_by_n(b64, 64), '\n');
insert_string(ss, b64, indent);
break;
}
}
}
std::string yaml::stringify(const dv::value& value) {
std::stringstream ss;
to_string(ss, value, 0);
return ss.str();
}

12
src/coders/yaml.hpp Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include "data/dv.hpp"
namespace yaml {
dv::value parse(std::string_view filename, std::string_view source);
dv::value parse(std::string_view source);
std::string stringify(const dv::value& value);
}

View File

@ -6,7 +6,7 @@
#include <string>
inline constexpr int ENGINE_VERSION_MAJOR = 0;
inline constexpr int ENGINE_VERSION_MINOR = 27;
inline constexpr int ENGINE_VERSION_MINOR = 28;
#ifdef NDEBUG
inline constexpr bool ENGINE_DEBUG_BUILD = false;
@ -14,7 +14,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = false;
inline constexpr bool ENGINE_DEBUG_BUILD = true;
#endif // NDEBUG
inline const std::string ENGINE_VERSION_STRING = "0.27";
inline const std::string ENGINE_VERSION_STRING = "0.28";
/// @brief world regions format version
inline constexpr uint REGION_FORMAT_VERSION = 3;

View File

@ -0,0 +1,133 @@
#include "ContentControl.hpp"
#include "io/io.hpp"
#include "io/engine_paths.hpp"
#include "Content.hpp"
#include "ContentPack.hpp"
#include "ContentBuilder.hpp"
#include "ContentLoader.hpp"
#include "PacksManager.hpp"
#include "objects/rigging.hpp"
#include "logic/scripting/scripting.hpp"
#include "core_defs.hpp"
static void load_configs(Input& input, const io::path& root) {
auto configFolder = root / "config";
}
ContentControl::ContentControl(
EnginePaths& paths, Input& input, std::function<void()> postContent
)
: paths(paths),
input(input),
postContent(std::move(postContent)),
basePacks(io::read_list("res:config/builtins.list")),
manager(std::make_unique<PacksManager>()) {
manager->setSources({
"world:content",
"user:content",
"res:content",
});
}
ContentControl::~ContentControl() = default;
Content* ContentControl::get() {
return content.get();
}
const Content* ContentControl::get() const {
return content.get();
}
std::vector<std::string>& ContentControl::getBasePacks() {
return basePacks;
}
void ContentControl::resetContent() {
paths.setCurrentWorldFolder("");
scripting::cleanup();
std::vector<PathsRoot> resRoots;
{
auto pack = ContentPack::createCore(paths);
resRoots.push_back({"core", pack.folder});
load_configs(input, pack.folder);
}
manager->scan();
for (const auto& pack : manager->getAll(basePacks)) {
resRoots.push_back({pack.id, pack.folder});
}
paths.resPaths = ResPaths(resRoots);
content.reset();
contentPacks.clear();
contentPacks = manager->getAll(basePacks);
postContent();
}
void ContentControl::loadContent(const std::vector<std::string>& names) {
manager->scan();
contentPacks = manager->getAll(manager->assemble(names));
loadContent();
}
void ContentControl::loadContent() {
scripting::cleanup();
std::vector<std::string> names;
for (auto& pack : contentPacks) {
names.push_back(pack.id);
}
manager->scan();
names = manager->assemble(names);
contentPacks = manager->getAll(names);
std::vector<PathsRoot> entryPoints;
for (auto& pack : contentPacks) {
entryPoints.emplace_back(pack.id, pack.folder);
}
paths.setEntryPoints(std::move(entryPoints));
ContentBuilder contentBuilder;
corecontent::setup(input, contentBuilder);
auto corePack = ContentPack::createCore(paths);
auto allPacks = contentPacks;
allPacks.insert(allPacks.begin(), corePack);
// Setup filesystem entry points
std::vector<PathsRoot> resRoots;
for (auto& pack : allPacks) {
resRoots.push_back({pack.id, pack.folder});
}
paths.resPaths = ResPaths(resRoots);
// Load content
for (auto& pack : allPacks) {
ContentLoader(&pack, contentBuilder, paths.resPaths).load();
load_configs(input, pack.folder);
}
content = contentBuilder.build();
scripting::on_content_load(content.get());
ContentLoader::loadScripts(*content);
postContent();
}
std::vector<ContentPack>& ContentControl::getContentPacks() {
return contentPacks;
}
std::vector<ContentPack> ContentControl::getAllContentPacks() {
auto packs = contentPacks;
packs.insert(packs.begin(), ContentPack::createCore(paths));
return packs;
}
PacksManager& ContentControl::scan() {
manager->scan();
return *manager;
}

View File

@ -0,0 +1,51 @@
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <functional>
#include "ContentPack.hpp"
class Content;
class PacksManager;
class EnginePaths;
class Input;
namespace io {
class path;
}
class ContentControl {
public:
ContentControl(
EnginePaths& paths, Input& input, std::function<void()> postContent
);
~ContentControl();
Content* get();
const Content* get() const;
std::vector<std::string>& getBasePacks();
/// @brief Reset content to base packs list
void resetContent();
void loadContent(const std::vector<std::string>& names);
void loadContent();
std::vector<ContentPack>& getContentPacks();
std::vector<ContentPack> getAllContentPacks();
PacksManager& scan();
private:
EnginePaths& paths;
Input& input;
std::unique_ptr<Content> content;
std::function<void()> postContent;
std::vector<std::string> basePacks;
std::unique_ptr<PacksManager> manager;
std::vector<ContentPack> contentPacks;
};

View File

@ -15,12 +15,13 @@ namespace fs = std::filesystem;
ContentPack ContentPack::createCore(const EnginePaths& paths) {
return ContentPack {
"core", "Core", ENGINE_VERSION_STRING, "", "", "res:", "res:", {}
"core", "Core", ENGINE_VERSION_STRING, "", "", "res:", {}
};
}
const std::vector<std::string> ContentPack::RESERVED_NAMES = {
"res", "abs", "local", "core", "user", "world", "none", "null"};
"res", "abs", "local", "core", "user", "world", "none", "null"
};
contentpack_error::contentpack_error(
std::string packId, io::path folder, const std::string& message
@ -70,7 +71,7 @@ static void checkContentPackId(const std::string& id, const io::path& folder) {
}
}
ContentPack ContentPack::read(const std::string& path, const io::path& folder) {
ContentPack ContentPack::read(const io::path& folder) {
auto root = io::read_json(folder / PACKAGE_FILENAME);
ContentPack pack;
root.at("id").get(pack.id);
@ -90,7 +91,6 @@ ContentPack ContentPack::read(const std::string& path, const io::path& folder) {
root.at("description").get(pack.description);
root.at("source").get(pack.source);
pack.folder = folder;
pack.path = path;
if (auto found = root.at("dependencies")) {
const auto& dependencies = *found;
@ -124,7 +124,7 @@ ContentPack ContentPack::read(const std::string& path, const io::path& folder) {
}
void ContentPack::scanFolder(
const std::string& path, const io::path& folder, std::vector<ContentPack>& packs
const io::path& folder, std::vector<ContentPack>& packs
) {
if (!io::is_directory(folder)) {
return;
@ -133,9 +133,7 @@ void ContentPack::scanFolder(
if (!io::is_directory(packFolder)) continue;
if (!is_pack(packFolder)) continue;
try {
packs.push_back(
read(path + "/" + packFolder.name(), packFolder)
);
packs.push_back(read(packFolder));
} catch (const contentpack_error& err) {
std::cerr << "package.json error at " << err.getFolder().string();
std::cerr << ": " << err.what() << std::endl;

View File

@ -43,7 +43,6 @@ struct ContentPack {
std::string creator = "";
std::string description = "no description";
io::path folder;
std::string path;
std::vector<DependencyPack> dependencies;
std::string source = "";
@ -58,14 +57,10 @@ struct ContentPack {
static const std::vector<std::string> RESERVED_NAMES;
static bool is_pack(const io::path& folder);
static ContentPack read(
const std::string& path, const io::path& folder
);
static ContentPack read(const io::path& folder);
static void scanFolder(
const std::string& path,
const io::path& folder,
std::vector<ContentPack>& packs
const io::path& folder, std::vector<ContentPack>& packs
);
static std::vector<std::string> worldPacksList(

View File

@ -7,7 +7,7 @@
PacksManager::PacksManager() = default;
void PacksManager::setSources(std::vector<std::pair<std::string, io::path>> sources) {
void PacksManager::setSources(std::vector<io::path> sources) {
this->sources = std::move(sources);
}
@ -15,8 +15,8 @@ void PacksManager::scan() {
packs.clear();
std::vector<ContentPack> packsList;
for (auto& [path, folder] : sources) {
ContentPack::scanFolder(path, folder, packsList);
for (auto& folder : sources) {
ContentPack::scanFolder(folder, packsList);
for (auto& pack : packsList) {
packs.try_emplace(pack.id, pack);
}

View File

@ -8,12 +8,12 @@
class PacksManager {
std::unordered_map<std::string, ContentPack> packs;
std::vector<std::pair<std::string, io::path>> sources;
std::vector<io::path> sources;
public:
PacksManager();
/// @brief Set content packs sources (search folders)
void setSources(std::vector<std::pair<std::string, io::path>> sources);
void setSources(std::vector<io::path> sources);
/// @brief Scan sources and collect all found packs excluding duplication.
/// Scanning order depends on sources order

View File

@ -5,13 +5,12 @@
#include "content/ContentBuilder.hpp"
#include "io/io.hpp"
#include "io/engine_paths.hpp"
#include "window/Window.hpp"
#include "window/Events.hpp"
#include "window/input.hpp"
#include "voxels/Block.hpp"
#include "coders/toml.hpp"
// All in-game definitions (blocks, items, etc..)
void corecontent::setup(ContentBuilder& builder) {
void corecontent::setup(Input& input, ContentBuilder& builder) {
{
Block& block = builder.blocks.create(CORE_AIR);
block.replaceable = true;
@ -30,8 +29,8 @@ void corecontent::setup(ContentBuilder& builder) {
auto bindsFile = "res:bindings.toml";
if (io::is_regular_file(bindsFile)) {
Events::loadBindings(
bindsFile, io::read_string(bindsFile), BindType::BIND
input.getBindings().read(
toml::parse(bindsFile, io::read_string(bindsFile)), BindType::BIND
);
}

View File

@ -28,8 +28,9 @@ inline const std::string BIND_PLAYER_FAST_INTERACTOIN =
"player.fast_interaction";
inline const std::string BIND_HUD_INVENTORY = "hud.inventory";
class Input;
class ContentBuilder;
namespace corecontent {
void setup(ContentBuilder& builder);
void setup(Input& input, ContentBuilder& builder);
}

View File

@ -6,6 +6,7 @@
#include <string.h>
#include <algorithm>
#include "data/dv.hpp"
#include "util/data_io.hpp"
#include "util/stringutil.hpp"

6
src/data/dv_fwd.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
namespace dv {
class value;
struct optionalvalue;
}

View File

@ -7,6 +7,7 @@
#include "delegates.hpp"
#include "typedefs.hpp"
#include "util/observer_handler.hpp"
enum class setting_format { simple, percent };
@ -41,15 +42,14 @@ public:
: Setting(format), initial(value), value(value) {
}
observer_handler observe(consumer<T> callback, bool callOnStart = false) {
ObserverHandler observe(consumer<T> callback, bool callOnStart = false) {
const int id = nextid++;
observers.emplace(id, callback);
if (callOnStart) {
callback(value);
}
return std::shared_ptr<int>(new int(id), [this](int* id) { //-V508
observers.erase(*id);
delete id;
return ObserverHandler([this, id]() {
observers.erase(id);
});
}

View File

@ -12,9 +12,7 @@
#include "coders/json.hpp"
#include "coders/toml.hpp"
#include "coders/commons.hpp"
#include "content/Content.hpp"
#include "content/ContentBuilder.hpp"
#include "content/ContentLoader.hpp"
#include "content/ContentControl.hpp"
#include "core_defs.hpp"
#include "io/io.hpp"
#include "frontend/locale.hpp"
@ -31,10 +29,8 @@
#include "logic/scripting/scripting.hpp"
#include "logic/scripting/scripting_hud.hpp"
#include "network/Network.hpp"
#include "util/listutil.hpp"
#include "util/platform.hpp"
#include "window/Camera.hpp"
#include "window/Events.hpp"
#include "window/input.hpp"
#include "window/Window.hpp"
#include "world/Level.hpp"
@ -50,8 +46,6 @@
static debug::Logger logger("engine");
namespace fs = std::filesystem;
static std::unique_ptr<ImageData> load_icon() {
try {
auto file = "res:textures/misc/icon.png";
@ -65,20 +59,21 @@ static std::unique_ptr<ImageData> load_icon() {
}
Engine::Engine() = default;
Engine::~Engine() = default;
static std::unique_ptr<Engine> engine;
static std::unique_ptr<Engine> instance = nullptr;
Engine& Engine::getInstance() {
if (!engine) {
engine = std::make_unique<Engine>();
if (!instance) {
instance = std::make_unique<Engine>();
}
return *engine;
return *instance;
}
void Engine::initialize(CoreParameters coreParameters) {
params = std::move(coreParameters);
settingsHandler = std::make_unique<SettingsHandler>(settings);
interpreter = std::make_unique<cmd::CommandsInterpreter>();
cmd = std::make_unique<cmd::CommandsInterpreter>();
network = network::Network::create(settings.network);
logger.info() << "engine version: " << ENGINE_VERSION_STRING;
@ -93,46 +88,80 @@ void Engine::initialize(CoreParameters coreParameters) {
}
loadSettings();
auto resdir = paths.getResourcesFolder();
controller = std::make_unique<EngineController>(*this);
if (!params.headless) {
if (Window::initialize(&settings.display)){
std::string title = "VoxelCore v" +
std::to_string(ENGINE_VERSION_MAJOR) + "." +
std::to_string(ENGINE_VERSION_MINOR);
if (ENGINE_DEBUG_BUILD) {
title += " [debug]";
}
auto [window, input] = Window::initialize(&settings.display, title);
if (!window || !input){
throw initialize_error("could not initialize window");
}
time.set(Window::time());
window->setFramerate(settings.display.framerate.get());
time.set(window->time());
if (auto icon = load_icon()) {
icon->flipY();
Window::setIcon(icon.get());
window->setIcon(icon.get());
}
this->window = std::move(window);
this->input = std::move(input);
loadControls();
gui = std::make_unique<gui::GUI>();
gui = std::make_unique<gui::GUI>(*this);
if (ENGINE_DEBUG_BUILD) {
menus::create_version_label(*this);
menus::create_version_label(*gui);
}
keepAlive(settings.display.fullscreen.observe(
[this](bool value) {
if (value != this->window->isFullscreen()) {
this->window->toggleFullscreen();
}
},
true
));
}
audio::initialize(!params.headless, settings.audio);
bool langNotSet = settings.ui.language.get() == "auto";
if (langNotSet) {
settings.ui.language.set(langs::locale_by_envlocale(
platform::detect_locale(),
"res:"
));
settings.ui.language.set(
langs::locale_by_envlocale(platform::detect_locale())
);
}
content = std::make_unique<ContentControl>(paths, *input, [this]() {
langs::setup(langs::get_current(), paths.resPaths.collectRoots());
if (!isHeadless()) {
for (auto& pack : content->getAllContentPacks()) {
auto configFolder = pack.folder / "config";
auto bindsFile = configFolder / "bindings.toml";
if (io::is_regular_file(bindsFile)) {
input->getBindings().read(
toml::parse(
bindsFile.string(), io::read_string(bindsFile)
),
BindType::BIND
);
}
}
loadAssets();
}
});
scripting::initialize(this);
if (!isHeadless()) {
gui->setPageLoader(scripting::create_page_loader());
}
keepAlive(settings.ui.language.observe([this](auto lang) {
setLanguage(lang);
langs::setup(lang, paths.resPaths.collectRoots());
}, true));
basePacks = io::read_list("res:config/builtins.list");
}
void Engine::loadSettings() {
io::path settings_file = paths.getSettingsFile();
io::path settings_file = EnginePaths::SETTINGS_FILE;
if (io::is_regular_file(settings_file)) {
logger.info() << "loading settings";
std::string text = io::read_string(settings_file);
@ -146,33 +175,30 @@ void Engine::loadSettings() {
}
void Engine::loadControls() {
io::path controls_file = paths.getControlsFile();
io::path controls_file = EnginePaths::CONTROLS_FILE;
if (io::is_regular_file(controls_file)) {
logger.info() << "loading controls";
std::string text = io::read_string(controls_file);
Events::loadBindings(controls_file.string(), text, BindType::BIND);
input->getBindings().read(
toml::parse(controls_file.string(), text), BindType::BIND
);
}
}
void Engine::onAssetsLoaded() {
assets->setup();
gui->onAssetsLoad(assets.get());
}
void Engine::updateHotkeys() {
if (Events::jpressed(keycode::F2)) {
if (input->jpressed(Keycode::F2)) {
saveScreenshot();
}
if (Events::jpressed(keycode::F8)) {
if (input->jpressed(Keycode::F8)) {
gui->toggleDebug();
}
if (Events::jpressed(keycode::F11)) {
if (input->jpressed(Keycode::F11)) {
settings.display.fullscreen.toggle();
}
}
void Engine::saveScreenshot() {
auto image = Window::takeScreenshot();
auto image = window->takeScreenshot();
image->flipY();
io::path filename = paths.getNewScreenshotFile("png");
imageio::write(filename.string(), image.get());
@ -197,39 +223,38 @@ void Engine::updateFrontend() {
double delta = time.getDelta();
updateHotkeys();
audio::update(delta);
gui->act(delta, Viewport(Window::width, Window::height));
gui->act(delta, window->getSize());
screen->update(delta);
gui->postAct();
}
void Engine::nextFrame() {
Window::setFramerate(
Window::isIconified() && settings.display.limitFpsIconified.get()
window->setFramerate(
window->isIconified() && settings.display.limitFpsIconified.get()
? 20
: settings.display.framerate.get()
);
Window::swapBuffers();
Events::pollEvents();
window->swapBuffers();
input->pollEvents();
}
void Engine::renderFrame() {
screen->draw(time.getDelta());
Viewport viewport(Window::width, Window::height);
DrawContext ctx(nullptr, viewport, nullptr);
DrawContext ctx(nullptr, *window, nullptr);
gui->draw(ctx, *assets);
}
void Engine::saveSettings() {
logger.info() << "saving settings";
io::write_string(paths.getSettingsFile(), toml::stringify(*settingsHandler));
io::write_string(EnginePaths::SETTINGS_FILE, toml::stringify(*settingsHandler));
if (!params.headless) {
logger.info() << "saving bindings";
io::write_string(paths.getControlsFile(), Events::writeBindings());
io::write_string(EnginePaths::CONTROLS_FILE, input->getBindings().write());
}
}
Engine::~Engine() {
void Engine::close() {
saveSettings();
logger.info() << "shutting down";
if (screen) {
@ -238,7 +263,7 @@ Engine::~Engine() {
}
content.reset();
assets.reset();
interpreter.reset();
cmd.reset();
if (gui) {
gui.reset();
logger.info() << "gui finished";
@ -249,45 +274,34 @@ Engine::~Engine() {
scripting::close();
logger.info() << "scripting finished";
if (!params.headless) {
Window::terminate();
window.reset();
logger.info() << "window closed";
}
logger.info() << "engine finished";
}
void Engine::terminate() {
engine.reset();
instance->close();
instance.reset();
}
EngineController* Engine::getController() {
return controller.get();
}
cmd::CommandsInterpreter* Engine::getCommandsInterpreter() {
return interpreter.get();
}
PacksManager Engine::createPacksManager(const io::path& worldFolder) {
PacksManager manager;
manager.setSources({
{"world:content", worldFolder.empty() ? worldFolder : worldFolder / "content"},
{"user:content", "user:content"},
{"res:content", "res:content"}
});
return manager;
}
void Engine::setLevelConsumer(OnWorldOpen levelConsumer) {
this->levelConsumer = std::move(levelConsumer);
}
void Engine::loadAssets() {
logger.info() << "loading assets";
Shader::preprocessor->setPaths(resPaths.get());
Shader::preprocessor->setPaths(&paths.resPaths);
auto content = this->content->get();
auto new_assets = std::make_unique<Assets>();
AssetsLoader loader(new_assets.get(), resPaths.get());
AssetsLoader::addDefaults(loader, content.get());
AssetsLoader loader(*this, *new_assets, paths.resPaths);
AssetsLoader::addDefaults(loader, content);
// no need
// correct log messages order is more useful
@ -301,139 +315,11 @@ void Engine::loadAssets() {
}
}
assets = std::move(new_assets);
if (content == nullptr) {
return;
if (content) {
ModelsGenerator::prepare(*content, *assets);
}
for (auto& [name, def] : content->blocks.getDefs()) {
if (def->model == BlockModel::custom && def->modelName.empty()) {
assets->store(
std::make_unique<model::Model>(
ModelsGenerator::loadCustomBlockModel(
def->customModelRaw, *assets, !def->shadeless
)
),
name + ".model"
);
def->modelName = def->name + ".model";
}
}
for (auto& [name, def] : content->items.getDefs()) {
assets->store(
std::make_unique<model::Model>(
ModelsGenerator::generate(*def, *content, *assets)
),
name + ".model"
);
}
}
static void load_configs(const io::path& root) {
auto configFolder = root / "config";
auto bindsFile = configFolder / "bindings.toml";
if (io::is_regular_file(bindsFile)) {
Events::loadBindings(
bindsFile.string(), io::read_string(bindsFile), BindType::BIND
);
}
}
void Engine::loadContent() {
scripting::cleanup();
std::vector<std::string> names;
for (auto& pack : contentPacks) {
names.push_back(pack.id);
}
ContentBuilder contentBuilder;
corecontent::setup(contentBuilder);
paths.setContentPacks(&contentPacks);
PacksManager manager = createPacksManager(paths.getCurrentWorldFolder());
manager.scan();
names = manager.assemble(names);
contentPacks = manager.getAll(names);
auto corePack = ContentPack::createCore(paths);
// Setup filesystem entry points
std::vector<PathsRoot> resRoots {
{"core", corePack.folder}
};
for (auto& pack : contentPacks) {
resRoots.push_back({pack.id, pack.folder});
}
resPaths = std::make_unique<ResPaths>("res:", resRoots);
// Load content
{
ContentLoader(&corePack, contentBuilder, *resPaths).load();
load_configs(corePack.folder);
}
for (auto& pack : contentPacks) {
ContentLoader(&pack, contentBuilder, *resPaths).load();
load_configs(pack.folder);
}
content = contentBuilder.build();
interpreter->reset();
scripting::on_content_load(content.get());
ContentLoader::loadScripts(*content);
langs::setup("res:", langs::current->getId(), contentPacks);
if (!isHeadless()) {
loadAssets();
onAssetsLoaded();
}
}
void Engine::resetContent() {
scripting::cleanup();
std::vector<PathsRoot> resRoots;
{
auto pack = ContentPack::createCore(paths);
resRoots.push_back({"core", pack.folder});
load_configs(pack.folder);
}
auto manager = createPacksManager(io::path());
manager.scan();
for (const auto& pack : manager.getAll(basePacks)) {
resRoots.push_back({pack.id, pack.folder});
}
resPaths = std::make_unique<ResPaths>("res:", resRoots);
contentPacks.clear();
content.reset();
langs::setup("res:", langs::current->getId(), contentPacks);
if (!isHeadless()) {
loadAssets();
onAssetsLoaded();
}
contentPacks = manager.getAll(basePacks);
}
void Engine::loadWorldContent(const io::path& folder) {
contentPacks.clear();
auto packNames = ContentPack::worldPacksList(folder);
PacksManager manager;
manager.setSources(
{{"world:content", folder.empty() ? folder : folder / "content"},
{"user:content", "user:content"},
{"res:content", "res:content"}}
);
manager.scan();
contentPacks = manager.getAll(manager.assemble(packNames));
paths.setCurrentWorldFolder(folder);
loadContent();
}
void Engine::loadAllPacks() {
PacksManager manager = createPacksManager(paths.getCurrentWorldFolder());
manager.scan();
auto allnames = manager.getAllNames();
contentPacks = manager.getAll(manager.assemble(allnames));
assets->setup();
gui->onAssetsLoad(assets.get());
}
void Engine::setScreen(std::shared_ptr<Screen> screen) {
@ -443,10 +329,6 @@ void Engine::setScreen(std::shared_ptr<Screen> screen) {
this->screen = std::move(screen);
}
void Engine::setLanguage(std::string locale) {
langs::setup("res:", std::move(locale), contentPacks);
}
void Engine::onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer) {
logger.info() << "world open";
levelConsumer(std::move(level), localPlayer);
@ -460,7 +342,7 @@ void Engine::onWorldClosed() {
void Engine::quit() {
quitSignal = true;
if (!isHeadless()) {
Window::setShouldClose(true);
window->setShouldClose(true);
}
}
@ -468,10 +350,6 @@ bool Engine::isQuitSignal() const {
return quitSignal;
}
gui::GUI* Engine::getGUI() {
return gui.get();
}
EngineSettings& Engine::getSettings() {
return settings;
}
@ -480,34 +358,12 @@ Assets* Engine::getAssets() {
return assets.get();
}
const Content* Engine::getContent() const {
return content.get();
}
Content* Engine::getWriteableContent() {
return content.get();
}
std::vector<ContentPack> Engine::getAllContentPacks() {
auto packs = getContentPacks();
packs.insert(packs.begin(), ContentPack::createCore(paths));
return packs;
}
std::vector<ContentPack>& Engine::getContentPacks() {
return contentPacks;
}
std::vector<std::string>& Engine::getBasePacks() {
return basePacks;
}
EnginePaths& Engine::getPaths() {
return paths;
}
ResPaths* Engine::getResPaths() {
return resPaths.get();
ResPaths& Engine::getResPaths() {
return paths.resPaths;
}
std::shared_ptr<Screen> Engine::getScreen() {
@ -518,10 +374,6 @@ SettingsHandler& Engine::getSettingsHandler() {
return *settingsHandler;
}
network::Network& Engine::getNetwork() {
return *network;
}
Time& Engine::getTime() {
return time;
}
@ -533,3 +385,7 @@ const CoreParameters& Engine::getCoreParameters() const {
bool Engine::isHeadless() const {
return params.headless;
}
ContentControl& Engine::getContentControl() {
return *content;
}

View File

@ -4,10 +4,6 @@
#include "typedefs.hpp"
#include "settings.hpp"
#include "assets/Assets.hpp"
#include "content/content_fwd.hpp"
#include "content/ContentPack.hpp"
#include "content/PacksManager.hpp"
#include "io/engine_paths.hpp"
#include "io/settings_io.hpp"
#include "util/ObjectsKeeper.hpp"
@ -15,17 +11,15 @@
#include "Time.hpp"
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
class Window;
class Assets;
class Level;
class Screen;
class EnginePaths;
class ResPaths;
class ContentControl;
class EngineController;
class SettingsHandler;
struct EngineSettings;
class Input;
namespace gui {
class GUI;
@ -62,13 +56,12 @@ class Engine : public util::ObjectsKeeper {
std::unique_ptr<SettingsHandler> settingsHandler;
std::unique_ptr<Assets> assets;
std::shared_ptr<Screen> screen;
std::vector<ContentPack> contentPacks;
std::unique_ptr<Content> content;
std::unique_ptr<ResPaths> resPaths;
std::unique_ptr<ContentControl> content;
std::unique_ptr<EngineController> controller;
std::unique_ptr<cmd::CommandsInterpreter> interpreter;
std::unique_ptr<cmd::CommandsInterpreter> cmd;
std::unique_ptr<network::Network> network;
std::vector<std::string> basePacks;
std::unique_ptr<Window> window;
std::unique_ptr<Input> input;
std::unique_ptr<gui::GUI> gui;
PostRunnables postRunnables;
Time time;
@ -87,6 +80,7 @@ public:
static Engine& getInstance();
void initialize(CoreParameters coreParameters);
void close();
static void terminate();
@ -98,9 +92,6 @@ public:
void updateFrontend();
void renderFrame();
void nextFrame();
/// @brief Called after assets loading when all engine systems are initialized
void onAssetsLoaded();
/// @brief Set screen (scene).
/// nullptr may be used to delete previous screen before creating new one,
@ -108,29 +99,8 @@ public:
/// @param screen nullable screen
void setScreen(std::shared_ptr<Screen> screen);
/// @brief Change locale to specified
/// @param locale isolanguage_ISOCOUNTRY (example: en_US)
void setLanguage(std::string locale);
/// @brief Load all selected content-packs and reload assets
void loadContent();
/// @brief Reset content to base packs list
void resetContent();
/// @brief Collect world content-packs and load content
/// @see loadContent
/// @param folder world folder
void loadWorldContent(const io::path& folder);
/// @brief Collect all available content-packs from res/content
void loadAllPacks();
/// @brief Get active assets storage instance
Assets* getAssets();
/// @brief Get main UI controller
gui::GUI* getGUI();
/// @brief Get writeable engine settings structure instance
EngineSettings& getSettings();
@ -139,7 +109,7 @@ public:
EnginePaths& getPaths();
/// @brief Get engine resource paths controller
ResPaths* getResPaths();
ResPaths& getResPaths();
void onWorldOpen(std::unique_ptr<Level> level, int64_t localPlayer);
void onWorldClosed();
@ -148,18 +118,6 @@ public:
bool isQuitSignal() const;
/// @brief Get current Content instance
const Content* getContent() const;
Content* getWriteableContent();
/// @brief Get selected content packs
std::vector<ContentPack>& getContentPacks();
std::vector<ContentPack> getAllContentPacks();
std::vector<std::string>& getBasePacks();
/// @brief Get current screen
std::shared_ptr<Screen> getScreen();
@ -171,19 +129,36 @@ public:
void saveScreenshot();
EngineController* getController();
cmd::CommandsInterpreter* getCommandsInterpreter();
PacksManager createPacksManager(const io::path& worldFolder);
void setLevelConsumer(OnWorldOpen levelConsumer);
SettingsHandler& getSettingsHandler();
network::Network& getNetwork();
Time& getTime();
const CoreParameters& getCoreParameters() const;
bool isHeadless() const;
ContentControl& getContentControl();
gui::GUI& getGUI() {
return *gui;
}
Input& getInput() {
return *input;
}
Window& getWindow() {
return *window;
}
network::Network& getNetwork() {
return *network;
}
cmd::CommandsInterpreter& getCmd() {
return *cmd;
}
};

View File

@ -14,6 +14,7 @@ Mainloop::Mainloop(Engine& engine) : engine(engine) {
void Mainloop::run() {
auto& time = engine.getTime();
auto& window = engine.getWindow();
engine.setLevelConsumer([this](auto level, int64_t localPlayer) {
if (level == nullptr) {
@ -32,10 +33,10 @@ void Mainloop::run() {
engine.setScreen(std::make_shared<MenuScreen>(engine));
logger.info() << "main loop started";
while (!Window::isShouldClose()){
time.update(Window::time());
while (!window.isShouldClose()){
time.update(window.time());
engine.updateFrontend();
if (!Window::isIconified()) {
if (!window.isIconified()) {
engine.renderFrame();
}
engine.postUpdate();

View File

@ -69,10 +69,6 @@ void ContentGfxCache::refresh() {
ContentGfxCache::~ContentGfxCache() = default;
const Content* ContentGfxCache::getContent() const {
return &content;
}
const model::Model& ContentGfxCache::getModel(blockid_t id) const {
const auto& found = models.find(id);
if (found == models.end()) {

View File

@ -6,6 +6,7 @@
#include <stdexcept>
#include <unordered_map>
#include "maths/UVRegion.hpp"
#include "graphics/commons/Model.hpp"
class Content;
@ -15,10 +16,6 @@ class Block;
struct UVRegion;
struct GraphicsSettings;
namespace model {
struct Model;
}
class ContentGfxCache {
const Content& content;
const Assets& assets;
@ -41,8 +38,6 @@ public:
const model::Model& getModel(blockid_t id) const;
const Content* getContent() const;
void refresh(const Block& block, const Atlas& atlas);
void refresh();

View File

@ -12,27 +12,33 @@
#include "objects/Player.hpp"
#include "voxels/Block.hpp"
#include "world/Level.hpp"
#include "engine/Engine.hpp"
LevelFrontend::LevelFrontend(
Engine& engine,
Player* currentPlayer,
LevelController* controller,
Assets& assets,
const EngineSettings& settings
)
: level(*controller->getLevel()),
controller(controller),
assets(assets),
assets(*engine.getAssets()),
contentCache(std::make_unique<ContentGfxCache>(
level.content, assets, settings.graphics
)) {
assets.store(
BlocksPreview::build(
*contentCache, assets, *level.content.getIndices()
engine.getWindow(),
*contentCache,
*engine.getAssets(),
*level.content.getIndices()
),
"block-previews"
);
auto& rassets = assets;
controller->getBlocksController()->listenBlockInteraction(
[currentPlayer, controller, &assets](auto player, const auto& pos, const auto& def, BlockInteraction type) {
[currentPlayer, controller, &rassets](auto player, const auto& pos, const auto& def, BlockInteraction type) {
const auto& level = *controller->getLevel();
auto material = level.content.findBlockMaterial(def.material);
if (material == nullptr) {
@ -40,7 +46,7 @@ LevelFrontend::LevelFrontend(
}
if (type == BlockInteraction::step) {
auto sound = assets.get<audio::Sound>(material->stepsSound);
auto sound = rassets.get<audio::Sound>(material->stepsSound);
glm::vec3 pos {};
auto soundsCamera = currentPlayer->currentCamera.get();
if (soundsCamera == currentPlayer->spCamera.get() ||
@ -66,10 +72,10 @@ LevelFrontend::LevelFrontend(
audio::Sound* sound = nullptr;
switch (type) {
case BlockInteraction::placing:
sound = assets.get<audio::Sound>(material->placeSound);
sound = rassets.get<audio::Sound>(material->placeSound);
break;
case BlockInteraction::destruction:
sound = assets.get<audio::Sound>(material->breakSound);
sound = rassets.get<audio::Sound>(material->breakSound);
break;
default:
break;
@ -95,14 +101,6 @@ Level& LevelFrontend::getLevel() {
return level;
}
const Level& LevelFrontend::getLevel() const {
return level;
}
const Assets& LevelFrontend::getAssets() const {
return assets;
}
ContentGfxCache& LevelFrontend::getContentGfxCache() {
return *contentCache;
}

View File

@ -5,6 +5,7 @@
class Level;
class Assets;
class Player;
class Engine;
class ContentGfxCache;
class LevelController;
struct EngineSettings;
@ -12,20 +13,18 @@ struct EngineSettings;
class LevelFrontend {
Level& level;
LevelController* controller;
const Assets& assets;
Assets& assets;
std::unique_ptr<ContentGfxCache> contentCache;
public:
LevelFrontend(
Engine& engine,
Player* currentPlayer,
LevelController* controller,
Assets& assets,
const EngineSettings& settings
);
~LevelFrontend();
Level& getLevel();
const Level& getLevel() const;
const Assets& getAssets() const;
const ContentGfxCache& getContentGfxCache() const;
ContentGfxCache& getContentGfxCache();
LevelController* getController() const;

View File

@ -21,11 +21,11 @@ void UiDocument::rebuildIndices() {
gui::UINode::getIndices(root, map);
}
const uinodes_map& UiDocument::getMap() const {
const UINodesMap& UiDocument::getMap() const {
return map;
}
uinodes_map& UiDocument::getMapWriteable() {
UINodesMap& UiDocument::getMapWriteable() {
return map;
}
@ -54,6 +54,7 @@ scriptenv UiDocument::getEnvironment() const {
}
std::unique_ptr<UiDocument> UiDocument::read(
gui::GUI& gui,
const scriptenv& penv,
const std::string& name,
const io::path& file,
@ -66,7 +67,7 @@ std::unique_ptr<UiDocument> UiDocument::read(
? scripting::create_doc_environment(scripting::get_root_environment(), name)
: scripting::create_doc_environment(penv, name);
gui::UiXmlReader reader(env);
gui::UiXmlReader reader(gui, env);
auto view = reader.readXML(file.string(), *xmldoc->getRoot());
view->setId("root");
uidocscript script {};
@ -80,8 +81,7 @@ std::unique_ptr<UiDocument> UiDocument::read(
}
std::shared_ptr<gui::UINode> UiDocument::readElement(
const io::path& file, const std::string& fileName
gui::GUI& gui, const io::path& file, const std::string& fileName
) {
auto document = read(nullptr, file.name(), file, fileName);
return document->getRoot();
return read(gui, nullptr, file.name(), file, fileName)->getRoot();
}

View File

@ -9,6 +9,7 @@
#include "io/fwd.hpp"
namespace gui {
class GUI;
class UINode;
}
@ -18,12 +19,12 @@ struct uidocscript {
bool onclose : 1;
};
using uinodes_map = std::unordered_map<std::string, std::shared_ptr<gui::UINode>>;
using UINodesMap = std::unordered_map<std::string, std::shared_ptr<gui::UINode>>;
class UiDocument {
std::string id;
uidocscript script;
uinodes_map map;
UINodesMap map;
std::shared_ptr<gui::UINode> root;
scriptenv env;
public:
@ -37,20 +38,21 @@ public:
void rebuildIndices();
const std::string& getId() const;
const uinodes_map& getMap() const;
uinodes_map& getMapWriteable();
const UINodesMap& getMap() const;
UINodesMap& getMapWriteable();
std::shared_ptr<gui::UINode> getRoot() const;
std::shared_ptr<gui::UINode> get(const std::string& id) const;
const uidocscript& getScript() const;
scriptenv getEnvironment() const;
static std::unique_ptr<UiDocument> read(
gui::GUI&,
const scriptenv& parent_env,
const std::string& name,
const io::path& file,
const std::string& fileName
);
static std::shared_ptr<gui::UINode> readElement(
const io::path& file, const std::string& fileName
gui::GUI&, const io::path& file, const std::string& fileName
);
};

View File

@ -35,8 +35,8 @@
using namespace gui;
static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
auto label = std::make_shared<Label>(L"-");
static std::shared_ptr<Label> create_label(GUI& gui, wstringsupplier supplier) {
auto label = std::make_shared<Label>(gui, L"-");
label->textSupplier(std::move(supplier));
return label;
}
@ -50,7 +50,10 @@ std::shared_ptr<UINode> create_debug_panel(
Player& player,
bool allowDebugCheats
) {
auto panel = std::make_shared<Panel>(glm::vec2(300, 200), glm::vec4(5.0f), 2.0f);
auto& gui = engine.getGUI();
auto panel = std::make_shared<Panel>(
gui, glm::vec2(300, 200), glm::vec4(5.0f), 2.0f
);
panel->setId("hud.debug-panel");
panel->setPos(glm::vec2(10, 10));
@ -87,48 +90,48 @@ std::shared_ptr<UINode> create_debug_panel(
lastTotalUpload = totalUpload;
});
panel->add(create_label([]() { return L"fps: "+fpsString;}));
panel->add(create_label(gui, []() { return L"fps: "+fpsString;}));
panel->add(create_label([]() {
panel->add(create_label(gui, []() {
return L"meshes: " + std::to_wstring(Mesh::meshesCount);
}));
panel->add(create_label([]() {
panel->add(create_label(gui, []() {
int drawCalls = Mesh::drawCalls;
Mesh::drawCalls = 0;
return L"draw-calls: " + std::to_wstring(drawCalls);
}));
panel->add(create_label([]() {
panel->add(create_label(gui, []() {
return L"speakers: " + std::to_wstring(audio::count_speakers())+
L" streams: " + std::to_wstring(audio::count_streams());
}));
panel->add(create_label([]() {
panel->add(create_label(gui, []() {
return L"lua-stack: " + std::to_wstring(scripting::get_values_on_stack());
}));
panel->add(create_label([]() { return netSpeedString; }));
panel->add(create_label([&engine]() {
panel->add(create_label(gui, []() { return netSpeedString; }));
panel->add(create_label(gui, [&engine]() {
auto& settings = engine.getSettings();
bool culling = settings.graphics.frustumCulling.get();
return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off");
}));
panel->add(create_label([=]() {
panel->add(create_label(gui, [=]() {
return L"particles: " +
std::to_wstring(ParticlesRenderer::visibleParticles) +
L" emitters: " +
std::to_wstring(ParticlesRenderer::aliveEmitters);
}));
panel->add(create_label([&]() {
panel->add(create_label(gui, [&]() {
return L"chunks: "+std::to_wstring(level.chunks->size())+
L" visible: "+std::to_wstring(ChunksRenderer::visibleChunks);
}));
panel->add(create_label([&]() {
panel->add(create_label(gui, [&]() {
return L"entities: "+std::to_wstring(level.entities->size())+L" next: "+
std::to_wstring(level.entities->peekNextID());
}));
panel->add(create_label([&]() {
panel->add(create_label(gui, [&]() {
return L"players: "+std::to_wstring(level.players->size())+L" local: "+
std::to_wstring(player.getId());
}));
panel->add(create_label([&]() -> std::wstring {
panel->add(create_label(gui, [&]() -> std::wstring {
const auto& vox = player.selection.vox;
std::wstringstream stream;
stream << "r:" << vox.state.rotation << " s:"
@ -141,7 +144,7 @@ std::shared_ptr<UINode> create_debug_panel(
L" "+stream.str();
}
}));
panel->add(create_label([&]() -> std::wstring {
panel->add(create_label(gui, [&]() -> std::wstring {
const auto& selection = player.selection;
const auto& vox = selection.vox;
if (vox.id == BLOCK_VOID) {
@ -151,7 +154,7 @@ std::shared_ptr<UINode> create_debug_panel(
L" y: " + std::to_wstring(selection.actualPosition.y) +
L" z: " + std::to_wstring(selection.actualPosition.z);
}));
panel->add(create_label([&]() {
panel->add(create_label(gui, [&]() {
auto eid = player.getSelectedEntity();
if (eid == ENTITY_NONE) {
return std::wstring {L"entity: -"};
@ -162,7 +165,7 @@ std::shared_ptr<UINode> create_debug_panel(
return std::wstring {L"entity: error (invalid UID)"};
}
}));
panel->add(create_label([&](){
panel->add(create_label(gui, [&](){
auto indices = level.content.getIndices();
if (auto def = indices->blocks.get(player.selection.vox.id)) {
return L"name: " + util::str2wstr_utf8(def->name);
@ -170,23 +173,23 @@ std::shared_ptr<UINode> create_debug_panel(
return std::wstring {L"name: void"};
}
}));
panel->add(create_label([&](){
panel->add(create_label(gui, [&](){
return L"seed: "+std::to_wstring(level.getWorld()->getSeed());
}));
for (int ax = 0; ax < 3; ax++) {
auto sub = std::make_shared<Container>(glm::vec2(250, 27));
auto sub = std::make_shared<Container>(gui, glm::vec2(250, 27));
std::wstring str = L"x: ";
str[0] += ax;
auto label = std::make_shared<Label>(str);
auto label = std::make_shared<Label>(gui, str);
label->setMargin(glm::vec4(2, 3, 2, 3));
label->setSize(glm::vec2(20, 27));
sub->add(label);
sub->setColor(glm::vec4(0.0f));
// Coord input
auto box = std::make_shared<TextBox>(L"");
auto box = std::make_shared<TextBox>(gui, L"");
auto boxRef = box.get();
box->setTextSupplier([&player, ax]() {
return util::to_wstring(player.getPosition()[ax], 2);
@ -212,7 +215,7 @@ std::shared_ptr<UINode> create_debug_panel(
panel->add(sub);
}
auto& worldInfo = level.getWorld()->getInfo();
panel->add(create_label([&](){
panel->add(create_label(gui, [&](){
int hour, minute, second;
timeutil::from_value(worldInfo.daytime, hour, minute, second);
@ -222,20 +225,20 @@ std::shared_ptr<UINode> create_debug_panel(
return L"time: "+timeString;
}));
if (allowDebugCheats) {
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 1.0f, 0.005f, 8);
auto bar = std::make_shared<TrackBar>(gui, 0.0f, 1.0f, 1.0f, 0.005f, 8);
bar->setSupplier([&]() {return worldInfo.daytime;});
bar->setConsumer([&](double val) {worldInfo.daytime = val;});
panel->add(bar);
}
if (allowDebugCheats) {
auto bar = std::make_shared<TrackBar>(0.0f, 1.0f, 0.0f, 0.005f, 8);
auto bar = std::make_shared<TrackBar>(gui, 0.0f, 1.0f, 0.0f, 0.005f, 8);
bar->setSupplier([&]() {return worldInfo.fog;});
bar->setConsumer([&](double val) {worldInfo.fog = val;});
panel->add(bar);
}
{
auto checkbox = std::make_shared<FullCheckBox>(
L"Show Chunk Borders", glm::vec2(400, 24)
gui, L"Show Chunk Borders", glm::vec2(400, 24)
);
checkbox->setSupplier([=]() {
return WorldRenderer::showChunkBorders;
@ -247,7 +250,7 @@ std::shared_ptr<UINode> create_debug_panel(
}
{
auto checkbox = std::make_shared<FullCheckBox>(
L"Show Hitboxes", glm::vec2(400, 24)
gui, L"Show Hitboxes", glm::vec2(400, 24)
);
checkbox->setSupplier([=]() {
return WorldRenderer::showEntitiesDebug;
@ -259,7 +262,7 @@ std::shared_ptr<UINode> create_debug_panel(
}
{
auto checkbox = std::make_shared<FullCheckBox>(
L"Show Generator Minimap", glm::vec2(400, 24)
gui, L"Show Generator Minimap", glm::vec2(400, 24)
);
checkbox->setSupplier([=]() {
return Hud::showGeneratorMinimap;

View File

@ -13,8 +13,6 @@
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/Batch3D.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Font.hpp"
#include "graphics/core/Mesh.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/Texture.hpp"
#include "graphics/core/ImageData.hpp"
@ -42,7 +40,6 @@
#include "voxels/Chunks.hpp"
#include "voxels/GlobalChunks.hpp"
#include "window/Camera.hpp"
#include "window/Events.hpp"
#include "window/input.hpp"
#include "window/Window.hpp"
#include "world/Level.hpp"
@ -126,7 +123,7 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
inventory->getSlot(player.getChosenSlot()).set(item);
});
InventoryBuilder builder;
InventoryBuilder builder(gui);
builder.addGrid(8, itemsCount-1, glm::vec2(), glm::vec4(8, 8, 12, 8), true, slotLayout);
auto view = builder.build();
view->bind(accessInventory, &content);
@ -139,7 +136,7 @@ std::shared_ptr<InventoryView> Hud::createHotbar() {
auto& content = frontend.getLevel().content;
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr);
InventoryBuilder builder;
InventoryBuilder builder(gui);
builder.addGrid(10, 10, glm::vec2(), glm::vec4(4), true, slotLayout);
auto view = builder.build();
view->setId("hud.hotbar");
@ -153,8 +150,10 @@ static constexpr uint WORLDGEN_IMG_SIZE = 128U;
Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
: engine(engine),
input(engine.getInput()),
assets(*engine.getAssets()),
gui(*engine.getGUI()),
gui(engine.getGUI()),
menu(*engine.getGUI().getMenu()),
frontend(frontend),
player(player),
debugImgWorldGen(std::make_unique<ImageData>(
@ -163,7 +162,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
contentAccess = createContentAccess();
contentAccess->setId("hud.content-access");
contentAccessPanel = std::make_shared<Panel>(
contentAccess->getSize(), glm::vec4(0.0f), 0.0f
gui, contentAccess->getSize(), glm::vec4(0.0f), 0.0f
);
contentAccessPanel->setColor(glm::vec4());
contentAccessPanel->add(contentAccess);
@ -172,6 +171,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
hotbarView = createHotbar();
darkOverlay = guiutil::create(
gui,
"<container size='4000' color='#00000080' z-index='-1' visible='false'/>"
);
@ -183,13 +183,13 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
engine, frontend.getLevel(), player, allowDebugCheats
);
debugPanel->setZIndex(2);
gui.add(debugPanel);
gui.add(darkOverlay);
gui.add(hotbarView);
gui.add(contentAccessPanel);
auto dplotter = std::make_shared<Plotter>(350, 250, 2000, 16);
auto dplotter = std::make_shared<Plotter>(gui, 350, 250, 2000, 16);
dplotter->setGravity(Gravity::bottom_right);
dplotter->setInteractive(false);
add(HudElement(HudElementMode::PERMANENT, nullptr, dplotter, true));
@ -197,9 +197,10 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
assets.store(Texture::from(debugImgWorldGen.get()), DEBUG_WORLDGEN_IMAGE);
debugMinimap = guiutil::create(
"<image src='"+DEBUG_WORLDGEN_IMAGE+
gui,
"<image src='" + DEBUG_WORLDGEN_IMAGE +
"' pos='0' size='256' gravity='top-right' margin='0,20,0,0'/>"
);
);
add(HudElement(HudElementMode::PERMANENT, nullptr, debugMinimap, true));
}
@ -223,11 +224,12 @@ void Hud::cleanup() {
}
void Hud::processInput(bool visible) {
auto menu = gui.getMenu();
if (!Window::isFocused() && !menu->hasOpenPage() && !isInventoryOpen()) {
const auto& window = engine.getWindow();
if (!window.isFocused() && !menu.hasOpenPage() && !isInventoryOpen()) {
setPause(true);
}
if (!pause && visible && Events::jactive(BIND_HUD_INVENTORY)) {
const auto& bindings = input.getBindings();
if (!pause && visible && bindings.jactive(BIND_HUD_INVENTORY)) {
if (inventoryOpen) {
closeInventory();
} else {
@ -240,24 +242,25 @@ void Hud::processInput(bool visible) {
}
void Hud::updateHotbarControl() {
if (!inventoryOpen && Events::scroll) {
int scroll = input.getScroll();
if (!inventoryOpen && scroll) {
int slot = player.getChosenSlot();
slot = (slot - Events::scroll) % 10;
slot = (slot - scroll) % 10;
if (slot < 0) {
slot += 10;
}
player.setChosenSlot(slot);
}
for (
int i = static_cast<int>(keycode::NUM_1);
i <= static_cast<int>(keycode::NUM_9);
int i = static_cast<int>(Keycode::NUM_1);
i <= static_cast<int>(Keycode::NUM_9);
i++
) {
if (Events::jpressed(i)) {
player.setChosenSlot(i - static_cast<int>(keycode::NUM_1));
if (input.jpressed(static_cast<Keycode>(i))) {
player.setChosenSlot(i - static_cast<int>(Keycode::NUM_1));
}
}
if (Events::jpressed(keycode::NUM_0)) {
if (input.jpressed(Keycode::NUM_0)) {
player.setChosenSlot(9);
}
}
@ -311,7 +314,6 @@ void Hud::updateWorldGenDebug() {
void Hud::update(bool visible) {
const auto& chunks = *player.chunks;
const auto& menu = gui.getMenu();
debugPanel->setVisible(
debug && visible && !(inventoryOpen && inventoryView == nullptr)
@ -320,14 +322,14 @@ void Hud::update(bool visible) {
if (!visible && inventoryOpen) {
closeInventory();
}
if (pause && !menu->hasOpenPage()) {
if (pause && !menu.hasOpenPage()) {
setPause(false);
}
if (!gui.isFocusCaught()) {
processInput(visible);
}
if ((menu->hasOpenPage() || inventoryOpen) == Events::isCursorLocked()) {
Events::toggleCursor();
if ((menu.hasOpenPage() || inventoryOpen) == input.getCursor().locked) {
input.toggleCursor();
}
if (blockUI) {
@ -341,10 +343,11 @@ void Hud::update(bool visible) {
element.getNode()->setVisible(visible);
}
const auto& windowSize = engine.getWindow().getSize();
glm::vec2 caSize = contentAccessPanel->getSize();
contentAccessPanel->setVisible(inventoryView != nullptr && showContentPanel);
contentAccessPanel->setSize(glm::vec2(caSize.x, Window::height));
contentAccess->setMinSize(glm::vec2(1, Window::height));
contentAccessPanel->setSize(glm::vec2(caSize.x, windowSize.y));
contentAccess->setMinSize(glm::vec2(1, windowSize.y));
hotbarView->setVisible(visible && !(secondUI && !inventoryView));
if (visible) {
@ -447,6 +450,7 @@ void Hud::showExchangeSlot() {
auto& content = level.content;
exchangeSlotInv = level.inventories->createVirtual(1);
exchangeSlot = std::make_shared<SlotView>(
gui,
SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr)
);
exchangeSlot->bind(exchangeSlotInv->getId(), exchangeSlotInv->getSlot(0), &content);
@ -587,18 +591,16 @@ void Hud::setDebug(bool flag) {
}
void Hud::draw(const DrawContext& ctx){
const Viewport& viewport = ctx.getViewport();
const uint width = viewport.getWidth();
const uint height = viewport.getHeight();
auto menu = gui.getMenu();
const auto& viewport = ctx.getViewport();
bool is_menu_open = menu->hasOpenPage();
bool is_menu_open = menu.hasOpenPage();
darkOverlay->setVisible(is_menu_open);
menu->setVisible(is_menu_open);
menu.setVisible(is_menu_open);
updateElementsPosition(viewport);
uicamera->setFov(height);
uicamera->setFov(viewport.y);
uicamera->setAspectRatio(viewport.x / static_cast<float>(viewport.y));
auto batch = ctx.getBatch2D();
batch->begin();
@ -616,28 +618,28 @@ void Hud::draw(const DrawContext& ctx){
int chsizex = texture != nullptr ? texture->getWidth() : 16;
int chsizey = texture != nullptr ? texture->getHeight() : 16;
batch->rect(
(width-chsizex)/2, (height-chsizey)/2,
chsizex, chsizey, 0,0, 1,1, 1,1,1,1
(viewport.x - chsizex) / 2, (viewport.y - chsizey) / 2,
chsizex, chsizey, 0, 0, 1, 1, 1, 1, 1, 1
);
}
}
void Hud::updateElementsPosition(const Viewport& viewport) {
const uint width = viewport.getWidth();
const uint height = viewport.getHeight();
void Hud::updateElementsPosition(const glm::uvec2& viewport) {
if (inventoryOpen) {
float caWidth = inventoryView && showContentPanel
? contentAccess->getSize().x
: 0.0f;
contentAccessPanel->setPos(glm::vec2(width-caWidth, 0));
contentAccessPanel->setPos(glm::vec2(viewport.x - caWidth, 0));
glm::vec2 invSize = inventoryView ? inventoryView->getSize() : glm::vec2();
if (secondUI == nullptr) {
if (inventoryView) {
inventoryView->setPos(glm::vec2(
glm::min(width/2-invSize.x/2, width-caWidth-10-invSize.x),
height/2-invSize.y/2
glm::min(
viewport.x / 2 - invSize.x / 2,
viewport.x - caWidth - 10 - invSize.x
),
viewport.y / 2 - invSize.y / 2
));
}
} else {
@ -647,25 +649,29 @@ void Hud::updateElementsPosition(const Viewport& viewport) {
float totalHeight = invSize.y + secondUISize.y + interval;
if (inventoryView) {
inventoryView->setPos(glm::vec2(
glm::min(width/2-invwidth/2, width-caWidth-10-invwidth),
height/2+totalHeight/2-invSize.y
glm::min(
viewport.x / 2 - invwidth / 2,
viewport.x - caWidth - 10 - invwidth
),
viewport.y / 2 + totalHeight / 2 - invSize.y
));
}
if (secondUI->getPositionFunc() == nullptr) {
secondUI->setPos(glm::vec2(
glm::min(
width / 2.f - invwidth / 2.f,
width - caWidth - (inventoryView ? 10 : 0) - invwidth
viewport.x / 2.f - invwidth / 2.f,
viewport.x - caWidth - (inventoryView ? 10 : 0) -
invwidth
),
height / 2.f - totalHeight / 2.f
viewport.y / 2.f - totalHeight / 2.f
));
}
}
}
if (exchangeSlot != nullptr) {
exchangeSlot->setPos(glm::vec2(Events::cursor));
exchangeSlot->setPos(input.getCursor().pos);
}
hotbarView->setPos(glm::vec2(width/2, height-65));
hotbarView->setPos(glm::vec2(viewport.x / 2, viewport.y - 65));
hotbarView->setSelected(player.getChosenSlot());
}
@ -689,12 +695,11 @@ void Hud::setPause(bool pause) {
closeInventory();
}
const auto& menu = gui.getMenu();
if (!pause && menu->hasOpenPage()) {
menu->reset();
if (!pause && menu.hasOpenPage()) {
menu.reset();
}
if (pause && !menu->hasOpenPage()) {
menu->setPage("pause");
if (pause && !menu.hasOpenPage()) {
menu.setPage("pause");
}
}
@ -730,9 +735,8 @@ void Hud::setDebugCheats(bool flag) {
void Hud::setAllowPause(bool flag) {
if (pause) {
auto menu = gui.getMenu();
setPause(false);
menu->setPage("pause", true);
menu.setPage("pause", true);
}
allowPause = flag;
}

View File

@ -18,11 +18,12 @@ class Inventory;
class LevelFrontend;
class UiDocument;
class DrawContext;
class Viewport;
class ImageData;
class Input;
namespace gui {
class GUI;
class Menu;
class UINode;
class Panel;
class Container;
@ -71,9 +72,11 @@ public:
class Hud : public util::ObjectsKeeper {
Engine& engine;
Input& input;
Assets& assets;
std::unique_ptr<Camera> uicamera;
gui::GUI& gui;
gui::Menu& menu;
std::unique_ptr<Camera> uicamera;
LevelFrontend& frontend;
Player& player;
@ -127,7 +130,7 @@ class Hud : public util::ObjectsKeeper {
std::shared_ptr<gui::InventoryView> createHotbar();
void processInput(bool visible);
void updateElementsPosition(const Viewport& viewport);
void updateElementsPosition(const glm::uvec2& viewport);
void updateHotbarControl();
void cleanup();

View File

@ -4,7 +4,6 @@
#include "coders/json.hpp"
#include "coders/BasicParser.hpp"
#include "content/ContentPack.hpp"
#include "io/io.hpp"
#include "util/stringutil.hpp"
#include "data/dv.hpp"
@ -14,9 +13,12 @@ static debug::Logger logger("locale");
namespace fs = std::filesystem;
using namespace std::literals;
using namespace langs;
std::unique_ptr<langs::Lang> langs::current;
std::unordered_map<std::string, langs::LocaleInfo> langs::locales_info;
namespace {
std::unique_ptr<langs::Lang> current;
std::unordered_map<std::string, LocaleInfo> locales_info;
}
langs::Lang::Lang(std::string locale) : locale(std::move(locale)) {
}
@ -40,18 +42,10 @@ const std::string& langs::Lang::getId() const {
/// @brief Language key-value txt files parser
namespace {
class Reader : BasicParser<char> {
void skipWhitespace() override {
BasicParser::skipWhitespace();
if (hasNext() && source[pos] == '#') {
skipLine();
if (hasNext() && is_whitespace(peek())) {
skipWhitespace();
}
}
}
public:
Reader(std::string_view file, std::string_view source)
: BasicParser(file, source) {
hashComment = true;
}
void read(langs::Lang& lang, const std::string &prefix) {
@ -69,11 +63,11 @@ namespace {
};
}
void langs::loadLocalesInfo(const io::path& resdir, std::string& fallback) {
auto file = resdir / langs::TEXTS_FOLDER / "langs.json";
static void load_locales_info(std::string& fallback) {
auto file = io::path("res:") / langs::TEXTS_FOLDER / "langs.json";
auto root = io::read_json(file);
langs::locales_info.clear();
::locales_info.clear();
root.at("fallback").get(fallback);
if (auto found = root.at("langs")) {
@ -88,16 +82,62 @@ void langs::loadLocalesInfo(const io::path& resdir, std::string& fallback) {
continue;
}
logline << key << " ";
langs::locales_info[key] = LocaleInfo {key, name};
::locales_info[key] = LocaleInfo {key, name};
}
logline << "added";
}
}
std::string langs::locale_by_envlocale(const std::string& envlocale, const io::path& resdir){
static void load(
const std::string& locale,
const std::vector<io::path>& roots,
Lang& lang
) {
io::path filename = io::path(TEXTS_FOLDER) / (locale + LANG_FILE_EXT);
io::path core_file = io::path("res:") / filename;
if (io::is_regular_file(core_file)) {
std::string text = io::read_string(core_file);
Reader reader(core_file.string(), text);
reader.read(lang, "");
}
for (auto root : roots) {
io::path file = root / filename;
if (io::is_regular_file(file)) {
std::string text = io::read_string(file);
Reader reader(file.string(), text);
reader.read(lang, "");
}
}
}
static void load(
const std::string& locale,
const std::string& fallback,
const std::vector<io::path>& roots
) {
auto lang = std::make_unique<Lang>(locale);
load(fallback, roots, *lang.get());
if (locale != fallback) {
load(locale, roots, *lang.get());
}
current = std::move(lang);
}
const std::string& langs::get_current() {
if (current == nullptr) {
throw std::runtime_error("localization is not initialized");
}
return current->getId();
}
const std::unordered_map<std::string, LocaleInfo>& langs::get_locales_info() {
return ::locales_info;
}
std::string langs::locale_by_envlocale(const std::string& envlocale){
std::string fallback = FALLBACK_DEFAULT;
if (locales_info.size() == 0) {
loadLocalesInfo(resdir, fallback);
load_locales_info(fallback);
}
if (locales_info.find(envlocale) != locales_info.end()) {
logger.info() << "locale " << envlocale << " is automatically selected";
@ -115,49 +155,16 @@ std::string langs::locale_by_envlocale(const std::string& envlocale, const io::p
}
}
void langs::load(const io::path& resdir,
const std::string& locale,
const std::vector<ContentPack>& packs,
Lang& lang) {
io::path filename = io::path(TEXTS_FOLDER) / (locale + LANG_FILE_EXT);
io::path core_file = resdir / filename;
if (io::is_regular_file(core_file)) {
std::string text = io::read_string(core_file);
Reader reader(core_file.string(), text);
reader.read(lang, "");
}
for (auto pack : packs) {
io::path file = pack.folder / filename;
if (io::is_regular_file(file)) {
std::string text = io::read_string(file);
Reader reader(file.string(), text);
reader.read(lang, "");
}
}
}
void langs::load(const io::path& resdir,
const std::string& locale,
const std::string& fallback,
const std::vector<ContentPack>& packs) {
auto lang = std::make_unique<Lang>(locale);
load(resdir, fallback, packs, *lang.get());
if (locale != fallback) {
load(resdir, locale, packs, *lang.get());
}
current = std::move(lang);
}
void langs::setup(const io::path& resdir,
std::string locale,
const std::vector<ContentPack>& packs) {
void langs::setup(
std::string locale,
const std::vector<io::path>& roots
) {
std::string fallback = langs::FALLBACK_DEFAULT;
langs::loadLocalesInfo(resdir, fallback);
if (langs::locales_info.find(locale) == langs::locales_info.end()) {
load_locales_info(fallback);
if (locales_info.find(locale) == locales_info.end()) {
locale = fallback;
}
langs::load(resdir, locale, fallback, packs);
load(locale, fallback, roots);
}
const std::wstring& langs::get(const std::wstring& key) {

View File

@ -7,8 +7,6 @@
#include "io/fwd.hpp"
struct ContentPack;
namespace langs {
const char LANG_FILE_EXT[] = ".txt";
const char TEXTS_FOLDER[] = "texts";
@ -41,30 +39,17 @@ namespace langs {
std::string name;
};
extern std::unique_ptr<Lang> current;
extern std::unordered_map<std::string, LocaleInfo> locales_info;
std::string locale_by_envlocale(const std::string& envlocale);
extern void loadLocalesInfo(
const io::path& resdir,
std::string& fallback);
const std::string& get_current();
const std::unordered_map<std::string, LocaleInfo>& get_locales_info();
extern std::string locale_by_envlocale(const std::string& envlocale,
const io::path& resdir);
const std::wstring& get(const std::wstring& key);
const std::wstring& get(
const std::wstring& key, const std::wstring& context
);
extern void load(const io::path& resdir,
const std::string& locale,
const std::vector<ContentPack>& packs,
Lang& lang);
extern void load(const io::path& resdir,
const std::string& locale,
const std::string& fallback,
const std::vector<ContentPack>& packs);
extern const std::wstring& get(const std::wstring& key);
extern const std::wstring& get(const std::wstring& key,
const std::wstring& context);
extern void setup(const io::path& resdir,
std::string locale,
const std::vector<ContentPack>& packs);
/// @brief Change locale to specified
/// @param locale isolanguage_ISOCOUNTRY (example: en_US)
void setup(std::string locale, const std::vector<io::path>& roots);
}

View File

@ -1,82 +1,88 @@
#include "menu.hpp"
#include "locale.hpp"
#include "UiDocument.hpp"
#include "screens/MenuScreen.hpp"
#include "delegates.hpp"
#include "engine/Engine.hpp"
#include "data/dv.hpp"
#include "interfaces/Task.hpp"
#include "io/engine_paths.hpp"
#include "graphics/ui/elements/Menu.hpp"
#include "graphics/ui/gui_util.hpp"
#include "graphics/ui/GUI.hpp"
#include "logic/scripting/scripting.hpp"
#include "settings.hpp"
#include "coders/commons.hpp"
#include "util/stringutil.hpp"
#include "window/Window.hpp"
#include <filesystem>
#include <glm/glm.hpp>
namespace fs = std::filesystem;
#include "UiDocument.hpp"
#include "coders/commons.hpp"
#include "data/dv.hpp"
#include "delegates.hpp"
#include "engine/Engine.hpp"
#include "graphics/ui/GUI.hpp"
#include "graphics/ui/elements/Menu.hpp"
#include "graphics/ui/gui_util.hpp"
#include "interfaces/Task.hpp"
#include "io/engine_paths.hpp"
#include "locale.hpp"
#include "logic/scripting/scripting.hpp"
#include "screens/MenuScreen.hpp"
#include "settings.hpp"
#include "util/stringutil.hpp"
#include "assets/assetload_funcs.hpp"
#include "content/ContentPack.hpp"
using namespace gui;
void menus::create_version_label(Engine& engine) {
auto gui = engine.getGUI();
auto text = ENGINE_VERSION_STRING+" debug build";
gui->add(guiutil::create(
"<label z-index='1000' color='#FFFFFF80' gravity='top-right' margin='4'>"
+text+
"</label>"
void menus::create_version_label(gui::GUI& gui) {
auto text = ENGINE_VERSION_STRING + " debug build";
gui.add(guiutil::create(
gui,
"<label z-index='1000' color='#FFFFFF80' gravity='top-right' "
"margin='4'>" +
text + "</label>"
));
}
bool menus::call(Engine& engine, runnable func) {
void menus::call(Engine& engine, runnable func) {
if (engine.isHeadless()) {
throw std::runtime_error("menus::call(...) in headless mode");
}
try {
func();
return true;
} catch (const contentpack_error& error) {
engine.setScreen(std::make_shared<MenuScreen>(engine));
// could not to find or read pack
guiutil::alert(
engine, langs::get(L"error.pack-not-found")+L": "+
util::str2wstr_utf8(error.getPackId())
engine,
langs::get(L"error.pack-not-found") + L": " +
util::str2wstr_utf8(error.getPackId())
);
return false;
throw std::runtime_error(error);
} catch (const assetload::error& error) {
engine.setScreen(std::make_shared<MenuScreen>(engine));
guiutil::alert(
engine, langs::get(L"Assets Load Error", L"menu")+L":\n"+
engine,
langs::get(L"Assets Load Error", L"menu") + L":\n" +
util::str2wstr_utf8(error.what())
);
return false;
throw std::runtime_error(error);
} catch (const parsing_error& error) {
engine.setScreen(std::make_shared<MenuScreen>(engine));
guiutil::alert(engine, util::str2wstr_utf8(error.errorLog()));
return false;
throw std::runtime_error(error);
} catch (const std::runtime_error& error) {
engine.setScreen(std::make_shared<MenuScreen>(engine));
guiutil::alert(
engine, langs::get(L"Content Error", L"menu")+L":\n"+
engine,
langs::get(L"Content Error", L"menu") + L":\n" +
util::str2wstr_utf8(error.what())
);
return false;
throw std::runtime_error(error);
}
}
UiDocument* menus::show(Engine& engine, const std::string& name, std::vector<dv::value> args) {
auto menu = engine.getGUI()->getMenu();
auto file = engine.getResPaths()->find("layouts/"+name+".xml");
auto fullname = "core:layouts/"+name;
UiDocument* menus::show(
Engine& engine, const std::string& name, std::vector<dv::value> args
) {
auto menu = engine.getGUI().getMenu();
auto file = engine.getResPaths().find("layouts/" + name + ".xml");
auto fullname = "core:layouts/" + name;
auto documentPtr = UiDocument::read(
scripting::get_root_environment(), fullname, file, "core:layouts/"+name
engine.getGUI(),
scripting::get_root_environment(),
fullname,
file,
"core:layouts/" + name
);
auto document = documentPtr.get();
engine.getAssets()->store(std::move(documentPtr), fullname);
@ -86,18 +92,20 @@ UiDocument* menus::show(Engine& engine, const std::string& name, std::vector<dv:
return document;
}
void menus::show_process_panel(Engine& engine, const std::shared_ptr<Task>& task, const std::wstring& text) {
void menus::show_process_panel(
Engine& engine, const std::shared_ptr<Task>& task, const std::wstring& text
) {
uint initialWork = task->getWorkTotal();
auto menu = engine.getGUI()->getMenu();
auto menu = engine.getGUI().getMenu();
menu->reset();
auto doc = menus::show(engine, "process", {
util::wstr2str_utf8(langs::get(text))
});
std::dynamic_pointer_cast<Container>(doc->getRoot())->listenInterval(0.01f, [=]() {
task->update();
auto doc =
menus::show(engine, "process", {util::wstr2str_utf8(langs::get(text))});
std::dynamic_pointer_cast<Container>(doc->getRoot())
->listenInterval(0.01f, [=]() {
task->update();
uint tasksDone = task->getWorkDone();
scripting::on_ui_progress(doc, tasksDone, initialWork);
});
uint tasksDone = task->getWorkDone();
scripting::on_ui_progress(doc, tasksDone, initialWork);
});
}

View File

@ -1,7 +1,7 @@
#pragma once
#include "data/dv.hpp"
#include "graphics/ui/elements/Menu.hpp"
#include "delegates.hpp"
#include <string>
#include <vector>
@ -12,9 +12,13 @@ class Engine;
class UiDocument;
namespace gui {
class GUI;
}
namespace menus {
/// @brief Create development version label at the top-right screen corner
void create_version_label(Engine& engine);
void create_version_label(gui::GUI& gui);
UiDocument* show(
Engine& engine,
@ -22,7 +26,11 @@ namespace menus {
std::vector<dv::value> args
);
void show_process_panel(Engine& engine, const std::shared_ptr<Task>& task, const std::wstring& text=L"");
void show_process_panel(
Engine& engine,
const std::shared_ptr<Task>& task,
const std::wstring& text = L""
);
bool call(Engine& engine, runnable func);
void call(Engine& engine, runnable func);
}

View File

@ -6,19 +6,19 @@
#include "core_defs.hpp"
#include "debug/Logger.hpp"
#include "engine/Engine.hpp"
#include "io/io.hpp"
#include "assets/Assets.hpp"
#include "frontend/ContentGfxCache.hpp"
#include "frontend/LevelFrontend.hpp"
#include "frontend/hud.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/ImageData.hpp"
#include "graphics/core/PostProcessing.hpp"
#include "graphics/core/Viewport.hpp"
#include "graphics/render/Decorator.hpp"
#include "graphics/render/WorldRenderer.hpp"
#include "graphics/ui/GUI.hpp"
#include "graphics/ui/elements/Menu.hpp"
#include "graphics/ui/GUI.hpp"
#include "frontend/ContentGfxCache.hpp"
#include "graphics/core/TextureAnimation.hpp"
#include "io/io.hpp"
#include "logic/LevelController.hpp"
#include "logic/PlayerController.hpp"
#include "logic/scripting/scripting.hpp"
@ -29,7 +29,6 @@
#include "util/stringutil.hpp"
#include "voxels/Chunks.hpp"
#include "window/Camera.hpp"
#include "window/Events.hpp"
#include "window/Window.hpp"
#include "world/Level.hpp"
#include "world/World.hpp"
@ -43,28 +42,27 @@ LevelScreen::LevelScreen(
)
: Screen(engine),
world(*levelPtr->getWorld()),
postProcessing(std::make_unique<PostProcessing>()) {
postProcessing(std::make_unique<PostProcessing>()),
gui(engine.getGUI()),
input(engine.getInput()) {
Level* level = levelPtr.get();
auto& settings = engine.getSettings();
auto& assets = *engine.getAssets();
auto menu = engine.getGUI()->getMenu();
auto menu = engine.getGUI().getMenu();
menu->reset();
auto player = level->players->get(localPlayer);
assert(player != nullptr);
controller =
std::make_unique<LevelController>(&engine, std::move(levelPtr), player);
playerController = std::make_unique<PlayerController>(
settings,
*level,
*player,
*controller->getBlocksController()
settings, *level, *player, *controller->getBlocksController()
);
frontend = std::make_unique<LevelFrontend>(
player, controller.get(), assets, settings
engine, player, controller.get(), settings
);
renderer = std::make_unique<WorldRenderer>(
engine, *frontend, *player
@ -87,7 +85,7 @@ LevelScreen::LevelScreen(
keepAlive(settings.camera.fov.observe([=](double value) {
player->fpCamera->setFov(glm::radians(value));
}));
keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){
keepAlive(input.addCallback(BIND_CHUNKS_RELOAD, [=]() {
player->chunks->saveAndClear();
renderer->clear();
return false;
@ -107,7 +105,7 @@ LevelScreen::~LevelScreen() {
}
scripting::on_frontend_close();
// unblock all bindings
Events::enableBindings();
input.getBindings().enableAll();
controller->onWorldQuit();
engine.getPaths().setCurrentWorldFolder("");
}
@ -162,11 +160,14 @@ void LevelScreen::saveWorldPreview() {
Camera camera = *player->fpCamera;
camera.setFov(glm::radians(70.0f));
DrawContext pctx(nullptr, {Window::width, Window::height}, batch.get());
DrawContext pctx(nullptr, engine.getWindow(), batch.get());
DrawContext ctx(&pctx, engine.getWindow(), batch.get());
ctx.setViewport(
{static_cast<uint>(previewSize * 1.5),
static_cast<uint>(previewSize)}
);
Viewport viewport(previewSize * 1.5, previewSize);
DrawContext ctx(&pctx, viewport, batch.get());
renderer->draw(ctx, camera, false, true, 0.0f, *postProcessing);
auto image = postProcessing->toImage();
image->flipY();
@ -178,13 +179,14 @@ void LevelScreen::saveWorldPreview() {
void LevelScreen::updateHotkeys() {
auto& settings = engine.getSettings();
if (Events::jpressed(keycode::O)) {
if (input.jpressed(Keycode::O)) {
settings.graphics.frustumCulling.toggle();
}
if (Events::jpressed(keycode::F1)) {
if (input.jpressed(Keycode::F1)) {
hudVisible = !hudVisible;
}
if (Events::jpressed(keycode::F3)) {
if (input.jpressed(Keycode::F3)) {
debug = !debug;
hud->setDebug(debug);
renderer->setDebug(debug);
@ -199,19 +201,16 @@ void LevelScreen::updateAudio() {
audio::get_channel("regular")->setPaused(paused);
audio::get_channel("ambient")->setPaused(paused);
glm::vec3 velocity {};
if (auto hitbox = player->getHitbox()) {
if (auto hitbox = player->getHitbox()) {
velocity = hitbox->velocity;
}
audio::set_listener(
camera->position,
velocity,
camera->dir,
glm::vec3(0, 1, 0)
camera->position, velocity, camera->dir, glm::vec3(0, 1, 0)
);
}
void LevelScreen::update(float delta) {
auto& gui = *engine.getGUI();
auto& gui = engine.getGUI();
if (!gui.isFocusCaught()) {
updateHotkeys();
@ -225,10 +224,15 @@ void LevelScreen::update(float delta) {
if (!paused) {
world.updateTimers(delta);
animator->update(delta);
playerController->update(delta, !inputLocked);
playerController->update(delta, inputLocked ? nullptr : &engine.getInput());
}
controller->update(glm::min(delta, 0.2f), paused);
playerController->postUpdate(delta, !inputLocked, paused);
playerController->postUpdate(
delta,
engine.getWindow().getSize().y,
inputLocked ? nullptr : &engine.getInput(),
paused
);
hud->update(hudVisible);
@ -241,8 +245,7 @@ void LevelScreen::update(float delta) {
void LevelScreen::draw(float delta) {
auto camera = playerController->getPlayer()->currentCamera;
Viewport viewport(Window::width, Window::height);
DrawContext ctx(nullptr, viewport, batch.get());
DrawContext ctx(nullptr, engine.getWindow(), batch.get());
if (!hud->isPause()) {
scripting::on_entities_render(engine.getTime().getDelta());

View File

@ -16,6 +16,11 @@ class ContentPackRuntime;
class Decorator;
class Level;
class World;
class Input;
namespace gui {
class GUI;
}
class LevelScreen : public Screen {
World& world;
@ -27,6 +32,8 @@ class LevelScreen : public Screen {
std::unique_ptr<PostProcessing> postProcessing;
std::unique_ptr<Decorator> decorator;
std::unique_ptr<Hud> hud;
gui::GUI& gui;
Input& input;
void saveWorldPreview();

View File

@ -1,29 +1,31 @@
#include "MenuScreen.hpp"
#include "content/ContentControl.hpp"
#include "graphics/ui/GUI.hpp"
#include "graphics/ui/elements/Menu.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/Texture.hpp"
#include "assets/Assets.hpp"
#include "maths/UVRegion.hpp"
#include "window/Window.hpp"
#include "window/Camera.hpp"
#include "engine/Engine.hpp"
MenuScreen::MenuScreen(Engine& engine) : Screen(engine) {
engine.resetContent();
engine.getContentControl().resetContent();
auto menu = engine.getGUI()->getMenu();
auto menu = engine.getGUI().getMenu();
menu->reset();
menu->setPage("main");
uicamera = std::make_unique<Camera>(glm::vec3(), Window::height);
uicamera =
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
uicamera->perspective = false;
uicamera->flipped = true;
}
MenuScreen::~MenuScreen() {
}
MenuScreen::~MenuScreen() = default;
void MenuScreen::update(float delta) {
}
@ -31,24 +33,26 @@ void MenuScreen::update(float delta) {
void MenuScreen::draw(float delta) {
auto assets = engine.getAssets();
Window::clear();
Window::setBgColor(glm::vec3(0.2f));
display::clear();
display::setBgColor(glm::vec3(0.2f));
uicamera->setFov(Window::height);
const auto& size = engine.getWindow().getSize();
uint width = size.x;
uint height = size.y;
uicamera->setFov(height);
uicamera->setAspectRatio(width / static_cast<float>(height));
auto uishader = assets->get<Shader>("ui");
uishader->use();
uishader->uniformMatrix("u_projview", uicamera->getProjView());
uint width = Window::width;
uint height = Window::height;
auto bg = assets->get<Texture>("gui/menubg");
batch->begin();
batch->texture(bg);
batch->rect(
0, 0,
width, height, 0, 0, 0,
UVRegion(0, 0, width/bg->getWidth(), height/bg->getHeight()),
UVRegion(0, 0, width / bg->getWidth(), height / bg->getHeight()),
false, false, glm::vec4(1.0f)
);
batch->flush();

View File

@ -25,10 +25,11 @@ static void set_blend_mode(BlendMode mode) {
DrawContext::DrawContext(
const DrawContext* parent,
Viewport viewport,
Window& window,
Batch2D* g2d
) : parent(parent),
viewport(std::move(viewport)),
) : window(window),
parent(parent),
viewport(window.getSize()),
g2d(g2d),
flushable(g2d)
{}
@ -39,7 +40,7 @@ DrawContext::~DrawContext() {
}
while (scissorsCount--) {
Window::popScissor();
window.popScissor();
}
if (parent == nullptr)
@ -54,11 +55,7 @@ DrawContext::~DrawContext() {
}
}
Window::viewport(
0, 0,
parent->viewport.getWidth(),
parent->viewport.getHeight()
);
glViewport(0, 0, parent->viewport.x, parent->viewport.y);
if (depthMask != parent->depthMask) {
glDepthMask(parent->depthMask);
@ -79,7 +76,7 @@ DrawContext::~DrawContext() {
}
}
const Viewport& DrawContext::getViewport() const {
const glm::uvec2& DrawContext::getViewport() const {
return viewport;
}
@ -98,13 +95,9 @@ DrawContext DrawContext::sub(Flushable* flushable) const {
return ctx;
}
void DrawContext::setViewport(const Viewport& viewport) {
void DrawContext::setViewport(const glm::uvec2& viewport) {
this->viewport = viewport;
Window::viewport(
0, 0,
viewport.getWidth(),
viewport.getHeight()
);
glViewport(0, 0, viewport.x, viewport.y);
}
void DrawContext::setFramebuffer(Framebuffer* fbo) {
@ -153,7 +146,7 @@ void DrawContext::setBlendMode(BlendMode mode) {
}
void DrawContext::setScissors(const glm::vec4& area) {
Window::pushScissor(area);
window.pushScissor(area);
scissorsCount++;
}

View File

@ -1,15 +1,19 @@
#pragma once
#include <glm/vec2.hpp>
#include <glm/vec4.hpp>
#include "commons.hpp"
#include "Viewport.hpp"
#include "typedefs.hpp"
class Window;
class Batch2D;
class Framebuffer;
class DrawContext {
Window& window;
const DrawContext* parent;
Viewport viewport;
glm::uvec2 viewport;
Batch2D* g2d;
Flushable* flushable = nullptr;
Framebuffer* fbo = nullptr;
@ -20,15 +24,19 @@ class DrawContext {
int scissorsCount = 0;
float lineWidth = 1.0f;
public:
DrawContext(const DrawContext* parent, Viewport viewport, Batch2D* g2d);
DrawContext(
const DrawContext* parent,
Window& window,
Batch2D* g2d
);
~DrawContext();
Batch2D* getBatch2D() const;
const Viewport& getViewport() const;
const glm::uvec2& getViewport() const;
DrawContext sub(Flushable* flushable=nullptr) const;
void setViewport(const Viewport& viewport);
void setViewport(const glm::uvec2& viewport);
void setFramebuffer(Framebuffer* fbo);
void setDepthMask(bool flag);
void setDepthTest(bool flag);

View File

@ -3,7 +3,6 @@
#include "Shader.hpp"
#include "Texture.hpp"
#include "Framebuffer.hpp"
#include "Viewport.hpp"
#include "DrawContext.hpp"
#include <stdexcept>
@ -23,9 +22,9 @@ PostProcessing::~PostProcessing() = default;
void PostProcessing::use(DrawContext& context) {
const auto& vp = context.getViewport();
if (fbo) {
fbo->resize(vp.getWidth(), vp.getHeight());
fbo->resize(vp.x, vp.y);
} else {
fbo = std::make_unique<Framebuffer>(vp.getWidth(), vp.getHeight());
fbo = std::make_unique<Framebuffer>(vp.x, vp.y);
}
context.setFramebuffer(fbo.get());
}
@ -37,7 +36,7 @@ void PostProcessing::render(const DrawContext& context, Shader* screenShader) {
const auto& viewport = context.getViewport();
screenShader->use();
screenShader->uniform2i("u_screenSize", viewport.size());
screenShader->uniform2i("u_screenSize", viewport);
fbo->getTexture()->bind();
quadMesh->draw();
}

View File

@ -1,13 +0,0 @@
#include "Viewport.hpp"
Viewport::Viewport(uint width, uint height)
: width(width), height(height) {
}
uint Viewport::getWidth() const {
return width;
}
uint Viewport::getHeight() const {
return height;
}

View File

@ -1,19 +0,0 @@
#pragma once
#include <glm/glm.hpp>
#include "typedefs.hpp"
class Viewport {
uint width;
uint height;
public:
Viewport(uint width, uint height);
virtual uint getWidth() const;
virtual uint getHeight() const;
glm::ivec2 size() const {
return glm::ivec2(width, height);
}
};

View File

@ -11,7 +11,6 @@
#include "objects/Player.hpp"
#include "voxels/Block.hpp"
#include "voxels/Chunks.hpp"
#include "window/Window.hpp"
#include "world/Level.hpp"
BlockWrapsRenderer::BlockWrapsRenderer(

View File

@ -13,7 +13,7 @@
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/Texture.hpp"
#include "graphics/core/Viewport.hpp"
#include "graphics/commons/Model.hpp"
#include <glm/ext.hpp>
@ -25,7 +25,7 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
const Block& def,
int size
){
Window::clear();
display::clear();
blockid_t id = def.rt.id;
const UVRegion texfaces[6]{cache.getRegion(id, 0), cache.getRegion(id, 1),
cache.getRegion(id, 2), cache.getRegion(id, 3),
@ -97,6 +97,7 @@ std::unique_ptr<ImageData> BlocksPreview::draw(
}
std::unique_ptr<Atlas> BlocksPreview::build(
Window& window,
const ContentGfxCache& cache,
const Assets& assets,
const ContentIndices& indices
@ -107,8 +108,7 @@ std::unique_ptr<Atlas> BlocksPreview::build(
auto& shader = assets.require<Shader>("ui3d");
const auto& atlas = assets.require<Atlas>("blocks");
Viewport viewport(iconSize, iconSize);
DrawContext pctx(nullptr, viewport, nullptr);
DrawContext pctx(nullptr, window, nullptr);
DrawContext ctx = pctx.sub();
ctx.setCullFace(true);
ctx.setDepthTest(true);
@ -126,8 +126,8 @@ std::unique_ptr<Atlas> BlocksPreview::build(
glm::vec3(0, 1, 0)));
AtlasBuilder builder;
Window::viewport(0, 0, iconSize, iconSize);
Window::setBgColor(glm::vec4(0.0f));
ctx.setViewport({iconSize, iconSize});
display::setBgColor(glm::vec4(0.0f));
fbo.bind();
for (size_t i = 0; i < count; i++) {
@ -136,7 +136,5 @@ std::unique_ptr<Atlas> BlocksPreview::build(
builder.add(def.name, draw(cache, shader, fbo, batch, def, iconSize));
}
fbo.unbind();
Window::viewport(0, 0, Window::width, Window::height);
return builder.build(2);
}

View File

@ -13,6 +13,7 @@ class Batch3D;
class Block;
class ContentIndices;
class Shader;
class Window;
class ContentGfxCache;
class BlocksPreview {
@ -26,6 +27,7 @@ class BlocksPreview {
);
public:
static std::unique_ptr<Atlas> build(
Window& window,
const ContentGfxCache& cache,
const Assets& assets,
const ContentIndices& indices

View File

@ -4,6 +4,7 @@
#include "WorldRenderer.hpp"
#include "TextsRenderer.hpp"
#include "TextNote.hpp"
#include "assets/Assets.hpp"
#include "assets/assets_util.hpp"
#include "content/Content.hpp"
#include "voxels/Chunks.hpp"
@ -60,7 +61,7 @@ Decorator::Decorator(
player->getPosition()
));
}
playerNamePreset.deserialize(engine.getResPaths()->readCombinedObject(
playerNamePreset.deserialize(engine.getResPaths().readCombinedObject(
"presets/text3d/player_name.toml"
));
}

View File

@ -64,8 +64,6 @@ void GuidesRenderer::renderDebugLines(
) {
DrawContext ctx = pctx.sub(&batch);
const auto& viewport = ctx.getViewport();
uint displayWidth = viewport.getWidth();
uint displayHeight = viewport.getHeight();
ctx.setDepthTest(true);
@ -91,15 +89,15 @@ void GuidesRenderer::renderDebugLines(
}
float length = 40.f;
glm::vec3 tsl(displayWidth / 2, displayHeight / 2, 0.f);
glm::vec3 tsl(viewport.x / 2, viewport.y / 2, 0.f);
glm::mat4 model(glm::translate(glm::mat4(1.f), tsl));
linesShader.uniformMatrix(
"u_projview",
glm::ortho(
0.f,
static_cast<float>(displayWidth),
static_cast<float>(viewport.x),
0.f,
static_cast<float>(displayHeight),
static_cast<float>(viewport.y),
-length,
length
) * model *

View File

@ -6,7 +6,6 @@
#include "graphics/core/Atlas.hpp"
#include "graphics/core/Texture.hpp"
#include "assets/Assets.hpp"
#include "window/Window.hpp"
#include "voxels/Chunks.hpp"
#include "lighting/Lightmap.hpp"
#include "settings.hpp"

View File

@ -48,6 +48,30 @@ static inline UVRegion get_region_for(
return texreg.region;
}
void ModelsGenerator::prepare(Content& content, Assets& assets) {
for (auto& [name, def] : content.blocks.getDefs()) {
if (def->model == BlockModel::custom && def->modelName.empty()) {
assets.store(
std::make_unique<model::Model>(
loadCustomBlockModel(
def->customModelRaw, assets, !def->shadeless
)
),
name + ".model"
);
def->modelName = def->name + ".model";
}
}
for (auto& [name, def] : content.items.getDefs()) {
assets.store(
std::make_unique<model::Model>(
generate(*def, content, assets)
),
name + ".model"
);
}
}
model::Model ModelsGenerator::fromCustom(
const Assets& assets,
const std::vector<BoxModel>& modelBoxes,

View File

@ -11,6 +11,8 @@ class Block;
class ModelsGenerator {
public:
static void prepare(Content& content, Assets& assets);
static model::Model generate(
const ItemDef& def, const Content& content, const Assets& assets
);

View File

@ -101,11 +101,9 @@ void Skybox::draw(
float daytime,
float fog)
{
const Viewport& viewport = pctx.getViewport();
int width = viewport.getWidth();
int height = viewport.getHeight();
const glm::uvec2& viewport = pctx.getViewport();
drawBackground(camera, assets, width, height);
drawBackground(camera, assets, viewport.x, viewport.y);
DrawContext ctx = pctx.sub();
ctx.setBlendMode(BlendMode::addition);
@ -145,7 +143,7 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality)
ctx.setDepthMask(false);
ctx.setDepthTest(false);
ctx.setFramebuffer(fbo.get());
ctx.setViewport(Viewport(size, size));
ctx.setViewport({size, size});
auto cubemap = dynamic_cast<Cubemap*>(fbo->getTexture());
assert(cubemap != nullptr);

View File

@ -4,11 +4,11 @@
#include "maths/util.hpp"
#include "assets/Assets.hpp"
#include "window/Camera.hpp"
#include "window/Window.hpp"
#include "maths/FrustumCulling.hpp"
#include "graphics/core/Font.hpp"
#include "graphics/core/Batch3D.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/DrawContext.hpp"
#include "presets/NotePreset.hpp"
#include "constants.hpp"
@ -66,6 +66,7 @@ void TextsRenderer::renderNote(
xvec *= 1.0f + scale;
yvec *= 1.0f + scale;
}
const auto& viewport = context.getViewport();
if (preset.displayMode == NoteDisplayMode::PROJECTED) {
float scale = 1.0f;
if (glm::abs(preset.perspective) > 0.0001f) {
@ -84,14 +85,14 @@ void TextsRenderer::renderNote(
}
pos /= projpos.w;
pos.z = 0;
xvec = {2.0f/Window::width*scale, 0, 0};
yvec = {0, 2.0f/Window::height*scale, 0};
xvec = {2.0f / viewport.x * scale, 0, 0};
yvec = {0, 2.0f / viewport.y * scale, 0};
} else {
auto matrix = camera.getProjView();
auto screenPos = matrix * glm::vec4(pos, 1.0f);
xvec = glm::vec3(2.0f/Window::width*scale, 0, 0);
yvec = glm::vec3(0, 2.0f/Window::height*scale, 0);
xvec = glm::vec3(2.0f / viewport.x * scale, 0, 0);
yvec = glm::vec3(0, 2.0f / viewport.y * scale, 0);
pos = screenPos / screenPos.w;
}

View File

@ -316,7 +316,7 @@ void WorldRenderer::renderHands(
assets.get<model::Model>(def.modelName),
nullptr
);
Window::clearDepth();
display::clearDepth();
setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);
skybox->bind();
modelBatch->render();
@ -338,8 +338,8 @@ void WorldRenderer::draw(
auto world = level.getWorld();
const Viewport& vp = pctx.getViewport();
camera.aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
const auto& vp = pctx.getViewport();
camera.setAspectRatio(vp.x / static_cast<float>(vp.y));
const auto& settings = engine.getSettings();
const auto& worldInfo = world->getInfo();
@ -359,7 +359,7 @@ void WorldRenderer::draw(
DrawContext wctx = pctx.sub();
postProcessing.use(wctx);
Window::clearDepth();
display::clearDepth();
// Drawing background sky plane
skybox->draw(pctx, camera, assets, worldInfo.daytime, clouds);

View File

@ -1,13 +1,15 @@
#include "GUI.hpp"
#include "gui_util.hpp"
#include <algorithm>
#include <iostream>
#include <utility>
#include "elements/UINode.hpp"
#include "assets/Assets.hpp"
#include "elements/Label.hpp"
#include "elements/Menu.hpp"
#include "elements/Panel.hpp"
#include "assets/Assets.hpp"
#include "elements/UINode.hpp"
#include "engine/Engine.hpp"
#include "frontend/UiDocument.hpp"
#include "frontend/locale.hpp"
#include "graphics/core/Batch2D.hpp"
@ -15,31 +17,36 @@
#include "graphics/core/Shader.hpp"
#include "graphics/core/Font.hpp"
#include "graphics/core/DrawContext.hpp"
#include "window/Events.hpp"
#include "graphics/core/Shader.hpp"
#include "gui_util.hpp"
#include "window/Camera.hpp"
#include "window/Window.hpp"
#include "window/input.hpp"
#include "window/Camera.hpp"
#include <algorithm>
#include <utility>
using namespace gui;
GUI::GUI()
: batch2D(std::make_unique<Batch2D>(1024)),
container(std::make_shared<Container>(glm::vec2(1000))) {
GUI::GUI(Engine& engine)
: engine(engine),
input(engine.getInput()),
batch2D(std::make_unique<Batch2D>(1024)),
container(std::make_shared<Container>(*this, glm::vec2(1000))) {
container->setId("root");
uicamera = std::make_unique<Camera>(glm::vec3(), Window::height);
uicamera =
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
uicamera->perspective = false;
uicamera->flipped = true;
menu = std::make_shared<Menu>();
menu = std::make_shared<Menu>(*this);
menu->setId("menu");
menu->setZIndex(10);
container->add(menu);
container->setScrollable(false);
tooltip = guiutil::create(
*this,
"<container color='#000000A0' interactive='false' z-index='999'>"
"<label id='tooltip.label' pos='2' autoresize='true' multiline='true' text-wrap='false'></label>"
"</container>"
@ -65,12 +72,15 @@ std::shared_ptr<Menu> GUI::getMenu() {
}
void GUI::onAssetsLoad(Assets* assets) {
assets->store(std::make_unique<UiDocument>(
"core:root",
uidocscript {},
std::dynamic_pointer_cast<gui::UINode>(container),
nullptr
), "core:root");
assets->store(
std::make_unique<UiDocument>(
"core:root",
uidocscript {},
std::dynamic_pointer_cast<gui::UINode>(container),
nullptr
),
"core:root"
);
}
void GUI::resetTooltip() {
@ -79,11 +89,13 @@ void GUI::resetTooltip() {
}
void GUI::updateTooltip(float delta) {
if (hover == nullptr || !hover->isInside(Events::cursor)) {
const auto& cursor = input.getCursor();
if (hover == nullptr || !hover->isInside(cursor.pos)) {
return resetTooltip();
}
if (tooltipTimer + delta >= hover->getTooltipDelay()) {
auto label = std::dynamic_pointer_cast<gui::Label>(get("tooltip.label"));
auto label =
std::dynamic_pointer_cast<gui::Label>(get("tooltip.label"));
const auto& text = hover->getTooltip();
if (text.empty() && tooltip->isVisible()) {
return resetTooltip();
@ -91,11 +103,11 @@ void GUI::updateTooltip(float delta) {
if (label && !text.empty()) {
tooltip->setVisible(true);
label->setText(langs::get(text));
auto size = label->getSize()+glm::vec2(4.0f);
auto pos = Events::cursor+glm::vec2(10.0f);
auto size = label->getSize() + glm::vec2(4.0f);
auto pos = cursor.pos + glm::vec2(10.0f);
auto rootSize = container->getSize();
pos.x = glm::min(pos.x, rootSize.x-size.x);
pos.y = glm::min(pos.y, rootSize.y-size.y);
pos.x = glm::min(pos.x, rootSize.x - size.x);
pos.y = glm::min(pos.y, rootSize.y - size.y);
tooltip->setSize(size);
tooltip->setPos(pos);
}
@ -103,32 +115,34 @@ void GUI::updateTooltip(float delta) {
tooltipTimer += delta;
}
/// @brief Mouse related input and logic handling
void GUI::actMouse(float delta) {
float mouseDelta = glm::length(Events::delta);
/// @brief Mouse related input and logic handling
void GUI::actMouse(float delta, const CursorState& cursor) {
float mouseDelta = glm::length(cursor.delta);
doubleClicked = false;
doubleClickTimer += delta + mouseDelta * 0.1f;
auto hover = container->getAt(Events::cursor);
auto hover = container->getAt(cursor.pos);
if (this->hover && this->hover != hover) {
this->hover->setHover(false);
}
if (hover) {
hover->setHover(true);
if (Events::scroll) {
hover->scrolled(Events::scroll);
int scroll = input.getScroll();
if (scroll) {
hover->scrolled(scroll);
}
}
this->hover = hover;
if (Events::jclicked(mousecode::BUTTON_1)) {
if (input.jclicked(Mousecode::BUTTON_1)) {
if (pressed == nullptr && this->hover) {
pressed = hover;
if (doubleClickTimer < doubleClickDelay) {
pressed->doubleClick(this, Events::cursor.x, Events::cursor.y);
pressed->doubleClick(cursor.pos.x, cursor.pos.y);
doubleClicked = true;
} else {
pressed->click(this, Events::cursor.x, Events::cursor.y);
pressed->click(cursor.pos.x, cursor.pos.y);
}
doubleClickTimer = 0.0f;
if (focus && focus != pressed) {
@ -136,7 +150,7 @@ void GUI::actMouse(float delta) {
}
if (focus != pressed) {
focus = pressed;
focus->onFocus(this);
focus->onFocus();
return;
}
}
@ -144,59 +158,61 @@ void GUI::actMouse(float delta) {
focus->defocus();
focus = nullptr;
}
} else if (!Events::clicked(mousecode::BUTTON_1) && pressed) {
pressed->mouseRelease(this, Events::cursor.x, Events::cursor.y);
} else if (!input.clicked(Mousecode::BUTTON_1) && pressed) {
pressed->mouseRelease(cursor.pos.x, cursor.pos.y);
pressed = nullptr;
}
if (hover) {
for (mousecode code : MOUSECODES_ALL) {
if (Events::jclicked(code)) {
hover->clicked(this, code);
}
}
}
}
void GUI::actFocused() {
if (Events::jpressed(keycode::ESCAPE)) {
focus->defocus();
focus = nullptr;
return;
}
for (auto codepoint : Events::codepoints) {
focus->typed(codepoint);
}
for (auto key : Events::pressedKeys) {
focus->keyPressed(key);
}
if (!Events::isCursorLocked()) {
if (Events::clicked(mousecode::BUTTON_1) &&
(Events::jclicked(mousecode::BUTTON_1) || Events::delta.x || Events::delta.y))
{
if (!doubleClicked) {
focus->mouseMove(this, Events::cursor.x, Events::cursor.y);
for (Mousecode code : MOUSECODES_ALL) {
if (input.jclicked(code)) {
hover->clicked(code);
}
}
}
}
void GUI::act(float delta, const Viewport& vp) {
container->setSize(vp.size());
void GUI::actFocused() {
if (input.jpressed(Keycode::ESCAPE)) {
focus->defocus();
focus = nullptr;
return;
}
for (auto codepoint : input.getCodepoints()) {
focus->typed(codepoint);
}
for (auto key : input.getPressedKeys()) {
focus->keyPressed(key);
}
const auto& cursor = input.getCursor();
if (!cursor.locked) {
if (input.clicked(Mousecode::BUTTON_1) &&
(input.jclicked(Mousecode::BUTTON_1) || cursor.delta.x ||
cursor.delta.y)) {
if (!doubleClicked) {
focus->mouseMove(cursor.pos.x, cursor.pos.y);
}
}
}
}
void GUI::act(float delta, const glm::uvec2& vp) {
container->setSize(vp);
container->act(delta);
auto prevfocus = focus;
updateTooltip(delta);
if (!Events::isCursorLocked()) {
actMouse(delta);
const auto& cursor = input.getCursor();
if (!cursor.locked) {
actMouse(delta, cursor);
} else {
if (hover) {
hover->setHover(false);
hover = nullptr;
}
}
if (focus) {
actFocused();
}
@ -217,7 +233,6 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
auto ctx = pctx.sub(batch2D.get());
auto& viewport = ctx.getViewport();
glm::vec2 wsize = viewport.size();
auto& page = menu->getCurrent();
if (page.panel) {
@ -227,8 +242,9 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
panel->cropToContent();
}
}
menu->setPos((wsize - menu->getSize()) / 2.0f);
uicamera->setFov(wsize.y);
menu->setPos((glm::vec2(viewport) - menu->getSize()) / 2.0f);
uicamera->setFov(viewport.y);
uicamera->setAspectRatio(viewport.x / static_cast<float>(viewport.y));
auto uishader = assets.get<Shader>("ui");
uishader->use();
@ -238,7 +254,7 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
container->draw(ctx, assets);
if (hover) {
Window::setCursor(hover->getCursor());
engine.getWindow().setCursor(hover->getCursor());
}
if (hover && debug) {
auto pos = hover->calcPos();
@ -260,11 +276,11 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
batch2D->untexture();
auto node = hover->getParent();
while (node) {
auto pos = node->calcPos();
auto parentPos = node->calcPos();
auto size = node->getSize();
batch2D->setColor(0, 255, 255);
batch2D->lineRect(pos.x, pos.y, size.x-1, size.y-1);
batch2D->lineRect(parentPos.x, parentPos.y, size.x-1, size.y-1);
node = node->getParent();
}
@ -313,7 +329,7 @@ void GUI::setFocus(std::shared_ptr<UINode> node) {
}
focus = std::move(node);
if (focus) {
focus->onFocus(this);
focus->onFocus();
}
}
@ -336,3 +352,15 @@ float GUI::getDoubleClickDelay() const {
void GUI::toggleDebug() {
debug = !debug;
}
const Input& GUI::getInput() const {
return engine.getInput();
}
Input& GUI::getInput() {
return engine.getInput();
}
Window& GUI::getWindow() {
return engine.getWindow();
}

View File

@ -9,12 +9,14 @@
#include <glm/glm.hpp>
#include <unordered_map>
class Viewport;
class DrawContext;
class Assets;
class Camera;
class Batch2D;
class LineBatch;
struct CursorState;
class Engine;
class Input;
class Window;
/*
Some info about padding and margin.
@ -56,6 +58,8 @@ namespace gui {
/// @brief The main UI controller
class GUI {
Engine& engine;
Input& input;
std::unique_ptr<Batch2D> batch2D;
std::shared_ptr<Container> container;
std::shared_ptr<UINode> hover;
@ -76,12 +80,12 @@ namespace gui {
bool doubleClicked = false;
bool debug = false;
void actMouse(float delta);
void actMouse(float delta, const CursorState& cursor);
void actFocused();
void updateTooltip(float delta);
void resetTooltip();
public:
GUI();
GUI(Engine& engine);
~GUI();
void setPageLoader(PageLoaderFunc pageLoader);
@ -101,7 +105,7 @@ namespace gui {
/// @brief Main input handling and logic update method
/// @param delta delta time
/// @param viewport window size
void act(float delta, const Viewport& viewport);
void act(float delta, const glm::uvec2& viewport);
/// @brief Draw all visible elements on main container
/// @param pctx parent graphics context
@ -152,5 +156,8 @@ namespace gui {
float getDoubleClickDelay() const;
void toggleDebug();
const Input& getInput() const;
Input& getInput();
Window& getWindow();
};
}

View File

@ -26,12 +26,13 @@ namespace gui {
}
protected:
BasePanel(
GUI& gui,
glm::vec2 size,
glm::vec4 padding = glm::vec4(0.0f),
float interval = 2.0f,
Orientation orientation = Orientation::vertical
)
: Container(std::move(size)),
: Container(gui, std::move(size)),
padding(std::move(padding)),
interval(interval) {
}

View File

@ -3,17 +3,23 @@
#include <utility>
#include "Label.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp"
using namespace gui;
Button::Button(const std::shared_ptr<UINode>& content, glm::vec4 padding)
: Panel(glm::vec2(), padding, 0) {
Button::Button(
GUI& gui, const std::shared_ptr<UINode>& content, glm::vec4 padding
)
: Panel(gui, glm::vec2(), padding, 0) {
glm::vec4 margin = getMargin();
setSize(content->getSize()+
glm::vec2(padding[0]+padding[2]+margin[0]+margin[2],
padding[1]+padding[3]+margin[1]+margin[3]));
setSize(
content->getSize() +
glm::vec2(
padding[0] + padding[2] + margin[0] + margin[2],
padding[1] + padding[3] + margin[1] + margin[3]
)
);
add(content);
setScrollable(false);
setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f));
@ -22,15 +28,16 @@ Button::Button(const std::shared_ptr<UINode>& content, glm::vec4 padding)
}
Button::Button(
GUI& gui,
const std::wstring& text,
glm::vec4 padding,
glm::vec4 padding,
const onaction& action,
glm::vec2 size
) : Panel(size, padding, 0)
{
)
: Panel(gui, size, padding, 0) {
if (size.y < 0.0f) {
size = glm::vec2(
glm::max(padding.x + padding.z + text.length()*8, size.x),
glm::max(padding.x + padding.z + text.length() * 8, size.x),
glm::max(padding.y + padding.w + 16, size.y)
);
}
@ -41,9 +48,11 @@ Button::Button(
}
setScrollable(false);
label = std::make_shared<Label>(text);
label = std::make_shared<Label>(gui, text);
label->setAlign(Align::center);
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
label->setSize(
size - glm::vec2(padding.z + padding.x, padding.w + padding.y)
);
label->setInteractive(false);
add(label);
setHoverColor(glm::vec4(0.05f, 0.1f, 0.15f, 0.75f));
@ -73,7 +82,9 @@ Button* Button::textSupplier(wstringsupplier supplier) {
void Button::refresh() {
Panel::refresh();
if (label) {
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
label->setSize(
size - glm::vec2(padding.z + padding.x, padding.w + padding.y)
);
}
}

View File

@ -9,13 +9,19 @@ namespace gui {
protected:
std::shared_ptr<Label> label;
public:
Button(const std::shared_ptr<UINode>& content,
glm::vec4 padding=glm::vec4(2.0f));
Button(const std::wstring& text,
glm::vec4 padding,
const onaction& action,
glm::vec2 size=glm::vec2(-1));
Button(
GUI& gui,
const std::shared_ptr<UINode>& content,
glm::vec4 padding = glm::vec4(2.0f)
);
Button(
GUI& gui,
const std::wstring& text,
glm::vec4 padding,
const onaction& action,
glm::vec2 size = glm::vec2(-1)
);
virtual void drawBackground(
const DrawContext& pctx, const Assets& assets

View File

@ -4,7 +4,8 @@
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Texture.hpp"
gui::Canvas::Canvas(ImageFormat inFormat, glm::uvec2 inSize) : UINode(inSize) {
gui::Canvas::Canvas(GUI& gui, ImageFormat inFormat, glm::uvec2 inSize)
: UINode(gui, inSize) {
auto data = std::make_shared<ImageData>(inFormat, inSize.x, inSize.y);
mTexture = Texture::from(data.get());
mData = std::move(data);

View File

@ -9,7 +9,7 @@ class Texture;
namespace gui {
class Canvas final : public UINode {
public:
explicit Canvas(ImageFormat inFormat, glm::uvec2 inSize);
explicit Canvas(GUI& gui, ImageFormat inFormat, glm::uvec2 inSize);
~Canvas() override = default;

View File

@ -2,13 +2,14 @@
#include <utility>
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Batch2D.hpp"
#include "Label.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp"
using namespace gui;
CheckBox::CheckBox(bool checked) : UINode(glm::vec2(32.0f)), checked(checked) {
CheckBox::CheckBox(GUI& gui, bool checked)
: UINode(gui, glm::vec2(32.0f)), checked(checked) {
setColor({0.0f, 0.0f, 0.0f, 0.5f});
setHoverColor({0.05f, 0.1f, 0.2f, 0.75f});
}
@ -24,7 +25,7 @@ void CheckBox::draw(const DrawContext& pctx, const Assets&) {
batch->rect(pos.x, pos.y, size.x, size.y);
}
void CheckBox::mouseRelease(GUI*, int, int) {
void CheckBox::mouseRelease(int, int) {
checked = !checked;
if (consumer) {
consumer(checked);
@ -44,15 +45,17 @@ CheckBox* CheckBox::setChecked(bool flag) {
return this;
}
FullCheckBox::FullCheckBox(const std::wstring& text, glm::vec2 size, bool checked)
: Panel(size),
checkbox(std::make_shared<CheckBox>(checked)),
label(std::make_shared<Label>(text)) {
FullCheckBox::FullCheckBox(
GUI& gui, const std::wstring& text, glm::vec2 size, bool checked
)
: Panel(gui, size),
checkbox(std::make_shared<CheckBox>(gui, checked)),
label(std::make_shared<Label>(gui, text)) {
setColor(glm::vec4(0.0f));
setOrientation(Orientation::horizontal);
add(checkbox);
label->setMargin(glm::vec4(5.f, 5.f, 0.f, 0.f));
add(label);
}

View File

@ -2,8 +2,8 @@
#include <utility>
#include "Panel.hpp"
#include "Label.hpp"
#include "Panel.hpp"
namespace gui {
class CheckBox : public UINode {
@ -13,11 +13,12 @@ namespace gui {
boolconsumer consumer = nullptr;
bool checked = false;
public:
CheckBox(bool checked=false);
explicit CheckBox(GUI& gui, bool checked = false);
virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void draw(const DrawContext& pctx, const Assets& assets)
override;
virtual void mouseRelease(GUI*, int x, int y) override;
virtual void mouseRelease(int x, int y) override;
virtual void setSupplier(boolsupplier supplier);
virtual void setConsumer(boolconsumer consumer);
@ -25,8 +26,7 @@ namespace gui {
virtual CheckBox* setChecked(bool flag);
virtual bool isChecked() const {
if (supplier)
return supplier();
if (supplier) return supplier();
return checked;
}
};
@ -36,7 +36,12 @@ namespace gui {
std::shared_ptr<CheckBox> checkbox;
std::shared_ptr<Label> label;
public:
FullCheckBox(const std::wstring& text, glm::vec2 size, bool checked=false);
explicit FullCheckBox(
GUI& gui,
const std::wstring& text,
glm::vec2 size,
bool checked = false
);
virtual void setSupplier(boolsupplier supplier) {
checkbox->setSupplier(std::move(supplier));

View File

@ -8,7 +8,7 @@
using namespace gui;
Container::Container(glm::vec2 size) : UINode(size) {
Container::Container(GUI& gui, glm::vec2 size) : UINode(gui, size) {
actualLength = size.y;
setColor(glm::vec4());
}
@ -41,8 +41,8 @@ std::shared_ptr<UINode> Container::getAt(const glm::vec2& pos) {
return UINode::getAt(pos);
}
void Container::mouseMove(GUI* gui, int x, int y) {
UINode::mouseMove(gui, x, y);
void Container::mouseMove(int x, int y) {
UINode::mouseMove(x, y);
if (!scrollable) {
return;
}
@ -65,8 +65,8 @@ void Container::mouseMove(GUI* gui, int x, int y) {
prevScrollY = y;
}
void Container::mouseRelease(GUI* gui, int x, int y) {
UINode::mouseRelease(gui, x, y);
void Container::mouseRelease(int x, int y) {
UINode::mouseRelease(x, y);
prevScrollY = -1;
}

View File

@ -7,7 +7,7 @@
#include <vector>
namespace gui {
class Container : public UINode, public util::ObjectsKeeper {
class Container : public UINode, public ::util::ObjectsKeeper {
int prevScrollY = -1;
protected:
std::vector<std::shared_ptr<UINode>> nodes;
@ -22,7 +22,7 @@ namespace gui {
return prevScrollY != -1;
}
public:
Container(glm::vec2 size);
Container(GUI& gui, glm::vec2 size);
virtual ~Container();
virtual void act(float delta) override;
@ -44,8 +44,8 @@ namespace gui {
virtual void refresh() override;
void setScroll(int scroll);
virtual void mouseMove(GUI*, int x, int y) override;
virtual void mouseRelease(GUI*, int x, int y) override;
virtual void mouseMove(int x, int y) override;
virtual void mouseRelease(int x, int y) override;
const std::vector<std::shared_ptr<UINode>>& getNodes() const;
};

View File

@ -2,23 +2,24 @@
#include <utility>
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/Texture.hpp"
#include "graphics/core/Atlas.hpp"
#include "assets/Assets.hpp"
#include "graphics/core/Atlas.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Texture.hpp"
#include "maths/UVRegion.hpp"
using namespace gui;
Image::Image(std::string texture, glm::vec2 size) : UINode(size), texture(std::move(texture)) {
Image::Image(GUI& gui, std::string texture, glm::vec2 size)
: UINode(gui, size), texture(std::move(texture)) {
setInteractive(false);
}
void Image::draw(const DrawContext& pctx, const Assets& assets) {
glm::vec2 pos = calcPos();
auto batch = pctx.getBatch2D();
Texture* texture = nullptr;
auto separator = this->texture.find(':');
if (separator == std::string::npos) {
@ -30,14 +31,16 @@ void Image::draw(const DrawContext& pctx, const Assets& assets) {
} else {
auto atlasName = this->texture.substr(0, separator);
if (auto atlas = assets.get<Atlas>(atlasName)) {
if (auto region = atlas->getIf(this->texture.substr(separator+1))) {
if (auto region =
atlas->getIf(this->texture.substr(separator + 1))) {
texture = atlas->getTexture();
batch->texture(atlas->getTexture());
batch->setRegion(*region);
if (autoresize) {
setSize(glm::vec2(
texture->getWidth()*region->getWidth(),
texture->getHeight()*region->getHeight()));
texture->getWidth() * region->getWidth(),
texture->getHeight() * region->getHeight()
));
}
} else {
batch->texture(nullptr);
@ -45,8 +48,17 @@ void Image::draw(const DrawContext& pctx, const Assets& assets) {
}
}
batch->rect(
pos.x, pos.y, size.x, size.y,
0, 0, 0, UVRegion(), false, true, calcColor()
pos.x,
pos.y,
size.x,
size.y,
0,
0,
0,
UVRegion(),
false,
true,
calcColor()
);
}

View File

@ -8,7 +8,7 @@ namespace gui {
std::string texture;
bool autoresize = false;
public:
Image(std::string texture, glm::vec2 size=glm::vec2(32,32));
Image(GUI& gui, std::string texture, glm::vec2 size=glm::vec2(32,32));
virtual void draw(const DrawContext& pctx, const Assets& assets) override;

View File

@ -7,10 +7,10 @@
using namespace gui;
InputBindBox::InputBindBox(Binding& binding, glm::vec4 padding)
: Panel(glm::vec2(100,32), padding, 0),
InputBindBox::InputBindBox(GUI& gui, Binding& binding, glm::vec4 padding)
: Panel(gui, glm::vec2(100,32), padding, 0),
binding(binding),
label(std::make_shared<Label>(L"")) {
label(std::make_shared<Label>(gui, L"")) {
add(label);
setScrollable(false);
hoverColor = glm::vec4(0.05f, 0.1f, 0.2f, 0.75f);
@ -25,15 +25,15 @@ void InputBindBox::drawBackground(const DrawContext& pctx, const Assets&) {
label->setText(util::str2wstr_utf8(binding.text()));
}
void InputBindBox::clicked(GUI*, mousecode button) {
void InputBindBox::clicked(Mousecode button) {
if (isFocused()) {
binding.reset(button);
defocus();
}
}
void InputBindBox::keyPressed(keycode key) {
if (key != keycode::ESCAPE) {
void InputBindBox::keyPressed(Keycode key) {
if (key != Keycode::ESCAPE) {
binding.reset(key);
}
defocus();

View File

@ -4,21 +4,25 @@
namespace gui {
class Label;
class InputBindBox : public Panel {
protected:
Binding& binding;
glm::vec4 focusedColor {0.1f, 0.15f, 0.35f, 0.75f};
std::shared_ptr<Label> label;
public:
InputBindBox(Binding& binding, glm::vec4 padding=glm::vec4(6.0f));
explicit InputBindBox(
GUI& gui, Binding& binding, glm::vec4 padding = glm::vec4(6.0f)
);
virtual void drawBackground(
const DrawContext& pctx, const Assets& assets
) override;
virtual void clicked(GUI*, mousecode button) override;
virtual void keyPressed(keycode key) override;
virtual bool isFocuskeeper() const override {return true;}
virtual void clicked(Mousecode button) override;
virtual void keyPressed(Keycode key) override;
virtual bool isFocuskeeper() const override {
return true;
}
};
}

View File

@ -13,7 +13,6 @@
#include "objects/Player.hpp"
#include "util/stringutil.hpp"
#include "voxels/Block.hpp"
#include "window/Events.hpp"
#include "window/input.hpp"
#include "world/Level.hpp"
#include "graphics/core/Atlas.hpp"
@ -46,8 +45,8 @@ SlotLayout::SlotLayout(
shareFunc(std::move(shareFunc)),
rightClick(std::move(rightClick)) {}
InventoryBuilder::InventoryBuilder() {
view = std::make_shared<InventoryView>();
InventoryBuilder::InventoryBuilder(GUI& gui) : gui(gui) {
view = std::make_shared<InventoryView>(gui);
}
void InventoryBuilder::addGrid(
@ -75,7 +74,8 @@ void InventoryBuilder::addGrid(
view->setSize(vsize);
if (addpanel) {
auto panel = std::make_shared<gui::Container>(glm::vec2(width, height));
auto panel =
std::make_shared<gui::Container>(gui, glm::vec2(width, height));
view->setColor(glm::vec4(0.122f, 0.122f, 0.122f, 0.878f));
view->add(panel, pos);
}
@ -106,8 +106,8 @@ std::shared_ptr<InventoryView> InventoryBuilder::build() {
}
SlotView::SlotView(
SlotLayout layout
) : UINode(glm::vec2(InventoryView::SLOT_SIZE)),
GUI& gui, SlotLayout layout
) : UINode(gui, glm::vec2(InventoryView::SLOT_SIZE)),
layout(std::move(layout))
{
setColor(glm::vec4(0, 0, 0, 0.2f));
@ -280,7 +280,8 @@ bool SlotView::isHighlighted() const {
}
void SlotView::performLeftClick(ItemStack& stack, ItemStack& grabbed) {
if (layout.taking && Events::pressed(keycode::LEFT_SHIFT)) {
const auto& input = gui.getInput();
if (layout.taking && input.pressed(Keycode::LEFT_SHIFT)) {
if (layout.shareFunc) {
layout.shareFunc(layout.index, stack);
}
@ -347,20 +348,20 @@ void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) {
}
}
void SlotView::clicked(gui::GUI* gui, mousecode button) {
void SlotView::clicked(Mousecode button) {
if (bound == nullptr)
return;
auto exchangeSlot =
std::dynamic_pointer_cast<SlotView>(gui->get(EXCHANGE_SLOT_NAME));
std::dynamic_pointer_cast<SlotView>(gui.get(EXCHANGE_SLOT_NAME));
if (exchangeSlot == nullptr) {
return;
}
ItemStack& grabbed = exchangeSlot->getStack();
ItemStack& stack = *bound;
if (button == mousecode::BUTTON_1) {
if (button == Mousecode::BUTTON_1) {
performLeftClick(stack, grabbed);
} else if (button == mousecode::BUTTON_2) {
} else if (button == Mousecode::BUTTON_2) {
performRightClick(stack, grabbed);
}
if (layout.updateFunc) {
@ -368,8 +369,8 @@ void SlotView::clicked(gui::GUI* gui, mousecode button) {
}
}
void SlotView::onFocus(gui::GUI* gui) {
clicked(gui, mousecode::BUTTON_1);
void SlotView::onFocus() {
clicked(Mousecode::BUTTON_1);
}
const std::wstring& SlotView::getTooltip() const {
@ -398,7 +399,7 @@ ItemStack& SlotView::getStack() {
return *bound;
}
InventoryView::InventoryView() : Container(glm::vec2()) {
InventoryView::InventoryView(GUI& gui) : Container(gui, glm::vec2()) {
setColor(glm::vec4(0, 0, 0, 0.0f));
}
@ -419,7 +420,7 @@ std::shared_ptr<SlotView> InventoryView::addSlot(const SlotLayout& layout) {
}
setSize(vsize);
auto slot = std::make_shared<SlotView>(layout);
auto slot = std::make_shared<SlotView>(gui, layout);
if (!layout.background) {
slot->setColor(glm::vec4());
}

View File

@ -86,15 +86,15 @@ namespace gui {
void refreshTooltip(const ItemStack& stack, const ItemDef& item);
public:
SlotView(SlotLayout layout);
SlotView(GUI& gui, SlotLayout layout);
virtual void draw(const DrawContext& pctx, const Assets& assets) override;
void setHighlighted(bool flag);
bool isHighlighted() const;
virtual void clicked(gui::GUI*, mousecode) override;
virtual void onFocus(gui::GUI*) override;
virtual void clicked(Mousecode) override;
virtual void onFocus() override;
virtual const std::wstring& getTooltip() const override;
void bind(
@ -117,7 +117,7 @@ namespace gui {
std::vector<SlotView*> slots;
glm::vec2 origin {};
public:
InventoryView();
InventoryView(GUI& gui);
virtual ~InventoryView();
virtual void setPos(glm::vec2 pos) override;
@ -145,9 +145,10 @@ namespace gui {
};
class InventoryBuilder {
GUI& gui;
std::shared_ptr<InventoryView> view;
public:
InventoryBuilder();
InventoryBuilder(GUI& gui);
/// @brief Add slots grid to inventory view
/// @param cols grid columns

View File

@ -83,8 +83,8 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) {
}
}
Label::Label(const std::string& text, std::string fontName)
: UINode(glm::vec2(text.length() * 8, 16)),
Label::Label(GUI& gui, const std::string& text, std::string fontName)
: UINode(gui, glm::vec2(text.length() * 8, 16)),
text(util::str2wstr_utf8(text)),
fontName(std::move(fontName))
{
@ -93,8 +93,8 @@ Label::Label(const std::string& text, std::string fontName)
}
Label::Label(const std::wstring& text, std::string fontName)
: UINode(glm::vec2(text.length() * 8, 16)),
Label::Label(GUI& gui, const std::wstring& text, std::string fontName)
: UINode(gui, glm::vec2(text.length() * 8, 16)),
text(text),
fontName(std::move(fontName))
{
@ -237,8 +237,8 @@ void Label::draw(const DrawContext& pctx, const Assets& assets) {
textYOffset = pos.y-calcPos().y;
totalLineHeight = lineHeight;
auto& viewport = pctx.getViewport();
glm::vec4 bounds {0, 0, viewport.getWidth(), viewport.getHeight()};
const auto& viewport = pctx.getViewport();
glm::vec4 bounds {0, 0, viewport.x, viewport.y};
if (parent) {
auto ppos = parent->calcPos();
auto psize = parent->getSize();

View File

@ -63,8 +63,8 @@ namespace gui {
std::unique_ptr<FontStylesScheme> styles;
public:
Label(const std::string& text, std::string fontName=FONT_DEFAULT);
Label(const std::wstring& text, std::string fontName=FONT_DEFAULT);
Label(GUI& gui, const std::string& text, std::string fontName="normal");
Label(GUI& gui, const std::wstring& text, std::string fontName="normal");
virtual ~Label();

View File

@ -5,7 +5,7 @@
using namespace gui;
Menu::Menu() : Container(glm::vec2(1)){
Menu::Menu(GUI& gui) : Container(gui, glm::vec2(1)){
}
bool Menu::has(const std::string& name) {

View File

@ -21,7 +21,7 @@ namespace gui {
std::unordered_map<std::string, supplier<std::shared_ptr<UINode>>> pageSuppliers;
PageLoaderFunc pagesLoader = nullptr;
public:
Menu();
explicit Menu(GUI& gui);
/// @brief Check menu have page or page supplier
/// @param name page name

View File

@ -4,14 +4,12 @@
using namespace gui;
Panel::Panel(glm::vec2 size, glm::vec4 padding, float interval)
: BasePanel(size, padding, interval, Orientation::vertical)
{
Panel::Panel(GUI& gui, glm::vec2 size, glm::vec4 padding, float interval)
: BasePanel(gui, size, padding, interval) {
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
}
Panel::~Panel() {
}
Panel::~Panel() = default;
void Panel::setMaxLength(int value) {
maxLength = value;
@ -46,7 +44,7 @@ void Panel::fullRefresh() {
Container::fullRefresh();
}
void Panel::add(const std::shared_ptr<UINode> &node) {
void Panel::add(const std::shared_ptr<UINode>& node) {
node->setResizing(true);
Container::add(node);
fullRefresh();
@ -68,10 +66,10 @@ void Panel::refresh() {
for (auto& node : nodes) {
const glm::vec4 margin = node->getMargin();
y += margin.y;
float ex = x + margin.x;
node->setPos(glm::vec2(ex, y));
float width = size.x - padding.x - padding.z - margin.x - margin.z;
if (node->isResizing()) {
node->setMaxSize({width, node->getMaxSize().y});
@ -80,7 +78,7 @@ void Panel::refresh() {
node->refresh();
glm::vec2 nodeSize = node->getSize();
y += nodeSize.y + margin.w + interval;
maxw = fmax(maxw, ex+nodeSize.x+margin.z+padding.z);
maxw = fmax(maxw, ex + nodeSize.x + margin.z + padding.z);
}
actualLength = y + padding.w;
} else {
@ -89,11 +87,13 @@ void Panel::refresh() {
glm::vec2 nodesize = node->getSize();
const glm::vec4 margin = node->getMargin();
x += margin.x;
node->setPos(glm::vec2(x, y+margin.y));
node->setPos(glm::vec2(x, y + margin.y));
x += nodesize.x + margin.z + interval;
node->refresh();
maxh = fmax(maxh, y+margin.y+node->getSize().y+margin.w+padding.w);
maxh = fmax(
maxh, y + margin.y + node->getSize().y + margin.w + padding.w
);
}
actualLength = size.y;
}

View File

@ -1,15 +1,16 @@
#pragma once
#include "commons.hpp"
#include "BasePanel.hpp"
#include "commons.hpp"
namespace gui {
class Panel : public BasePanel {
public:
Panel(
glm::vec2 size,
glm::vec4 padding=glm::vec4(0.0f),
float interval=2.0f
GUI& gui,
glm::vec2 size,
glm::vec4 padding = glm::vec4(2.0f),
float interval = 2.0f
);
virtual ~Panel();

View File

@ -1,15 +1,16 @@
#pragma once
#include <glm/glm.hpp>
#include <memory>
#include "UINode.hpp"
#include "typedefs.hpp"
#include <memory>
class Assets;
class DrawContext;
namespace gui {
class Plotter : public gui::UINode {
class Plotter : public UINode {
std::unique_ptr<int[]> points;
float multiplier;
int index = 0;
@ -17,13 +18,18 @@ namespace gui {
int dmheight;
int labelsInterval;
public:
Plotter(uint width, uint height, float multiplier, int labelsInterval)
: gui::UINode(glm::vec2(width, height)),
multiplier(multiplier),
dmwidth(width-50),
dmheight(height),
labelsInterval(labelsInterval)
{
Plotter(
GUI& gui,
uint width,
uint height,
float multiplier,
int labelsInterval
)
: UINode(gui, glm::vec2(width, height)),
multiplier(multiplier),
dmwidth(width - 50),
dmheight(height),
labelsInterval(labelsInterval) {
points = std::make_unique<int[]>(dmwidth);
}

View File

@ -2,15 +2,15 @@
using namespace gui;
SplitBox::SplitBox(const glm::vec2& size, float splitPos, Orientation orientation)
: BasePanel(size, glm::vec4(), 4.0f, orientation), splitPos(splitPos) {
SplitBox::SplitBox(GUI& gui, const glm::vec2& size, float splitPos, Orientation orientation)
: BasePanel(gui, size, glm::vec4(), 4.0f, orientation), splitPos(splitPos) {
setCursor(
orientation == Orientation::vertical ? CursorShape::NS_RESIZE
: CursorShape::EW_RESIZE
);
}
void SplitBox::mouseMove(GUI*, int x, int y) {
void SplitBox::mouseMove(int x, int y) {
auto pos = calcPos();
auto size = getSize();
@ -59,7 +59,7 @@ void SplitBox::refresh() {
}
}
void SplitBox::doubleClick(GUI*, int x, int y) {
void SplitBox::doubleClick(int x, int y) {
if (nodes.size() < 2) {
return;
}

View File

@ -5,12 +5,12 @@
namespace gui {
class SplitBox : public BasePanel {
public:
SplitBox(const glm::vec2& size, float splitPos, Orientation orientation);
SplitBox(GUI& gui, const glm::vec2& size, float splitPos, Orientation orientation);
virtual void mouseMove(GUI*, int x, int y) override;
virtual void mouseMove(int x, int y) override;
virtual void refresh() override;
virtual void fullRefresh() override;
virtual void doubleClick(GUI*, int x, int y) override;
virtual void doubleClick(int x, int y) override;
private:
float splitPos;
};

View File

@ -4,15 +4,17 @@
#include <sstream>
#include <utility>
#include "../GUI.hpp"
#include "../markdown.hpp"
#include "Label.hpp"
#include "assets/Assets.hpp"
#include "devtools/syntax_highlighting.hpp"
#include "engine/Engine.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Font.hpp"
#include "graphics/ui/markdown.hpp"
#include "util/stringutil.hpp"
#include "window/Events.hpp"
#include "window/Window.hpp"
#include "devtools/actions.hpp"
#include "../markdown.hpp"
@ -186,8 +188,9 @@ namespace gui {
};
}
TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
: Container(glm::vec2(200, 32)),
TextBox::TextBox(GUI& gui, std::wstring placeholder, glm::vec4 padding)
: Container(gui, glm::vec2(200, 32)),
inputEvents(gui.getInput()),
history(std::make_shared<ActionsHistory>()),
historian(std::make_unique<TextBoxHistorian>(*this, *history)),
padding(padding),
@ -197,19 +200,24 @@ TextBox::TextBox(std::wstring placeholder, glm::vec4 padding)
setOnUpPressed(nullptr);
setOnDownPressed(nullptr);
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
label = std::make_shared<Label>(L"");
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
label = std::make_shared<Label>(gui, L"");
label->setSize(
size - glm::vec2(padding.z + padding.x, padding.w + padding.y)
);
label->setPos(glm::vec2(
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers, padding.y
));
add(label);
lineNumbersLabel = std::make_shared<Label>(L"");
lineNumbersLabel = std::make_shared<Label>(gui, L"");
lineNumbersLabel->setMultiline(true);
lineNumbersLabel->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
lineNumbersLabel->setSize(
size - glm::vec2(padding.z + padding.x, padding.w + padding.y)
);
lineNumbersLabel->setVerticalAlign(Align::top);
add(lineNumbersLabel);
setHoverColor(glm::vec4(0.05f, 0.1f, 0.2f, 0.75f));
textInitX = label->getPos().x;
@ -242,7 +250,9 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
batch->texture(nullptr);
batch->setColor(glm::vec4(1.0f));
if (editable && int((Window::time() - caretLastMove) * 2) % 2 == 0) {
float time = gui.getWindow().time();
if (editable && static_cast<int>((time - caretLastMove) * 2) % 2 == 0) {
uint line = rawTextCache.getLineByTextIndex(caret);
uint lcaret = caret - rawTextCache.getTextLineOffset(line);
int width = font->calcWidth(input, lcaret);
@ -342,7 +352,7 @@ void TextBox::drawBackground(const DrawContext& pctx, const Assets&) {
batch->texture(nullptr);
auto subctx = pctx.sub();
subctx.setScissors(glm::vec4(pos.x, pos.y-0.5, size.x, size.y+1));
subctx.setScissors(glm::vec4(pos.x, pos.y - 0.5, size.x, size.y + 1));
if (valid) {
if (isFocused() && !multiline) {
@ -371,7 +381,8 @@ void TextBox::refreshLabel() {
const auto& displayText = input.empty() && !hint.empty() ? hint : getText();
if (markup == "md") {
auto [processedText, styles] = markdown::process(displayText, !focused || !editable);
auto [processedText, styles] =
markdown::process(displayText, !focused || !editable);
label->setText(std::move(processedText));
label->setStyles(std::move(styles));
} else {
@ -380,13 +391,13 @@ void TextBox::refreshLabel() {
label->setStyles(nullptr);
}
}
if (showLineNumbers) {
if (lineNumbersLabel->getLinesNumber() != label->getLinesNumber()) {
std::wstringstream ss;
int n = 1;
for (int i = 1; i <= label->getLinesNumber(); i++) {
if (!label->isFakeLine(i-1)) {
if (!label->isFakeLine(i - 1)) {
ss << n;
n++;
}
@ -402,11 +413,12 @@ void TextBox::refreshLabel() {
if (autoresize && font) {
auto size = getSize();
int newy = glm::min(static_cast<int>(parent->getSize().y),
static_cast<int>(
label->getLinesNumber() *
label->getLineInterval() *
font->getLineHeight()) + 1
int newy = glm::min(
static_cast<int>(parent->getSize().y),
static_cast<int>(
label->getLinesNumber() * label->getLineInterval() *
font->getLineHeight()
) + 1
);
if (newy != static_cast<int>(size.y)) {
size.y = newy;
@ -475,7 +487,7 @@ bool TextBox::eraseSelected() {
input.substr(selectionStart, selectionEnd - selectionStart),
true
);
erase(selectionStart, selectionEnd-selectionStart);
erase(selectionStart, selectionEnd - selectionStart);
resetSelection();
onInput();
return true;
@ -495,7 +507,7 @@ void TextBox::extendSelection(int index) {
size_t TextBox::getLineLength(uint line) const {
size_t position = label->getTextLineOffset(line);
size_t lineLength = label->getTextLineOffset(line+1)-position;
size_t lineLength = label->getTextLineOffset(line + 1) - position;
if (lineLength == 0) {
lineLength = label->getText().length() - position + 1;
}
@ -547,7 +559,7 @@ void TextBox::setMultiline(bool multiline) {
bool TextBox::isMultiline() const {
return multiline;
}
void TextBox::setTextWrapping(bool flag) {
label->setTextWrapping(flag);
}
@ -593,9 +605,9 @@ bool TextBox::isAutoResize() const {
return autoresize;
}
void TextBox::onFocus(GUI* gui) {
Container::onFocus(gui);
if (onEditStart){
void TextBox::onFocus() {
Container::onFocus();
if (onEditStart) {
setCaret(input.size());
onEditStart();
resetSelection();
@ -609,7 +621,9 @@ void TextBox::reposition() {
void TextBox::refresh() {
Container::refresh();
label->setSize(size-glm::vec2(padding.z+padding.x, padding.w+padding.y));
label->setSize(
size - glm::vec2(padding.z + padding.x, padding.w + padding.y)
);
label->setPos(glm::vec2(
padding.x + LINE_NUMBERS_PANE_WIDTH * showLineNumbers + textInitX -
static_cast<int>(textOffset),
@ -629,24 +643,27 @@ size_t TextBox::normalizeIndex(int index) {
/// @param y screen Y position
/// @return non-normalized character index
int TextBox::calcIndexAt(int x, int y) const {
if (font == nullptr)
return 0;
if (font == nullptr) return 0;
const auto& labelText = label->getText();
glm::vec2 lcoord = label->calcPos();
uint line = label->getLineByYOffset(y-lcoord.y);
line = std::min(line, label->getLinesNumber()-1);
uint line = label->getLineByYOffset(y - lcoord.y);
line = std::min(line, label->getLinesNumber() - 1);
size_t lineLength = getLineLength(line);
uint offset = 0;
while (lcoord.x + font->calcWidth(labelText, offset) < x && offset < lineLength-1) {
while (lcoord.x + font->calcWidth(labelText, offset) < x &&
offset < lineLength - 1) {
offset++;
}
return std::min(offset+label->getTextLineOffset(line), labelText.length());
return std::min(
offset + label->getTextLineOffset(line), labelText.length()
);
}
static inline std::wstring get_alphabet(wchar_t c) {
std::wstring alphabet {c};
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') {
return L"abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return L"abcdefghijklmnopqrstuvwxyz_"
L"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
} else if (c >= '0' && c <= '9') {
return L"0123456789";
}
@ -660,7 +677,7 @@ void TextBox::tokenSelectAt(int index) {
}
int left = index;
int right = index;
std::wstring alphabet = get_alphabet(actualText.at(index));
while (left >= 0) {
if (alphabet.find(actualText.at(left)) == std::wstring::npos) {
@ -674,23 +691,23 @@ void TextBox::tokenSelectAt(int index) {
}
right++;
}
select(left+1, right);
select(left + 1, right);
}
void TextBox::doubleClick(GUI* gui, int x, int y) {
UINode::doubleClick(gui, x, y);
tokenSelectAt(normalizeIndex(calcIndexAt(x, y)-1));
void TextBox::doubleClick(int x, int y) {
UINode::doubleClick(x, y);
tokenSelectAt(normalizeIndex(calcIndexAt(x, y) - 1));
}
void TextBox::click(GUI*, int x, int y) {
void TextBox::click(int x, int y) {
int index = normalizeIndex(calcIndexAt(x, y));
selectionStart = index;
selectionEnd = index;
selectionOrigin = index;
}
void TextBox::mouseMove(GUI* gui, int x, int y) {
Container::mouseMove(gui, x, y);
void TextBox::mouseMove(int x, int y) {
Container::mouseMove(x, y);
if (isScrolling()) {
return;
}
@ -701,7 +718,8 @@ void TextBox::mouseMove(GUI* gui, int x, int y) {
}
void TextBox::resetMaxLocalCaret() {
maxLocalCaret = caret - label->getTextLineOffset(label->getLineByTextIndex(caret));
maxLocalCaret =
caret - label->getTextLineOffset(label->getLineByTextIndex(caret));
}
void TextBox::stepLeft(bool shiftPressed, bool breakSelection) {
@ -709,9 +727,9 @@ void TextBox::stepLeft(bool shiftPressed, bool breakSelection) {
size_t caret = breakSelection ? selectionStart : this->caret;
if (caret > 0) {
if (caret > input.length()) {
setCaret(input.length()-1);
setCaret(input.length() - 1);
} else {
setCaret(caret-1);
setCaret(caret - 1);
}
if (shiftPressed) {
if (selectionStart == selectionEnd) {
@ -732,8 +750,8 @@ void TextBox::stepRight(bool shiftPressed, bool breakSelection) {
uint previousCaret = this->caret;
size_t caret = breakSelection ? selectionEnd : this->caret;
if (caret < input.length()) {
setCaret(caret+1);
caretLastMove = Window::time();
setCaret(caret + 1);
caretLastMove = gui.getWindow().time();
if (shiftPressed) {
if (selectionStart == selectionEnd) {
selectionOrigin = previousCaret;
@ -753,9 +771,10 @@ void TextBox::stepDefaultDown(bool shiftPressed, bool breakSelection) {
uint previousCaret = this->caret;
uint caret = breakSelection ? selectionEnd : this->caret;
uint caretLine = label->getLineByTextIndex(caret);
if (caretLine < label->getLinesNumber()-1) {
uint offset = std::min(size_t(maxLocalCaret), getLineLength(caretLine+1)-1);
setCaret(label->getTextLineOffset(caretLine+1) + offset);
if (caretLine < label->getLinesNumber() - 1) {
uint offset =
std::min(size_t(maxLocalCaret), getLineLength(caretLine + 1) - 1);
setCaret(label->getTextLineOffset(caretLine + 1) + offset);
} else {
setCaret(input.length());
}
@ -774,8 +793,9 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) {
uint caret = breakSelection ? selectionStart : this->caret;
uint caretLine = label->getLineByTextIndex(caret);
if (caretLine > 0) {
uint offset = std::min(size_t(maxLocalCaret), getLineLength(caretLine-1)-1);
setCaret(label->getTextLineOffset(caretLine-1) + offset);
uint offset =
std::min(size_t(maxLocalCaret), getLineLength(caretLine - 1) - 1);
setCaret(label->getTextLineOffset(caretLine - 1) + offset);
} else {
setCaret(static_cast<size_t>(0));
}
@ -804,22 +824,22 @@ void TextBox::onInput() {
refreshSyntax();
}
void TextBox::performEditingKeyboardEvents(keycode key) {
bool shiftPressed = Events::pressed(keycode::LEFT_SHIFT);
void TextBox::performEditingKeyboardEvents(Keycode key) {
bool shiftPressed = gui.getInput().pressed(Keycode::LEFT_SHIFT);
bool breakSelection = getSelectionLength() != 0 && !shiftPressed;
if (key == keycode::BACKSPACE) {
if (key == Keycode::BACKSPACE) {
if (!eraseSelected() && caret > 0 && input.length() > 0) {
if (caret > input.length()) {
caret = input.length();
}
historian->onErase(caret - 1, input.substr(caret - 1, 1));
input = input.substr(0, caret-1) + input.substr(caret);
setCaret(caret-1);
input = input.substr(0, caret - 1) + input.substr(caret);
setCaret(caret - 1);
if (validate()) {
onInput();
}
}
} else if (key == keycode::DELETE) {
} else if (key == Keycode::DELETE) {
if (!eraseSelected() && caret < input.length()) {
historian->onErase(caret, input.substr(caret, 1));
input = input.substr(0, caret) + input.substr(caret + 1);
@ -827,7 +847,7 @@ void TextBox::performEditingKeyboardEvents(keycode key) {
onInput();
}
}
} else if (key == keycode::ENTER) {
} else if (key == Keycode::ENTER) {
if (multiline) {
paste(L"\n");
} else {
@ -836,42 +856,43 @@ void TextBox::performEditingKeyboardEvents(keycode key) {
consumer(getText());
}
}
} else if (key == keycode::TAB) {
} else if (key == Keycode::TAB) {
paste(L" ");
} else if (key == keycode::LEFT) {
} else if (key == Keycode::LEFT) {
stepLeft(shiftPressed, breakSelection);
} else if (key == keycode::RIGHT) {
} else if (key == Keycode::RIGHT) {
stepRight(shiftPressed, breakSelection);
} else if (key == keycode::UP && onUpPressed) {
} else if (key == Keycode::UP && onUpPressed) {
onUpPressed();
} else if (key == keycode::DOWN && onDownPressed) {
} else if (key == Keycode::DOWN && onDownPressed) {
onDownPressed();
}
}
void TextBox::keyPressed(keycode key) {
void TextBox::keyPressed(Keycode key) {
const auto& inputEvents = gui.getInput();
if (editable) {
performEditingKeyboardEvents(key);
}
if (Events::pressed(keycode::LEFT_CONTROL) && key != keycode::LEFT_CONTROL) {
if (inputEvents.pressed(Keycode::LEFT_CONTROL) && key != Keycode::LEFT_CONTROL) {
if (controlCombinationsHandler) {
if (controlCombinationsHandler(static_cast<int>(key))) {
return;
}
}
// Copy selected text to clipboard
if (key == keycode::C || key == keycode::X) {
if (key == Keycode::C || key == Keycode::X) {
std::string text = util::wstr2str_utf8(getSelection());
if (!text.empty()) {
Window::setClipboardText(text.c_str());
gui.getInput().setClipboardText(text.c_str());
}
if (editable && key == keycode::X) {
if (editable && key == Keycode::X) {
eraseSelected();
}
}
// Paste text from clipboard
if (key == keycode::V && editable) {
const char* text = Window::getClipboardText();
if (key == Keycode::V && editable) {
const char* text = inputEvents.getClipboardText();
if (text) {
historian->sync(); // flush buffer before combination
// Combine deleting selected text and pasing a clipboard content
@ -881,18 +902,18 @@ void TextBox::keyPressed(keycode key) {
}
}
// Select/deselect all
if (key == keycode::A) {
if (key == Keycode::A) {
if (selectionStart == selectionEnd) {
select(0, input.length());
} else {
resetSelection();
}
}
if (key == keycode::Z) {
if (key == Keycode::Z) {
historian->undo();
refreshSyntax();
}
if (key == keycode::Y) {
if (key == Keycode::Y) {
historian->redo();
refreshSyntax();
}
@ -924,14 +945,14 @@ std::shared_ptr<UINode> TextBox::getAt(const glm::vec2& pos) {
return UINode::getAt(pos);
}
void TextBox::setOnUpPressed(const runnable &callback) {
void TextBox::setOnUpPressed(const runnable& callback) {
if (callback == nullptr) {
onUpPressed = [this]() {
if (Events::pressed(keycode::LEFT_CONTROL)) {
if (inputEvents.pressed(Keycode::LEFT_CONTROL)) {
scrolled(1);
return;
}
bool shiftPressed = Events::pressed(keycode::LEFT_SHIFT);
bool shiftPressed = inputEvents.pressed(Keycode::LEFT_SHIFT);
bool breakSelection = getSelectionLength() != 0 && !shiftPressed;
stepDefaultUp(shiftPressed, breakSelection);
};
@ -940,14 +961,14 @@ void TextBox::setOnUpPressed(const runnable &callback) {
}
}
void TextBox::setOnDownPressed(const runnable &callback) {
void TextBox::setOnDownPressed(const runnable& callback) {
if (callback == nullptr) {
onDownPressed = [this]() {
if (Events::pressed(keycode::LEFT_CONTROL)) {
if (inputEvents.pressed(Keycode::LEFT_CONTROL)) {
scrolled(-1);
return;
}
bool shiftPressed = Events::pressed(keycode::LEFT_SHIFT);
bool shiftPressed = inputEvents.pressed(Keycode::LEFT_SHIFT);
bool breakSelection = getSelectionLength() != 0 && !shiftPressed;
stepDefaultDown(shiftPressed, breakSelection);
};
@ -984,7 +1005,6 @@ glm::vec4 TextBox::getFocusedColor() const {
return focusedColor;
}
void TextBox::setTextColor(glm::vec4 color) {
this->textColor = color;
}
@ -1002,8 +1022,7 @@ glm::vec4 TextBox::getErrorColor() const {
}
const std::wstring& TextBox::getText() const {
if (input.empty())
return placeholder;
if (input.empty()) return placeholder;
return input;
}
@ -1024,7 +1043,6 @@ void TextBox::setPlaceholder(const std::wstring& placeholder) {
this->placeholder = placeholder;
}
const std::wstring& TextBox::getHint() const {
return hint;
}
@ -1035,7 +1053,7 @@ void TextBox::setHint(const std::wstring& text) {
std::wstring TextBox::getSelection() const {
const auto& text = label->getText();
return text.substr(selectionStart, selectionEnd-selectionStart);
return text.substr(selectionStart, selectionEnd - selectionStart);
}
size_t TextBox::getCaret() const {
@ -1053,28 +1071,29 @@ void TextBox::setCaret(size_t position) {
rawTextCache.prepare(font, width);
rawTextCache.update(input, multiline, label->isTextWrapping());
caretLastMove = Window::time();
caretLastMove = gui.getWindow().time();
uint line = rawTextCache.getLineByTextIndex(caret);
int offset = label->getLineYOffset(line) + getContentOffset().y;
uint lineHeight = font->getLineHeight()*label->getLineInterval();
uint lineHeight = font->getLineHeight() * label->getLineInterval();
if (scrollStep == 0) {
scrollStep = lineHeight;
}
if (offset < 0) {
scrolled(-glm::floor(offset / static_cast<double>(scrollStep)+0.5f));
scrolled(-glm::floor(offset / static_cast<double>(scrollStep) + 0.5f));
} else if (offset >= getSize().y) {
offset -= getSize().y;
scrolled(-glm::ceil(offset / static_cast<double>(scrollStep)+0.5f));
scrolled(-glm::ceil(offset / static_cast<double>(scrollStep) + 0.5f));
}
int lcaret = caret - rawTextCache.getTextLineOffset(line);
int realoffset =
font->calcWidth(labelText, lcaret) - static_cast<int>(textOffset) + 2;
if (realoffset-width > 0) {
setTextOffset(textOffset + realoffset-width);
if (realoffset - width > 0) {
setTextOffset(textOffset + realoffset - width);
} else if (realoffset < 0) {
setTextOffset(std::max(textOffset + realoffset, static_cast<size_t>(0)));
setTextOffset(std::max(textOffset + realoffset, static_cast<size_t>(0))
);
}
}

View File

@ -9,6 +9,7 @@ class ActionsHistory;
namespace gui {
class TextBoxHistorian;
class TextBox : public Container {
const Input& inputEvents;
LabelCache rawTextCache;
std::shared_ptr<ActionsHistory> history;
std::unique_ptr<TextBoxHistorian> historian;
@ -86,7 +87,7 @@ namespace gui {
/// @brief Set maxLocalCaret to local (line) caret position
void resetMaxLocalCaret();
void performEditingKeyboardEvents(keycode key);
void performEditingKeyboardEvents(Keycode key);
void refreshLabel();
@ -94,7 +95,8 @@ namespace gui {
void refreshSyntax();
public:
TextBox(
explicit TextBox(
GUI& gui,
std::wstring placeholder,
glm::vec4 padding=glm::vec4(4.0f)
);
@ -227,16 +229,16 @@ namespace gui {
virtual bool isShowLineNumbers() const;
virtual void reposition() override;
virtual void onFocus(GUI*) override;
virtual void onFocus() override;
virtual void refresh() override;
virtual void doubleClick(GUI*, int x, int y) override;
virtual void click(GUI*, int, int) override;
virtual void mouseMove(GUI*, int x, int y) override;
virtual void doubleClick(int x, int y) override;
virtual void click(int, int) override;
virtual void mouseMove(int x, int y) override;
virtual bool isFocuskeeper() const override {return true;}
virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void drawBackground(const DrawContext& pctx, const Assets& assets) override;
virtual void typed(unsigned int codepoint) override;
virtual void keyPressed(keycode key) override;
virtual void keyPressed(Keycode key) override;
virtual std::shared_ptr<UINode> getAt(const glm::vec2& pos) override;
virtual void setOnUpPressed(const runnable &callback);
virtual void setOnDownPressed(const runnable &callback);

View File

@ -9,12 +9,13 @@
using namespace gui;
TrackBar::TrackBar(
GUI& gui,
double min,
double max,
double value,
double step,
int trackWidth
) : UINode(glm::vec2(26)),
) : UINode(gui, glm::vec2(26)),
min(min),
max(max),
value(value),
@ -54,7 +55,7 @@ void TrackBar::setSubConsumer(doubleconsumer consumer) {
this->subconsumer = std::move(consumer);
}
void TrackBar::mouseMove(GUI*, int x, int) {
void TrackBar::mouseMove(int x, int) {
glm::vec2 pos = calcPos();
value = x - trackWidth/2;
value -= pos.x;
@ -62,7 +63,7 @@ void TrackBar::mouseMove(GUI*, int x, int) {
value += min;
value = (value > max) ? max : value;
value = (value < min) ? min : value;
value = (int64_t)round(value / step) * step;
value = static_cast<int64_t>(std::round(value / step)) * step;
if (consumer && !changeOnRelease) {
consumer(value);
@ -72,7 +73,7 @@ void TrackBar::mouseMove(GUI*, int x, int) {
}
}
void TrackBar::mouseRelease(GUI*, int, int) {
void TrackBar::mouseRelease(int, int) {
if (consumer && changeOnRelease) {
consumer(value);
}

View File

@ -16,19 +16,22 @@ namespace gui {
int trackWidth;
bool changeOnRelease = false;
public:
TrackBar(double min,
double max,
double value,
double step=1.0,
int trackWidth=12);
TrackBar(
GUI& gui,
double min,
double max,
double value,
double step = 1.0,
int trackWidth = 12
);
virtual void draw(const DrawContext& pctx, const Assets& assets) override;
virtual void setSupplier(doublesupplier);
virtual void setConsumer(doubleconsumer);
virtual void setSubConsumer(doubleconsumer);
virtual void mouseMove(GUI*, int x, int y) override;
virtual void mouseRelease(GUI*, int x, int y) override;
virtual void mouseMove(int x, int y) override;
virtual void mouseRelease(int x, int y) override;
virtual double getValue() const;
virtual double getMin() const;

View File

@ -8,7 +8,7 @@
using gui::UINode;
using gui::Align;
UINode::UINode(glm::vec2 size) : size(size) {
UINode::UINode(GUI& gui, glm::vec2 size) : gui(gui), size(size) {
}
UINode::~UINode() {
@ -74,18 +74,18 @@ UINode* UINode::listenDoubleClick(const onaction& action) {
return this;
}
void UINode::click(GUI*, int, int) {
void UINode::click(int, int) {
pressed = true;
}
void UINode::doubleClick(GUI* gui, int x, int y) {
void UINode::doubleClick(int x, int y) {
pressed = true;
if (isInside(glm::vec2(x, y))) {
doubleClickCallbacks.notify(gui);
}
}
void UINode::mouseRelease(GUI* gui, int x, int y) {
void UINode::mouseRelease(int x, int y) {
pressed = false;
if (isInside(glm::vec2(x, y))) {
actions.notify(gui);

View File

@ -19,8 +19,8 @@ namespace gui {
class GUI;
class Container;
using onaction = std::function<void(GUI*)>;
using onnumberchange = std::function<void(GUI*, double)>;
using onaction = std::function<void(GUI&)>;
using onnumberchange = std::function<void(GUI&, double)>;
class ActionsSet {
std::unique_ptr<std::vector<onaction>> callbacks;
@ -32,7 +32,7 @@ namespace gui {
callbacks->push_back(callback);
}
void notify(GUI* gui) {
void notify(GUI& gui) {
if (callbacks) {
for (auto& callback : *callbacks) {
callback(gui);
@ -64,6 +64,9 @@ namespace gui {
/// @brief Base abstract class for all UI elements
class UINode : public std::enable_shared_from_this<UINode> {
protected:
GUI& gui;
private:
/// @brief element identifier used for direct access in UiDocument
std::string id = "";
/// @brief element enabled state
@ -118,7 +121,7 @@ namespace gui {
/// @brief cursor shape when mouse is over the element
CursorShape cursor = CursorShape::ARROW;
UINode(glm::vec2 size);
UINode(GUI& gui, glm::vec2 size);
public:
virtual ~UINode();
@ -169,12 +172,12 @@ namespace gui {
virtual UINode* listenAction(const onaction &action);
virtual UINode* listenDoubleClick(const onaction &action);
virtual void onFocus(GUI*) {focused = true;}
virtual void doubleClick(GUI*, int x, int y);
virtual void click(GUI*, int x, int y);
virtual void clicked(GUI*, mousecode button) {}
virtual void mouseMove(GUI*, int x, int y) {};
virtual void mouseRelease(GUI*, int x, int y);
virtual void onFocus() {focused = true;}
virtual void doubleClick(int x, int y);
virtual void click(int x, int y);
virtual void clicked(Mousecode button) {}
virtual void mouseMove(int x, int y) {};
virtual void mouseRelease(int x, int y);
virtual void scrolled(int value);
bool isPressed() const;
@ -185,7 +188,7 @@ namespace gui {
virtual bool isFocuskeeper() const {return false;}
virtual void typed(unsigned int codepoint) {};
virtual void keyPressed(keycode key) {};
virtual void keyPressed(Keycode key) {};
/// @brief Check if screen position is inside of the element
/// @param pos screen position
@ -222,11 +225,11 @@ namespace gui {
virtual glm::vec2 calcPos() const;
virtual void setPos(glm::vec2 pos);
virtual glm::vec2 getPos() const;
virtual glm::vec2 getSize() const;
glm::vec2 getSize() const;
virtual void setSize(glm::vec2 size);
virtual glm::vec2 getMinSize() const;
glm::vec2 getMinSize() const;
virtual void setMinSize(glm::vec2 size);
virtual glm::vec2 getMaxSize() const;
glm::vec2 getMaxSize() const;
virtual void setMaxSize(glm::vec2 size);
/// @brief Called in containers when new element added
virtual void refresh() {};

View File

@ -1,37 +1,36 @@
#include "gui_util.hpp"
#include "elements/Label.hpp"
#include "elements/Menu.hpp"
#include "elements/Button.hpp"
#include "elements/TextBox.hpp"
#include "gui_xml.hpp"
#include "logic/scripting/scripting.hpp"
#include "frontend/locale.hpp"
#include "util/stringutil.hpp"
#include "delegates.hpp"
#include "window/Events.hpp"
#include "engine/Engine.hpp"
#include <glm/glm.hpp>
#include "delegates.hpp"
#include "elements/Button.hpp"
#include "elements/Label.hpp"
#include "elements/Menu.hpp"
#include "elements/TextBox.hpp"
#include "engine/Engine.hpp"
#include "frontend/locale.hpp"
#include "gui_xml.hpp"
#include "logic/scripting/scripting.hpp"
#include "util/stringutil.hpp"
using namespace gui;
std::shared_ptr<gui::UINode> guiutil::create(const std::string& source, scriptenv env) {
std::shared_ptr<gui::UINode> guiutil::create(
GUI& gui, const std::string& source, scriptenv env
) {
if (env == nullptr) {
env = scripting::get_root_environment();
}
UiXmlReader reader(env);
UiXmlReader reader(gui, env);
return reader.readXML("[string]", source);
}
void guiutil::alert(
Engine& engine,
const std::wstring& text,
const runnable& on_hidden
Engine& engine, const std::wstring& text, const runnable& on_hidden
) {
GUI& gui = engine.getGUI();
auto panel = std::make_shared<Panel>(
gui,
glm::vec2(
glm::min(
static_cast<size_t>(650),
@ -44,7 +43,7 @@ void guiutil::alert(
);
panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
auto menuPtr = engine.getGUI()->getMenu();
auto menuPtr = gui.getMenu();
auto& menu = *menuPtr;
runnable on_hidden_final = [on_hidden, &menu]() {
menu.removePage("<alert>");
@ -54,25 +53,26 @@ void guiutil::alert(
menu.reset();
}
};
auto label = std::make_shared<Label>(text);
auto label = std::make_shared<Label>(gui, text);
label->setMultiline(true);
label->setSize(glm::vec2(1, 24));
label->setAutoResize(true);
panel->add(label);
panel->add(std::make_shared<Button>(
langs::get(L"Ok"), glm::vec4(10.f),
[on_hidden_final](GUI*) {
on_hidden_final();
}
gui,
langs::get(L"Ok"),
glm::vec4(10.f),
[on_hidden_final](GUI&) { on_hidden_final(); }
));
panel->refresh();
panel->keepAlive(Events::keyCallbacks[keycode::ENTER].add([on_hidden_final](){
auto& input = engine.getInput();
panel->keepAlive(input.addKeyCallback(Keycode::ENTER, [on_hidden_final]() {
on_hidden_final();
return true;
}));
panel->keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([on_hidden_final](){
panel->keepAlive(input.addKeyCallback(Keycode::ESCAPE, [on_hidden_final]() {
on_hidden_final();
return true;
}));
@ -91,17 +91,25 @@ void guiutil::confirm(
if (yestext.empty()) yestext = langs::get(L"Yes");
if (notext.empty()) notext = langs::get(L"No");
auto container = std::make_shared<Container>(glm::vec2(5000, 5000));
auto& gui = engine.getGUI();
auto& input = engine.getInput();
auto container = std::make_shared<Container>(gui, glm::vec2(5000, 5000));
container->setColor(glm::vec4(0.05f, 0.05f, 0.05f, 0.7f));
auto panel = std::make_shared<Panel>(glm::vec2(600, 200), glm::vec4(8.0f), 8.0f);
auto panel = std::make_shared<Panel>(
gui, glm::vec2(600, 200), glm::vec4(8.0f), 8.0f
);
panel->setGravity(Gravity::center_center);
container->add(panel);
panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
panel->add(std::make_shared<Label>(text));
auto subpanel = std::make_shared<Panel>(glm::vec2(600, 53));
panel->add(std::make_shared<Label>(gui, text));
auto subpanel = std::make_shared<Panel>(gui, glm::vec2(600, 53));
subpanel->setColor(glm::vec4(0));
auto menu = engine.getGUI()->getMenu();
auto menu = gui.getMenu();
runnable on_confirm_final = [on_confirm, menu]() {
menu->removePage("<confirm>");
@ -121,20 +129,20 @@ void guiutil::confirm(
}
};
subpanel->add(std::make_shared<Button>(yestext, glm::vec4(8.f), [=](GUI*){
subpanel->add(std::make_shared<Button>(gui, yestext, glm::vec4(8.f), [=](GUI&) {
on_confirm_final();
}));
subpanel->add(std::make_shared<Button>(notext, glm::vec4(8.f), [=](GUI*){
subpanel->add(std::make_shared<Button>(gui, notext, glm::vec4(8.f), [=](GUI&) {
on_deny_final();
}));
panel->add(subpanel);
panel->keepAlive(Events::keyCallbacks[keycode::ENTER].add([=](){
panel->keepAlive(input.addKeyCallback(Keycode::ENTER, [=]() {
on_confirm_final();
return true;
}));
panel->keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([=](){
panel->keepAlive(input.addKeyCallback(Keycode::ESCAPE, [=]() {
on_deny_final();
return true;
}));
@ -145,21 +153,25 @@ void guiutil::confirm(
}
void guiutil::confirm_with_memo(
const std::shared_ptr<gui::Menu>& menu,
const std::wstring& text,
const std::wstring& memo,
const runnable& on_confirm,
std::wstring yestext,
std::wstring notext) {
Engine& engine,
const std::wstring& text,
const std::wstring& memo,
const runnable& on_confirm,
std::wstring yestext,
std::wstring notext
) {
auto& gui = engine.getGUI();
auto menu = gui.getMenu();
if (yestext.empty()) yestext = langs::get(L"Yes");
if (notext.empty()) notext = langs::get(L"No");
auto panel = std::make_shared<Panel>(glm::vec2(600, 500), glm::vec4(8.0f), 8.0f);
auto panel = std::make_shared<Panel>(
gui, glm::vec2(600, 500), glm::vec4(8.0f), 8.0f
);
panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
panel->add(std::make_shared<Label>(text));
auto textbox = std::make_shared<TextBox>(L"");
panel->add(std::make_shared<Label>(gui, text));
auto textbox = std::make_shared<TextBox>(gui, L"");
textbox->setMultiline(true);
textbox->setTextWrapping(true);
textbox->setSize(glm::vec2(600, 300));
@ -167,16 +179,15 @@ void guiutil::confirm_with_memo(
textbox->setEditable(false);
panel->add(textbox);
auto subpanel = std::make_shared<Panel>(glm::vec2(600, 53));
auto subpanel = std::make_shared<Panel>(gui, glm::vec2(600, 53));
subpanel->setColor(glm::vec4(0));
subpanel->add(std::make_shared<Button>(yestext, glm::vec4(8.f), [=](GUI*){
if (on_confirm)
on_confirm();
subpanel->add(std::make_shared<Button>(gui, yestext, glm::vec4(8.f), [=](GUI&) {
if (on_confirm) on_confirm();
menu->back();
}));
subpanel->add(std::make_shared<Button>(notext, glm::vec4(8.f), [=](GUI*){
subpanel->add(std::make_shared<Button>(gui, notext, glm::vec4(8.f), [=](GUI&) {
menu->back();
}));

View File

@ -1,38 +1,46 @@
#pragma once
#include "GUI.hpp"
#include "typedefs.hpp"
#include "delegates.hpp"
#include <memory>
#include <string>
#include "delegates.hpp"
#include "typedefs.hpp"
class Engine;
namespace gui {
class GUI;
class UINode;
}
namespace guiutil {
/// @brief Create element from XML
/// @param source XML
std::shared_ptr<gui::UINode> create(const std::string& source, scriptenv env=0);
std::shared_ptr<gui::UINode> create(
gui::GUI& gui, const std::string& source, scriptenv env = 0
);
void alert(
Engine& engine,
const std::wstring& text,
const runnable& on_hidden=nullptr
const std::wstring& text,
const runnable& on_hidden = nullptr
);
void confirm(
Engine& engine,
const std::wstring& text,
const runnable& on_confirm=nullptr,
const runnable& on_deny=nullptr,
std::wstring yestext=L"",
std::wstring notext=L"");
const std::wstring& text,
const runnable& on_confirm = nullptr,
const runnable& on_deny = nullptr,
std::wstring yestext = L"",
std::wstring notext = L""
);
void confirm_with_memo(
const std::shared_ptr<gui::Menu>& menu,
const std::wstring& text,
Engine& engine,
const std::wstring& text,
const std::wstring& memo,
const runnable& on_confirm=nullptr,
std::wstring yestext=L"",
std::wstring notext=L"");
const runnable& on_confirm = nullptr,
std::wstring yestext = L"",
std::wstring notext = L""
);
}

View File

@ -1,29 +1,29 @@
#include "gui_xml.hpp"
#include "elements/Panel.hpp"
#include "elements/Image.hpp"
#include "elements/Menu.hpp"
#include <stdexcept>
#include <utility>
#include "GUI.hpp"
#include "elements/Button.hpp"
#include "elements/Canvas.hpp"
#include "elements/CheckBox.hpp"
#include "elements/TextBox.hpp"
#include "elements/SplitBox.hpp"
#include "elements/TrackBar.hpp"
#include "elements/Image.hpp"
#include "elements/InputBindBox.hpp"
#include "elements/InventoryView.hpp"
#include "GUI.hpp"
#include "elements/Menu.hpp"
#include "elements/Panel.hpp"
#include "elements/TextBox.hpp"
#include "elements/TrackBar.hpp"
#include "engine/Engine.hpp"
#include "frontend/menu.hpp"
#include "frontend/locale.hpp"
#include "frontend/menu.hpp"
#include "items/Inventory.hpp"
#include "logic/scripting/scripting.hpp"
#include "maths/voxmaths.hpp"
#include "util/stringutil.hpp"
#include "window/Events.hpp"
#include <stdexcept>
#include <utility>
using namespace gui;
@ -56,7 +56,7 @@ static Gravity gravity_from_string(const std::string& str) {
}
static runnable create_runnable(
const UiXmlReader& reader,
const UiXmlReader& reader,
const xml::xmlelement& element,
const std::string& name
) {
@ -80,7 +80,7 @@ static onaction create_action(
if (callback == nullptr) {
return nullptr;
}
return [callback](GUI*) {callback();};
return [callback](GUI&) { callback(); };
}
/// @brief Read basic UINode properties
@ -152,9 +152,7 @@ static void read_uinode(
node.setAlign(align_from_string(alignName, node.getAlign()));
if (element.has("gravity")) {
node.setGravity(gravity_from_string(
element.attr("gravity").getText()
));
node.setGravity(gravity_from_string(element.attr("gravity").getText()));
}
if (element.has("tooltip")) {
@ -184,7 +182,9 @@ static void read_uinode(
}
}
static void read_container_impl(UiXmlReader& reader, const xml::xmlelement& element, Container& container) {
static void read_container_impl(
UiXmlReader& reader, const xml::xmlelement& element, Container& container
) {
read_uinode(reader, element, container);
if (element.has("scrollable")) {
@ -194,8 +194,7 @@ static void read_container_impl(UiXmlReader& reader, const xml::xmlelement& elem
container.setScrollStep(element.attr("scroll-step").asInt());
}
for (auto& sub : element.getElements()) {
if (sub->isText())
continue;
if (sub->isText()) continue;
auto subnode = reader.readUINode(*sub);
if (subnode) {
container.add(subnode);
@ -203,7 +202,9 @@ static void read_container_impl(UiXmlReader& reader, const xml::xmlelement& elem
}
}
void UiXmlReader::readUINode(UiXmlReader& reader, const xml::xmlelement& element, Container& container) {
void UiXmlReader::readUINode(
UiXmlReader& reader, const xml::xmlelement& element, Container& container
) {
read_container_impl(reader, element, container);
}
@ -225,8 +226,7 @@ static void read_base_panel_impl(
panel.setPadding(padding);
glm::vec2 size = panel.getSize();
panel.setSize(glm::vec2(
size.x + padding.x + padding.z,
size.y + padding.y + padding.w
size.x + padding.x + padding.z, size.y + padding.y + padding.w
));
}
if (element.has("orientation")) {
@ -254,10 +254,15 @@ static void read_panel_impl(
if (element.has("min-length")) {
panel.setMinLength(element.attr("min-length").asInt());
}
if (element.has("orientation")) {
auto& oname = element.attr("orientation").getText();
if (oname == "horizontal") {
panel.setOrientation(Orientation::horizontal);
}
}
if (subnodes) {
for (auto& sub : element.getElements()) {
if (sub->isText())
continue;
if (sub->isText()) continue;
auto subnode = reader.readUINode(*sub);
if (subnode) {
panel.add(subnode);
@ -273,7 +278,7 @@ static std::wstring parse_inner_text(
if (element.size() == 1) {
std::string source = element.sub(0).attr("#").getText();
util::trim(source);
text = util::str2wstr_utf8(source);
text = util::str2wstr_utf8(source);
if (text[0] == '@') {
if (context.empty()) {
text = langs::get(text.substr(1));
@ -289,12 +294,12 @@ static std::shared_ptr<UINode> readLabel(
const UiXmlReader& reader, const xml::xmlelement& element
) {
std::wstring text = parse_inner_text(element, reader.getContext());
auto label = std::make_shared<Label>(text);
auto label = std::make_shared<Label>(reader.getGUI(), text);
read_uinode(reader, element, *label);
if (element.has("valign")) {
label->setVerticalAlign(
align_from_string(element.attr("valign").getText(), label->getVerticalAlign())
);
label->setVerticalAlign(align_from_string(
element.attr("valign").getText(), label->getVerticalAlign()
));
}
if (element.has("supplier")) {
label->textSupplier(scripting::create_wstring_supplier(
@ -324,7 +329,7 @@ static std::shared_ptr<UINode> readLabel(
static std::shared_ptr<UINode> read_container(
UiXmlReader& reader, const xml::xmlelement& element
) {
auto container = std::make_shared<Container>(glm::vec2());
auto container = std::make_shared<Container>(reader.getGUI(), glm::vec2());
read_container_impl(reader, element, *container);
return container;
}
@ -337,8 +342,9 @@ static std::shared_ptr<UINode> read_split_box(
element.attr("orientation", "vertical").getText() == "horizontal"
? Orientation::horizontal
: Orientation::vertical;
auto splitBox =
std::make_shared<SplitBox>(glm::vec2(), splitPos, orientation);
auto splitBox = std::make_shared<SplitBox>(
reader.getGUI(), glm::vec2(), splitPos, orientation
);
read_base_panel_impl(reader, element, *splitBox);
for (auto& sub : element.getElements()) {
if (sub->isText())
@ -355,7 +361,9 @@ static std::shared_ptr<UINode> read_panel(
UiXmlReader& reader, const xml::xmlelement& element
) {
float interval = element.attr("interval", "2").asFloat();
auto panel = std::make_shared<Panel>(glm::vec2(), glm::vec4(), interval);
auto panel = std::make_shared<Panel>(
reader.getGUI(), glm::vec2(), glm::vec4(), interval
);
read_panel_impl(reader, element, *panel);
return panel;
}
@ -363,6 +371,7 @@ static std::shared_ptr<UINode> read_panel(
static std::shared_ptr<UINode> read_button(
UiXmlReader& reader, const xml::xmlelement& element
) {
auto& gui = reader.getGUI();
glm::vec4 padding = element.attr("padding", "10").asVec4();
std::shared_ptr<Button> button;
@ -370,14 +379,14 @@ static std::shared_ptr<UINode> read_button(
if (!elements.empty() && elements[0]->getTag() != "#") {
auto inner = reader.readUINode(*elements.at(0));
if (inner != nullptr) {
button = std::make_shared<Button>(inner, padding);
button = std::make_shared<Button>(gui, inner, padding);
} else {
button = std::make_shared<Button>(L"", padding, nullptr);
button = std::make_shared<Button>(gui, L"", padding, nullptr);
}
read_panel_impl(reader, element, *button, false);
} else {
std::wstring text = parse_inner_text(element, reader.getContext());
button = std::make_shared<Button>(text, padding, nullptr);
button = std::make_shared<Button>(gui, text, padding, nullptr);
read_panel_impl(reader, element, *button, true);
}
if (element.has("text-align")) {
@ -393,7 +402,9 @@ static std::shared_ptr<UINode> read_check_box(
) {
auto text = parse_inner_text(element, reader.getContext());
bool checked = element.attr("checked", "false").asBool();
auto checkbox = std::make_shared<FullCheckBox>(text, glm::vec2(32), checked);
auto checkbox = std::make_shared<FullCheckBox>(
reader.getGUI(), text, glm::vec2(32), checked
);
read_panel_impl(reader, element, *checkbox);
if (element.has("consumer")) {
@ -417,20 +428,22 @@ static std::shared_ptr<UINode> read_check_box(
static std::shared_ptr<UINode> read_text_box(
UiXmlReader& reader, const xml::xmlelement& element
) {
auto placeholder = util::str2wstr_utf8(element.attr("placeholder", "").getText());
auto placeholder =
util::str2wstr_utf8(element.attr("placeholder", "").getText());
auto hint = util::str2wstr_utf8(element.attr("hint", "").getText());
auto text = parse_inner_text(element, reader.getContext());
auto textbox = std::make_shared<TextBox>(placeholder, glm::vec4(0.0f));
auto textbox = std::make_shared<TextBox>(
reader.getGUI(), placeholder, glm::vec4(0.0f)
);
textbox->setHint(hint);
read_container_impl(reader, element, *textbox);
if (element.has("padding")) {
glm::vec4 padding = element.attr("padding").asVec4();
textbox->setPadding(padding);
glm::vec2 size = textbox->getSize();
textbox->setSize(glm::vec2(
size.x + padding.x + padding.z,
size.y + padding.y + padding.w
size.x + padding.x + padding.z, size.y + padding.y + padding.w
));
}
textbox->setText(text);
@ -513,7 +526,7 @@ static std::shared_ptr<UINode> read_image(
const UiXmlReader& reader, const xml::xmlelement& element
) {
std::string src = element.attr("src", "").getText();
auto image = std::make_shared<Image>(src);
auto image = std::make_shared<Image>(reader.getGUI(), src);
read_uinode(reader, element, *image);
return image;
}
@ -521,11 +534,12 @@ static std::shared_ptr<UINode> read_image(
static std::shared_ptr<UINode> read_canvas(
const UiXmlReader& reader, const xml::xmlelement& element
) {
auto size = glm::uvec2{32, 32};
auto size = glm::uvec2 {32, 32};
if (element.has("size")) {
size = element.attr("size").asVec2();
}
auto image = std::make_shared<Canvas>(ImageFormat::rgba8888, size);
auto image =
std::make_shared<Canvas>(reader.getGUI(), ImageFormat::rgba8888, size);
read_uinode(reader, element, *image);
return image;
}
@ -540,19 +554,24 @@ static std::shared_ptr<UINode> read_track_bar(
float def = element.attr("value", "0.0").asFloat();
float step = element.attr("step", "1.0").asFloat();
int trackWidth = element.attr("track-width", "12").asInt();
auto bar = std::make_shared<TrackBar>(minv, maxv, def, step, trackWidth);
auto bar = std::make_shared<TrackBar>(
reader.getGUI(), minv, maxv, def, step, trackWidth
);
read_uinode(reader, element, *bar);
if (element.has("consumer")) {
bar->setConsumer(scripting::create_number_consumer(
env, element.attr("consumer").getText(), file));
env, element.attr("consumer").getText(), file
));
}
if (element.has("sub-consumer")) {
bar->setSubConsumer(scripting::create_number_consumer(
env, element.attr("sub-consumer").getText(), file));
env, element.attr("sub-consumer").getText(), file
));
}
if (element.has("supplier")) {
bar->setSupplier(scripting::create_number_supplier(
env, element.attr("supplier").getText(), file));
env, element.attr("supplier").getText(), file
));
}
if (element.has("track-color")) {
bar->setTrackColor(element.attr("track-color").asColor());
@ -567,14 +586,11 @@ static std::shared_ptr<UINode> read_input_bind_box(
UiXmlReader& reader, const xml::xmlelement& element
) {
auto bindname = element.attr("binding").getText();
auto found = Events::bindings.find(bindname);
if (found == Events::bindings.end()) {
throw std::runtime_error("binding does not exists "+util::quote(bindname));
}
auto& found = reader.getGUI().getInput().getBindings().require(bindname);
glm::vec4 padding = element.attr("padding", "6").asVec4();
auto bindbox = std::make_shared<InputBindBox>(found->second, padding);
auto bindbox =
std::make_shared<InputBindBox>(reader.getGUI(), found, padding);
read_panel_impl(reader, element, *bindbox);
return bindbox;
}
@ -585,11 +601,12 @@ static slotcallback read_slot_func(
const std::string& attr
) {
auto consumer = scripting::create_int_array_consumer(
reader.getEnvironment(),
element.attr(attr).getText()
reader.getEnvironment(), element.attr(attr).getText()
);
return [=](uint slot, ItemStack&) {
int args[] {int(view->getInventory()->getId()), int(slot)};
int args[] {
static_cast<int>(view->getInventory()->getId()),
static_cast<int>(slot)};
consumer(args, 2);
};
}
@ -601,7 +618,9 @@ static void readSlot(
bool itemSource = element.attr("item-source", "false").asBool();
bool taking = element.attr("taking", "true").asBool();
bool placing = element.attr("placing", "true").asBool();
SlotLayout layout(index, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr);
SlotLayout layout(
index, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr
);
if (element.has("pos")) {
layout.position = element.attr("pos").asVec2();
}
@ -612,7 +631,8 @@ static void readSlot(
layout.shareFunc = read_slot_func(view, reader, element, "sharefunc");
}
if (element.has("onrightclick")) {
layout.rightClick = read_slot_func(view, reader, element, "onrightclick");
layout.rightClick =
read_slot_func(view, reader, element, "onrightclick");
}
layout.taking = taking;
layout.placing = placing;
@ -622,7 +642,9 @@ static void readSlot(
}
static void readSlotsGrid(
InventoryView* view, const UiXmlReader& reader, const xml::xmlelement& element
InventoryView* view,
const UiXmlReader& reader,
const xml::xmlelement& element
) {
int startIndex = element.attr("start-index", "0").asInt();
int rows = element.attr("rows", "0").asInt();
@ -647,7 +669,9 @@ static void readSlotsGrid(
count = rows * cols;
}
bool itemSource = element.attr("item-source", "false").asBool();
SlotLayout layout(-1, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr);
SlotLayout layout(
-1, glm::vec2(), true, itemSource, nullptr, nullptr, nullptr
);
if (element.has("pos")) {
layout.position = element.attr("pos").asVec2();
}
@ -658,7 +682,8 @@ static void readSlotsGrid(
layout.shareFunc = read_slot_func(view, reader, element, "sharefunc");
}
if (element.has("onrightclick")) {
layout.rightClick = read_slot_func(view, reader, element, "onrightclick");
layout.rightClick =
read_slot_func(view, reader, element, "onrightclick");
}
layout.padding = padding;
layout.taking = taking;
@ -674,7 +699,7 @@ static void readSlotsGrid(
slotLayout.index = startIndex + idx;
slotLayout.position += glm::vec2(
padding + col * (slotSize + interval),
padding + (rows-row-1) * (slotSize + interval)
padding + (rows - row - 1) * (slotSize + interval)
);
auto slot = view->addSlot(slotLayout);
view->add(slot, slotLayout.position);
@ -685,8 +710,8 @@ static void readSlotsGrid(
static std::shared_ptr<UINode> read_inventory(
UiXmlReader& reader, const xml::xmlelement& element
) {
auto view = std::make_shared<InventoryView>();
view->setColor(glm::vec4(0.122f, 0.122f, 0.122f, 0.878f)); // todo: fixme
auto view = std::make_shared<InventoryView>(reader.getGUI());
view->setColor(glm::vec4(0.122f, 0.122f, 0.122f, 0.878f)); // TODO: fixme
reader.addIgnore("slot");
reader.addIgnore("slots-grid");
reader.readUINode(reader, element, *view);
@ -704,16 +729,15 @@ static std::shared_ptr<UINode> read_inventory(
static std::shared_ptr<UINode> read_page_box(
UiXmlReader& reader, const xml::xmlelement& element
) {
auto menu = std::make_shared<Menu>();
menu->setPageLoader(
Engine::getInstance().getGUI()->getMenu()->getPageLoader()
);
auto& gui = reader.getGUI();
auto menu = std::make_shared<Menu>(gui);
menu->setPageLoader(gui.getMenu()->getPageLoader());
read_container_impl(reader, element, *menu);
return menu;
}
UiXmlReader::UiXmlReader(const scriptenv& env) : env(env) {
UiXmlReader::UiXmlReader(gui::GUI& gui, const scriptenv& env) : gui(gui), env(env) {
contextStack.emplace("");
add("image", read_image);
add("canvas", read_canvas);
@ -742,16 +766,15 @@ void UiXmlReader::addIgnore(const std::string& tag) {
ignored.insert(tag);
}
std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element) {
std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element
) {
if (element.has("if")) {
const auto& cond = element.attr("if").getText();
if (cond.empty() || cond == "false" || cond == "nil")
return nullptr;
if (cond.empty() || cond == "false" || cond == "nil") return nullptr;
}
if (element.has("ifnot")) {
const auto& cond = element.attr("ifnot").getText();
if (!(cond.empty() || cond == "false" || cond == "nil"))
return nullptr;
if (!(cond.empty() || cond == "false" || cond == "nil")) return nullptr;
}
const std::string& tag = element.getTag();
@ -760,7 +783,7 @@ std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element)
if (ignored.find(tag) != ignored.end()) {
return nullptr;
}
throw std::runtime_error("unsupported element '"+tag+"'");
throw std::runtime_error("unsupported element '" + tag + "'");
}
bool hascontext = element.has("context");
@ -775,8 +798,7 @@ std::shared_ptr<UINode> UiXmlReader::readUINode(const xml::xmlelement& element)
}
std::shared_ptr<UINode> UiXmlReader::readXML(
const std::string& filename,
const std::string& source
const std::string& filename, const std::string& source
) {
this->filename = filename;
auto document = xml::parse(filename, source);
@ -784,8 +806,7 @@ std::shared_ptr<UINode> UiXmlReader::readXML(
}
std::shared_ptr<UINode> UiXmlReader::readXML(
const std::string& filename,
const xml::xmlelement& root
const std::string& filename, const xml::xmlelement& root
) {
this->filename = filename;
return readUINode(root);
@ -802,3 +823,7 @@ const std::string& UiXmlReader::getFilename() const {
const scriptenv& UiXmlReader::getEnvironment() const {
return env;
}
gui::GUI& UiXmlReader::getGUI() const {
return gui;
}

Some files were not shown because too many files have changed in this diff Show More