mirror of https://github.com/AxioDL/metaforce.git
Compile-time locale refactor
This commit is contained in:
parent
afb1162c43
commit
70ce423d00
|
@ -37,7 +37,6 @@ list(APPEND SPECTER_HEADERS
|
||||||
include/specter/FileBrowser.hpp
|
include/specter/FileBrowser.hpp
|
||||||
include/specter/Icon.hpp
|
include/specter/Icon.hpp
|
||||||
include/specter/FontCache.hpp
|
include/specter/FontCache.hpp
|
||||||
include/specter/Translator.hpp
|
|
||||||
include/specter/genie.hpp)
|
include/specter/genie.hpp)
|
||||||
|
|
||||||
list(APPEND SPECTER_SOURCES
|
list(APPEND SPECTER_SOURCES
|
||||||
|
@ -61,8 +60,7 @@ list(APPEND SPECTER_SOURCES
|
||||||
lib/PathButtons.cpp
|
lib/PathButtons.cpp
|
||||||
lib/FileBrowser.cpp
|
lib/FileBrowser.cpp
|
||||||
lib/Icon.cpp
|
lib/Icon.cpp
|
||||||
lib/FontCache.cpp
|
lib/FontCache.cpp)
|
||||||
lib/Translator.cpp)
|
|
||||||
|
|
||||||
add_library(specter ${SPECTER_SOURCES} ${SPECTER_HEADERS})
|
add_library(specter ${SPECTER_SOURCES} ${SPECTER_HEADERS})
|
||||||
target_link_libraries(specter PUBLIC specter-fonts freetype hecl-full zeus)
|
target_link_libraries(specter PUBLIC specter-fonts freetype hecl-full zeus)
|
||||||
|
|
|
@ -78,7 +78,7 @@ private:
|
||||||
FileBrowser& m_browser;
|
FileBrowser& m_browser;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
FileFieldBind(FileBrowser& browser, const IViewManager& vm)
|
FileFieldBind(FileBrowser& browser, const IViewManager& vm)
|
||||||
: m_browser(browser), m_name(vm.translateOr("file_name", "File Name")) {}
|
: m_browser(browser), m_name(vm.translate<locale::file_name>()) {}
|
||||||
std::string_view name(const Control* control) const { return m_name; }
|
std::string_view name(const Control* control) const { return m_name; }
|
||||||
void changed(const Control* control, std::string_view val) {}
|
void changed(const Control* control, std::string_view val) {}
|
||||||
} m_fileFieldBind;
|
} m_fileFieldBind;
|
||||||
|
@ -196,12 +196,12 @@ private:
|
||||||
void rowActivated(size_t rIdx) { m_fb.okActivated(false); }
|
void rowActivated(size_t rIdx) { m_fb.okActivated(false); }
|
||||||
|
|
||||||
FileListingDataBind(FileBrowser& fb, const IViewManager& vm) : m_fb(fb) {
|
FileListingDataBind(FileBrowser& fb, const IViewManager& vm) : m_fb(fb) {
|
||||||
m_nameCol = vm.translateOr("name", "Name");
|
m_nameCol = vm.translate<locale::name>();
|
||||||
m_typeCol = vm.translateOr("type", "Type");
|
m_typeCol = vm.translate<locale::type>();
|
||||||
m_sizeCol = vm.translateOr("size", "Size");
|
m_sizeCol = vm.translate<locale::size>();
|
||||||
m_dirStr = vm.translateOr("directory", "Directory");
|
m_dirStr = vm.translate<locale::directory>();
|
||||||
m_projStr = vm.translateOr("hecl_project", "HECL Project");
|
m_projStr = vm.translate<locale::hecl_project>();
|
||||||
m_fileStr = vm.translateOr("file", "File");
|
m_fileStr = vm.translate<locale::file>();
|
||||||
}
|
}
|
||||||
|
|
||||||
} m_fileListingBind;
|
} m_fileListingBind;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Translator.hpp"
|
#include <locale.hpp>
|
||||||
#include "SplitView.hpp"
|
#include "SplitView.hpp"
|
||||||
#include <hecl/hecl.hpp>
|
#include <hecl/hecl.hpp>
|
||||||
|
|
||||||
|
@ -9,12 +9,10 @@ struct ISpaceController;
|
||||||
|
|
||||||
struct IViewManager {
|
struct IViewManager {
|
||||||
public:
|
public:
|
||||||
virtual const Translator* getTranslator() const { return nullptr; }
|
virtual locale::ELocale getTranslatorLocale() const { return locale::ELocale::en_US; }
|
||||||
virtual std::string_view translateOr(std::string_view key, std::string_view vor) const {
|
template <typename Key, typename... Args>
|
||||||
const Translator* trans = getTranslator();
|
constexpr auto translate(Args&&... args) const {
|
||||||
if (trans)
|
return locale::Translate<Key>(getTranslatorLocale(), std::forward<Args>(args)...);
|
||||||
return trans->translateOr(key, vor);
|
|
||||||
return vor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void deferSpaceSplit(ISpaceController* split, SplitView::Axis axis, int thisSlot,
|
virtual void deferSpaceSplit(ISpaceController* split, SplitView::Axis axis, int thisSlot,
|
||||||
|
|
|
@ -32,8 +32,8 @@ private:
|
||||||
std::string m_leftName, m_rightName;
|
std::string m_leftName, m_rightName;
|
||||||
SideButtonBinding(ScrollView& sv, IViewManager& vm)
|
SideButtonBinding(ScrollView& sv, IViewManager& vm)
|
||||||
: m_sv(sv)
|
: m_sv(sv)
|
||||||
, m_leftName(vm.translateOr("scroll_left", "Scroll Left"))
|
, m_leftName(vm.translate<locale::scroll_left>())
|
||||||
, m_rightName(vm.translateOr("scroll_right", "Scroll Right")) {}
|
, m_rightName(vm.translate<locale::scroll_right>()) {}
|
||||||
std::string_view name(const Control* control) const {
|
std::string_view name(const Control* control) const {
|
||||||
return (control == reinterpret_cast<Control*>(m_sv.m_sideButtons[0].m_view.get())) ? m_leftName.c_str()
|
return (control == reinterpret_cast<Control*>(m_sv.m_sideButtons[0].m_view.get())) ? m_leftName.c_str()
|
||||||
: m_rightName.c_str();
|
: m_rightName.c_str();
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <athena/DNAYaml.hpp>
|
|
||||||
|
|
||||||
namespace specter {
|
|
||||||
|
|
||||||
class Locale {
|
|
||||||
std::string_view m_name;
|
|
||||||
std::string_view m_fullName;
|
|
||||||
std::unique_ptr<athena::io::YAMLNode> m_rootNode;
|
|
||||||
const athena::io::YAMLNode* m_langNode;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Locale(std::string_view name, std::string_view fullName, const unsigned char* yamlSource, size_t yamlLength);
|
|
||||||
std::string_view name() const { return m_name; }
|
|
||||||
std::string_view fullName() const { return m_fullName; }
|
|
||||||
const athena::io::YAMLNode* rootNode() const { return m_langNode; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class Translator {
|
|
||||||
const Locale* m_targetLocale;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Translator(const Locale* targetLocale) { setLocale(targetLocale); }
|
|
||||||
void setLocale(const Locale* targetLocale);
|
|
||||||
std::string_view translate(std::string_view key) const;
|
|
||||||
std::string_view translateOr(std::string_view key, std::string_view vor) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace specter
|
|
|
@ -365,7 +365,7 @@ void Button::MenuTarget::mouseDown(const boo::SWindowCoord& coord, boo::EMouseBu
|
||||||
m_pressed = true;
|
m_pressed = true;
|
||||||
setPressed();
|
setPressed();
|
||||||
if (m_hovered) {
|
if (m_hovered) {
|
||||||
Log.report(logvisor::Info, "button menu '%s' activated", m_button.m_textStr.c_str());
|
Log.report(logvisor::Info, fmt("button menu '{}' activated"), m_button.m_textStr);
|
||||||
if (m_button.m_controlBinding) {
|
if (m_button.m_controlBinding) {
|
||||||
m_button.m_modalMenu.m_view = static_cast<IButtonBinding&>(*m_button.m_controlBinding).buildMenu(&m_button);
|
m_button.m_modalMenu.m_view = static_cast<IButtonBinding&>(*m_button.m_controlBinding).buildMenu(&m_button);
|
||||||
rootView().setActiveMenuButton(&m_button);
|
rootView().setActiveMenuButton(&m_button);
|
||||||
|
@ -385,7 +385,7 @@ void Button::ButtonTarget::mouseUp(const boo::SWindowCoord& coord, boo::EMouseBu
|
||||||
if (m_button.m_controlBinding)
|
if (m_button.m_controlBinding)
|
||||||
static_cast<IButtonBinding&>(*m_button.m_controlBinding).up(&m_button, coord);
|
static_cast<IButtonBinding&>(*m_button.m_controlBinding).up(&m_button, coord);
|
||||||
if (m_hovered) {
|
if (m_hovered) {
|
||||||
Log.report(logvisor::Info, "button '%s' activated", m_button.m_textStr.c_str());
|
Log.report(logvisor::Info, fmt("button '{}' activated"), m_button.m_textStr);
|
||||||
if (m_button.m_controlBinding)
|
if (m_button.m_controlBinding)
|
||||||
static_cast<IButtonBinding&>(*m_button.m_controlBinding).activated(&m_button, coord);
|
static_cast<IButtonBinding&>(*m_button.m_controlBinding).activated(&m_button, coord);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, std::string_view
|
||||||
, m_left(*this, res)
|
, m_left(*this, res)
|
||||||
, m_right(*this, res)
|
, m_right(*this, res)
|
||||||
, m_ok(*this, res, title)
|
, m_ok(*this, res, title)
|
||||||
, m_cancel(*this, res, rootView().viewManager().translateOr("cancel", "Cancel"))
|
, m_cancel(*this, res, rootView().viewManager().translate<locale::cancel>())
|
||||||
, m_fileFieldBind(*this, rootView().viewManager())
|
, m_fileFieldBind(*this, rootView().viewManager())
|
||||||
, m_fileListingBind(*this, rootView().viewManager())
|
, m_fileListingBind(*this, rootView().viewManager())
|
||||||
, m_systemBookmarkBind(*this)
|
, m_systemBookmarkBind(*this)
|
||||||
|
@ -61,15 +61,15 @@ FileBrowser::FileBrowser(ViewResources& res, View& parentView, std::string_view
|
||||||
m_fileListing.m_view.reset(new Table(res, *this, &m_fileListingBind, &m_fileListingBind, 3));
|
m_fileListing.m_view.reset(new Table(res, *this, &m_fileListingBind, &m_fileListingBind, 3));
|
||||||
m_systemBookmarks.m_view.reset(new Table(res, *this, &m_systemBookmarkBind, &m_systemBookmarkBind, 1));
|
m_systemBookmarks.m_view.reset(new Table(res, *this, &m_systemBookmarkBind, &m_systemBookmarkBind, 1));
|
||||||
m_systemBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
m_systemBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
||||||
m_systemBookmarksLabel->typesetGlyphs(vm.translateOr("system_locations", "System Locations"),
|
m_systemBookmarksLabel->typesetGlyphs(vm.translate<locale::system_locations>(),
|
||||||
res.themeData().uiText());
|
res.themeData().uiText());
|
||||||
m_projectBookmarks.m_view.reset(new Table(res, *this, &m_projectBookmarkBind, &m_projectBookmarkBind, 1));
|
m_projectBookmarks.m_view.reset(new Table(res, *this, &m_projectBookmarkBind, &m_projectBookmarkBind, 1));
|
||||||
m_projectBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
m_projectBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
||||||
m_projectBookmarksLabel->typesetGlyphs(vm.translateOr("recent_projects", "Recent Projects"),
|
m_projectBookmarksLabel->typesetGlyphs(vm.translate<locale::recent_projects>(),
|
||||||
res.themeData().uiText());
|
res.themeData().uiText());
|
||||||
m_recentBookmarks.m_view.reset(new Table(res, *this, &m_recentBookmarkBind, &m_recentBookmarkBind, 1));
|
m_recentBookmarks.m_view.reset(new Table(res, *this, &m_recentBookmarkBind, &m_recentBookmarkBind, 1));
|
||||||
m_recentBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
m_recentBookmarksLabel.reset(new TextView(res, *this, res.m_mainFont));
|
||||||
m_recentBookmarksLabel->typesetGlyphs(vm.translateOr("recent_files", "Recent Files"), res.themeData().uiText());
|
m_recentBookmarksLabel->typesetGlyphs(vm.translate<locale::recent_files>(), res.themeData().uiText());
|
||||||
|
|
||||||
/* Populate system bookmarks */
|
/* Populate system bookmarks */
|
||||||
std::vector<std::pair<hecl::SystemString, std::string>> systemLocs = hecl::GetSystemLocations();
|
std::vector<std::pair<hecl::SystemString, std::string>> systemLocs = hecl::GetSystemLocations();
|
||||||
|
@ -184,8 +184,7 @@ void FileBrowser::okActivated(bool viaButton) {
|
||||||
hecl::Sstat theStat;
|
hecl::Sstat theStat;
|
||||||
if (hecl::Stat(path.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
|
if (hecl::Stat(path.c_str(), &theStat) || !S_ISDIR(theStat.st_mode)) {
|
||||||
hecl::SystemUTF8Conv utf8(path);
|
hecl::SystemUTF8Conv utf8(path);
|
||||||
m_fileField.m_view->setErrorState(
|
m_fileField.m_view->setErrorState(vm.translate<locale::no_access_as_dir>(utf8));
|
||||||
hecl::Format(vm.translateOr("no_access_as_dir", "Unable to access '%s' as directory").data(), utf8.c_str()));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,13 +194,13 @@ void FileBrowser::okActivated(bool viaButton) {
|
||||||
int err = hecl::Stat(path.c_str(), &theStat);
|
int err = hecl::Stat(path.c_str(), &theStat);
|
||||||
if (m_type == Type::SaveFile) {
|
if (m_type == Type::SaveFile) {
|
||||||
if (m_fileField.m_view->getText().empty()) {
|
if (m_fileField.m_view->getText().empty()) {
|
||||||
m_fileField.m_view->setErrorState(vm.translateOr("file_field_empty", "Unable to save empty file"));
|
m_fileField.m_view->setErrorState(vm.translate<locale::file_field_empty>());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!err && !S_ISDIR(theStat.st_mode)) {
|
if (!err && !S_ISDIR(theStat.st_mode)) {
|
||||||
m_confirmWindow.reset(
|
m_confirmWindow.reset(
|
||||||
new MessageWindow(rootView().viewRes(), *this, MessageWindow::Type::ConfirmOkCancel,
|
new MessageWindow(rootView().viewRes(), *this, MessageWindow::Type::ConfirmOkCancel,
|
||||||
hecl::Format(vm.translateOr("overwrite_confirm", "Overwrite '%s'?").data(), path.c_str()),
|
vm.translate<locale::overwrite_confirm>(path),
|
||||||
[&, path](bool ok) {
|
[&, path](bool ok) {
|
||||||
if (ok) {
|
if (ok) {
|
||||||
m_returnFunc(true, path);
|
m_returnFunc(true, path);
|
||||||
|
@ -222,20 +221,18 @@ void FileBrowser::okActivated(bool viaButton) {
|
||||||
return;
|
return;
|
||||||
} else if (m_type == Type::SaveDirectory || m_type == Type::NewHECLProject) {
|
} else if (m_type == Type::SaveDirectory || m_type == Type::NewHECLProject) {
|
||||||
if (m_fileField.m_view->getText().empty()) {
|
if (m_fileField.m_view->getText().empty()) {
|
||||||
m_fileField.m_view->setErrorState(
|
m_fileField.m_view->setErrorState(vm.translate<locale::directory_field_empty>());
|
||||||
vm.translateOr("directory_field_empty", "Unable to make empty-named directory"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!err && !S_ISDIR(theStat.st_mode)) {
|
if (!err && !S_ISDIR(theStat.st_mode)) {
|
||||||
m_fileField.m_view->setErrorState(vm.translateOr("no_overwrite_file", "Unable to make directory over file"));
|
m_fileField.m_view->setErrorState(vm.translate<locale::no_overwrite_file>());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!err && S_ISDIR(theStat.st_mode)) {
|
if (!err && S_ISDIR(theStat.st_mode)) {
|
||||||
if (m_type == Type::NewHECLProject) {
|
if (m_type == Type::NewHECLProject) {
|
||||||
hecl::ProjectRootPath projRoot = hecl::SearchForProject(path);
|
hecl::ProjectRootPath projRoot = hecl::SearchForProject(path);
|
||||||
if (projRoot) {
|
if (projRoot) {
|
||||||
m_fileField.m_view->setErrorState(
|
m_fileField.m_view->setErrorState(vm.translate<locale::no_overwrite_project>());
|
||||||
vm.translateOr("no_overwrite_project", "Unable to make project within existing project"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,8 +250,7 @@ void FileBrowser::okActivated(bool viaButton) {
|
||||||
return;
|
return;
|
||||||
} else if (err || !S_ISREG(theStat.st_mode)) {
|
} else if (err || !S_ISREG(theStat.st_mode)) {
|
||||||
hecl::SystemUTF8Conv utf8(path);
|
hecl::SystemUTF8Conv utf8(path);
|
||||||
m_fileField.m_view->setErrorState(
|
m_fileField.m_view->setErrorState(vm.translate<locale::no_access_as_file>(utf8));
|
||||||
hecl::Format(vm.translateOr("no_access_as_file", "Unable to access '%s' as file").data(), utf8.c_str()));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_returnFunc(true, path);
|
m_returnFunc(true, path);
|
||||||
|
@ -275,8 +271,7 @@ void FileBrowser::okActivated(bool viaButton) {
|
||||||
}
|
}
|
||||||
if (err || !S_ISDIR(theStat.st_mode)) {
|
if (err || !S_ISDIR(theStat.st_mode)) {
|
||||||
hecl::SystemUTF8Conv utf8(path);
|
hecl::SystemUTF8Conv utf8(path);
|
||||||
m_fileField.m_view->setErrorState(
|
m_fileField.m_view->setErrorState(vm.translate<locale::no_access_as_dir>(utf8));
|
||||||
hecl::Format(vm.translateOr("no_access_as_dir", "Unable to access '%s' as directory").data(), utf8.c_str()));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_returnFunc(true, path);
|
m_returnFunc(true, path);
|
||||||
|
|
|
@ -60,12 +60,12 @@ void FreeTypeGZipMemFace::open() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (FT_Stream_OpenGzip(&m_decomp, &m_comp))
|
if (FT_Stream_OpenGzip(&m_decomp, &m_comp))
|
||||||
Log.report(logvisor::Fatal, "unable to open FreeType gzip stream");
|
Log.report(logvisor::Fatal, fmt("unable to open FreeType gzip stream"));
|
||||||
|
|
||||||
FT_Open_Args args = {FT_OPEN_STREAM, nullptr, 0, nullptr, &m_decomp};
|
FT_Open_Args args = {FT_OPEN_STREAM, nullptr, 0, nullptr, &m_decomp};
|
||||||
|
|
||||||
if (FT_Open_Face(m_lib, &args, 0, &m_face))
|
if (FT_Open_Face(m_lib, &args, 0, &m_face))
|
||||||
Log.report(logvisor::Fatal, "unable to open FreeType gzip face");
|
Log.report(logvisor::Fatal, fmt("unable to open FreeType gzip face"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeTypeGZipMemFace::close() {
|
void FreeTypeGZipMemFace::close() {
|
||||||
|
@ -116,7 +116,7 @@ static void MemcpyRect(RgbaPixel* img, const FT_Bitmap* bmp, unsigned slice, uns
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void GridFitGlyph(FT_GlyphSlot slot, FT_UInt& width, FT_UInt& height) {
|
static void GridFitGlyph(FT_GlyphSlot slot, FT_UInt& width, FT_UInt& height) {
|
||||||
width = slot->metrics.width >> 6;
|
width = slot->metrics.width >> 6;
|
||||||
height = slot->metrics.height >> 6;
|
height = slot->metrics.height >> 6;
|
||||||
}
|
}
|
||||||
|
@ -585,7 +585,7 @@ boo::ObjToken<boo::ITextureSA> FontAtlas::texture(boo::IGraphicsDataFactory* gf)
|
||||||
FontCache::Library::Library() {
|
FontCache::Library::Library() {
|
||||||
FT_Error err = FT_Init_FreeType(&m_lib);
|
FT_Error err = FT_Init_FreeType(&m_lib);
|
||||||
if (err)
|
if (err)
|
||||||
Log.report(logvisor::Fatal, "unable to FT_Init_FreeType");
|
Log.report(logvisor::Fatal, fmt("unable to FT_Init_FreeType"));
|
||||||
}
|
}
|
||||||
|
|
||||||
FontCache::Library::~Library() { FT_Done_FreeType(m_lib); }
|
FontCache::Library::~Library() { FT_Done_FreeType(m_lib); }
|
||||||
|
@ -603,10 +603,10 @@ FontTag FontCache::prepCustomFont(std::string_view name, FT_Face face, FCharFilt
|
||||||
uint32_t dpi) {
|
uint32_t dpi) {
|
||||||
/* Quick validation */
|
/* Quick validation */
|
||||||
if (!face)
|
if (!face)
|
||||||
Log.report(logvisor::Fatal, "invalid freetype face");
|
Log.report(logvisor::Fatal, fmt("invalid freetype face"));
|
||||||
|
|
||||||
if (!face->charmap || face->charmap->encoding != FT_ENCODING_UNICODE)
|
if (!face->charmap || face->charmap->encoding != FT_ENCODING_UNICODE)
|
||||||
Log.report(logvisor::Fatal, "font does not contain a unicode char map");
|
Log.report(logvisor::Fatal, fmt("font does not contain a unicode char map"));
|
||||||
|
|
||||||
/* Set size with FreeType */
|
/* Set size with FreeType */
|
||||||
FT_Set_Char_Size(face, 0, points * 64.0, 0, dpi);
|
FT_Set_Char_Size(face, 0, points * 64.0, 0, dpi);
|
||||||
|
@ -618,7 +618,7 @@ FontTag FontCache::prepCustomFont(std::string_view name, FT_Face face, FCharFilt
|
||||||
return tag;
|
return tag;
|
||||||
|
|
||||||
/* Now check filesystem cache */
|
/* Now check filesystem cache */
|
||||||
hecl::SystemString cachePath = m_cacheRoot + _SYS_STR('/') + hecl::SysFormat(_SYS_STR("%" PRIx64), tag.hash());
|
hecl::SystemString cachePath = m_cacheRoot + _SYS_STR('/') + fmt::format(fmt(_SYS_STR("{}")), tag.hash());
|
||||||
hecl::Sstat st;
|
hecl::Sstat st;
|
||||||
if (!hecl::Stat(cachePath.c_str(), &st) && S_ISREG(st.st_mode)) {
|
if (!hecl::Stat(cachePath.c_str(), &st) && S_ISREG(st.st_mode)) {
|
||||||
athena::io::FileReader r(cachePath);
|
athena::io::FileReader r(cachePath);
|
||||||
|
@ -637,7 +637,7 @@ FontTag FontCache::prepCustomFont(std::string_view name, FT_Face face, FCharFilt
|
||||||
/* Nada, build and cache now */
|
/* Nada, build and cache now */
|
||||||
athena::io::FileWriter w(cachePath);
|
athena::io::FileWriter w(cachePath);
|
||||||
if (w.hasError())
|
if (w.hasError())
|
||||||
Log.report(logvisor::Fatal, "unable to open '%s' for writing", cachePath.c_str());
|
Log.report(logvisor::Fatal, fmt("unable to open '{}' for writing"), cachePath);
|
||||||
w.writeUint32Big('FONT');
|
w.writeUint32Big('FONT');
|
||||||
m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(face, dpi, subpixel, filter, w));
|
m_cachedAtlases.emplace(tag, std::make_unique<FontAtlas>(face, dpi, subpixel, filter, w));
|
||||||
return tag;
|
return tag;
|
||||||
|
@ -646,7 +646,7 @@ FontTag FontCache::prepCustomFont(std::string_view name, FT_Face face, FCharFilt
|
||||||
const FontAtlas& FontCache::lookupAtlas(FontTag tag) const {
|
const FontAtlas& FontCache::lookupAtlas(FontTag tag) const {
|
||||||
auto search = m_cachedAtlases.find(tag);
|
auto search = m_cachedAtlases.find(tag);
|
||||||
if (search == m_cachedAtlases.cend())
|
if (search == m_cachedAtlases.cend())
|
||||||
Log.report(logvisor::Fatal, "invalid font");
|
Log.report(logvisor::Fatal, fmt("invalid font"));
|
||||||
return *search->second.get();
|
return *search->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ MessageWindow::MessageWindow(ViewResources& res, View& parentView, Type type, st
|
||||||
type == Type::ErrorOk ? res.themeData().splashErrorBackground() : res.themeData().splashBackground())
|
type == Type::ErrorOk ? res.themeData().splashErrorBackground() : res.themeData().splashBackground())
|
||||||
, m_type(type)
|
, m_type(type)
|
||||||
, m_func(func)
|
, m_func(func)
|
||||||
, m_okBind(*this, rootView().viewManager().translateOr("ok", "OK"))
|
, m_okBind(*this, rootView().viewManager().translate<locale::ok>())
|
||||||
, m_cancelBind(*this, rootView().viewManager().translateOr("cancel", "Cancel")) {
|
, m_cancelBind(*this, rootView().viewManager().translate<locale::cancel>()) {
|
||||||
m_text.reset(new MultiLineTextView(res, *this, res.m_mainFont, TextView::Alignment::Center));
|
m_text.reset(new MultiLineTextView(res, *this, res.m_mainFont, TextView::Alignment::Center));
|
||||||
m_text->typesetGlyphs(message, res.themeData().uiText(), 380 * res.pixelFactor());
|
m_text->typesetGlyphs(message, res.themeData().uiText(), 380 * res.pixelFactor());
|
||||||
constraint() = RectangleConstraint(400 * res.pixelFactor(), 80 * res.pixelFactor() + m_text->nominalHeight());
|
constraint() = RectangleConstraint(400 * res.pixelFactor(), 80 * res.pixelFactor() + m_text->nominalHeight());
|
||||||
|
|
|
@ -19,7 +19,7 @@ std::string MultiLineTextView::LineWrap(std::string_view str, int wrap) {
|
||||||
utf8proc_int32_t ch;
|
utf8proc_int32_t ch;
|
||||||
utf8proc_ssize_t sz = utf8proc_iterate(it, -1, &ch);
|
utf8proc_ssize_t sz = utf8proc_iterate(it, -1, &ch);
|
||||||
if (sz < 0)
|
if (sz < 0)
|
||||||
Log.report(logvisor::Fatal, "invalid UTF-8 char");
|
Log.report(logvisor::Fatal, fmt("invalid UTF-8 char"));
|
||||||
if (ch == '\n') {
|
if (ch == '\n') {
|
||||||
ret += '\n';
|
ret += '\n';
|
||||||
lCh = -1;
|
lCh = -1;
|
||||||
|
@ -147,7 +147,7 @@ void MultiLineTextView::typesetGlyphs(std::string_view str, const zeus::CColor&
|
||||||
utf8proc_int32_t ch;
|
utf8proc_int32_t ch;
|
||||||
utf8proc_ssize_t sz = utf8proc_iterate(it, -1, &ch);
|
utf8proc_ssize_t sz = utf8proc_iterate(it, -1, &ch);
|
||||||
if (sz < 0)
|
if (sz < 0)
|
||||||
Log.report(logvisor::Fatal, "invalid UTF-8 char");
|
Log.report(logvisor::Fatal, fmt("invalid UTF-8 char"));
|
||||||
if (ch == '\n' || ch == '\0')
|
if (ch == '\n' || ch == '\0')
|
||||||
++lineCount;
|
++lineCount;
|
||||||
rem -= sz;
|
rem -= sz;
|
||||||
|
|
|
@ -23,7 +23,7 @@ RootView::~RootView() { m_window->setCallback(nullptr); }
|
||||||
|
|
||||||
RootView::SplitMenuSystem::SplitMenuSystem(RootView& rv, boo::IGraphicsDataFactory::Context& ctx)
|
RootView::SplitMenuSystem::SplitMenuSystem(RootView& rv, boo::IGraphicsDataFactory::Context& ctx)
|
||||||
: m_rv(rv)
|
: m_rv(rv)
|
||||||
, m_text(rv.m_viewMan.translateOr("boundary_action", "Boundary Action"))
|
, m_text(rv.m_viewMan.translate<locale::boundary_action>())
|
||||||
, m_splitActionNode(*this)
|
, m_splitActionNode(*this)
|
||||||
, m_joinActionNode(*this) {
|
, m_joinActionNode(*this) {
|
||||||
ViewResources& res = *rv.m_viewRes;
|
ViewResources& res = *rv.m_viewRes;
|
||||||
|
@ -76,10 +76,10 @@ RootView::SplitMenuSystem::SplitMenuSystem(RootView& rv, boo::IGraphicsDataFacto
|
||||||
}
|
}
|
||||||
|
|
||||||
RootView::SplitMenuSystem::SplitActionNode::SplitActionNode(SplitMenuSystem& smn)
|
RootView::SplitMenuSystem::SplitActionNode::SplitActionNode(SplitMenuSystem& smn)
|
||||||
: m_smn(smn), m_text(smn.m_rv.m_viewMan.translateOr("split", "Split")) {}
|
: m_smn(smn), m_text(smn.m_rv.m_viewMan.translate<locale::split>()) {}
|
||||||
|
|
||||||
RootView::SplitMenuSystem::JoinActionNode::JoinActionNode(SplitMenuSystem& smn)
|
RootView::SplitMenuSystem::JoinActionNode::JoinActionNode(SplitMenuSystem& smn)
|
||||||
: m_smn(smn), m_text(smn.m_rv.m_viewMan.translateOr("join", "Join")) {}
|
: m_smn(smn), m_text(smn.m_rv.m_viewMan.translate<locale::join>()) {}
|
||||||
|
|
||||||
void RootView::SplitMenuSystem::setArrowVerts(const boo::SWindowRect& rect, SplitView::ArrowDir dir) {
|
void RootView::SplitMenuSystem::setArrowVerts(const boo::SWindowRect& rect, SplitView::ArrowDir dir) {
|
||||||
const boo::SWindowRect& root = m_rv.subRect();
|
const boo::SWindowRect& root = m_rv.subRect();
|
||||||
|
|
|
@ -31,7 +31,7 @@ SplitView::SplitView(ViewResources& res, View& parentView, ISplitSpaceController
|
||||||
|
|
||||||
View* SplitView::setContentView(int slot, View* view) {
|
View* SplitView::setContentView(int slot, View* view) {
|
||||||
if (slot < 0 || slot > 1)
|
if (slot < 0 || slot > 1)
|
||||||
Log.report(logvisor::Fatal, "out-of-range slot to RootView::SplitView::setContentView");
|
Log.report(logvisor::Fatal, fmt("out-of-range slot to RootView::SplitView::setContentView"));
|
||||||
View* ret = m_views[slot].m_view;
|
View* ret = m_views[slot].m_view;
|
||||||
m_views[slot].m_view = view;
|
m_views[slot].m_view = view;
|
||||||
m_views[slot].m_mouseDown = 0;
|
m_views[slot].m_mouseDown = 0;
|
||||||
|
|
|
@ -17,7 +17,7 @@ Table::Table(ViewResources& res, View& parentView, ITableDataBinding* data, ITab
|
||||||
, m_hVerts(new SolidShaderVert[maxColumns * 6])
|
, m_hVerts(new SolidShaderVert[maxColumns * 6])
|
||||||
, m_rowsView(*this, res) {
|
, m_rowsView(*this, res) {
|
||||||
if (!maxColumns)
|
if (!maxColumns)
|
||||||
Log.report(logvisor::Fatal, "0-column tables not supported");
|
Log.report(logvisor::Fatal, fmt("0-column tables not supported"));
|
||||||
|
|
||||||
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
m_scroll.m_view.reset(new ScrollView(res, *this, ScrollView::Style::ThinIndicator));
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ void Table::RowsView::_setRowVerts(const boo::SWindowRect& sub, const boo::SWind
|
||||||
|
|
||||||
void Table::cycleSortColumn(size_t c) {
|
void Table::cycleSortColumn(size_t c) {
|
||||||
if (c >= m_columns)
|
if (c >= m_columns)
|
||||||
Log.report(logvisor::Fatal, "cycleSortColumn out of bounds (%" PRISize ", %" PRISize ")", c, m_columns);
|
Log.report(logvisor::Fatal, fmt("cycleSortColumn out of bounds ({}, {})"), c, m_columns);
|
||||||
if (m_state) {
|
if (m_state) {
|
||||||
size_t cIdx;
|
size_t cIdx;
|
||||||
SortDirection dir = m_state->getSort(cIdx);
|
SortDirection dir = m_state->getSort(cIdx);
|
||||||
|
@ -187,7 +187,7 @@ void Table::selectRow(size_t r) {
|
||||||
if (m_inSelectRow)
|
if (m_inSelectRow)
|
||||||
return;
|
return;
|
||||||
if (r >= m_rows && r != SIZE_MAX)
|
if (r >= m_rows && r != SIZE_MAX)
|
||||||
Log.report(logvisor::Fatal, "selectRow out of bounds (%" PRISize ", %" PRISize ")", r, m_rows);
|
Log.report(logvisor::Fatal, fmt("selectRow out of bounds ({}, {})"), r, m_rows);
|
||||||
if (r == m_selectedRow) {
|
if (r == m_selectedRow) {
|
||||||
if (m_state) {
|
if (m_state) {
|
||||||
m_inSelectRow = true;
|
m_inSelectRow = true;
|
||||||
|
|
|
@ -48,7 +48,7 @@ void TextView::_commitResources(size_t capacity) {
|
||||||
TextView::TextView(ViewResources& res, View& parentView, const FontAtlas& font, Alignment align, size_t capacity)
|
TextView::TextView(ViewResources& res, View& parentView, const FontAtlas& font, Alignment align, size_t capacity)
|
||||||
: View(res, parentView), m_capacity(capacity), m_fontAtlas(font), m_align(align) {
|
: View(res, parentView), m_capacity(capacity), m_fontAtlas(font), m_align(align) {
|
||||||
if (size_t(hecl::VertexBufferPool<RenderGlyph>::bucketCapacity()) < capacity)
|
if (size_t(hecl::VertexBufferPool<RenderGlyph>::bucketCapacity()) < capacity)
|
||||||
Log.report(logvisor::Fatal, "bucket overflow [%" PRISize "/%" PRISize "]", capacity,
|
Log.report(logvisor::Fatal, fmt("bucket overflow [{}/{}]"), capacity,
|
||||||
hecl::VertexBufferPool<RenderGlyph>::bucketCapacity());
|
hecl::VertexBufferPool<RenderGlyph>::bucketCapacity());
|
||||||
|
|
||||||
_commitResources(0);
|
_commitResources(0);
|
||||||
|
@ -105,7 +105,7 @@ void TextView::typesetGlyphs(std::string_view str, const zeus::CColor& defaultCo
|
||||||
for (; it.iter() < str.end(); ++it) {
|
for (; it.iter() < str.end(); ++it) {
|
||||||
utf8proc_int32_t ch = *it;
|
utf8proc_int32_t ch = *it;
|
||||||
if (ch == -1) {
|
if (ch == -1) {
|
||||||
Log.report(logvisor::Warning, "invalid UTF-8 char");
|
Log.report(logvisor::Warning, fmt("invalid UTF-8 char"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == '\n' || ch == '\0')
|
if (ch == '\n' || ch == '\0')
|
||||||
|
@ -237,7 +237,7 @@ void TextView::draw(boo::IGraphicsCommandQueue* gfxQ) {
|
||||||
|
|
||||||
std::pair<int, int> TextView::queryGlyphDimensions(size_t pos) const {
|
std::pair<int, int> TextView::queryGlyphDimensions(size_t pos) const {
|
||||||
if (pos >= m_glyphInfo.size())
|
if (pos >= m_glyphInfo.size())
|
||||||
Log.report(logvisor::Fatal, "TextView::queryGlyphWidth(%" PRISize ") out of bounds: %" PRISize, pos,
|
Log.report(logvisor::Fatal, fmt("TextView::queryGlyphWidth({}) out of bounds: {}"), pos,
|
||||||
m_glyphInfo.size());
|
m_glyphInfo.size());
|
||||||
|
|
||||||
return m_glyphInfo[pos].m_dims;
|
return m_glyphInfo[pos].m_dims;
|
||||||
|
@ -260,7 +260,7 @@ size_t TextView::reverseSelectGlyph(int x) const {
|
||||||
|
|
||||||
int TextView::queryReverseAdvance(size_t idx) const {
|
int TextView::queryReverseAdvance(size_t idx) const {
|
||||||
if (idx > m_glyphInfo.size())
|
if (idx > m_glyphInfo.size())
|
||||||
Log.report(logvisor::Fatal, "TextView::queryReverseGlyph(%" PRISize ") out of inclusive bounds: %" PRISize, idx,
|
Log.report(logvisor::Fatal, fmt("TextView::queryReverseGlyph({}) out of inclusive bounds: {}"), idx,
|
||||||
m_glyphInfo.size());
|
m_glyphInfo.size());
|
||||||
if (!idx)
|
if (!idx)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -269,7 +269,7 @@ int TextView::queryReverseAdvance(size_t idx) const {
|
||||||
|
|
||||||
std::pair<size_t, size_t> TextView::queryWholeWordRange(size_t idx) const {
|
std::pair<size_t, size_t> TextView::queryWholeWordRange(size_t idx) const {
|
||||||
if (idx > m_glyphInfo.size())
|
if (idx > m_glyphInfo.size())
|
||||||
Log.report(logvisor::Fatal, "TextView::queryWholeWordRange(%" PRISize ") out of inclusive bounds: %" PRISize, idx,
|
Log.report(logvisor::Fatal, fmt("TextView::queryWholeWordRange({}) out of inclusive bounds: {}"), idx,
|
||||||
m_glyphInfo.size());
|
m_glyphInfo.size());
|
||||||
if (m_glyphInfo.empty())
|
if (m_glyphInfo.empty())
|
||||||
return {0, 0};
|
return {0, 0};
|
||||||
|
|
|
@ -111,7 +111,7 @@ void Toolbar::setVerticalVerts(int height) {
|
||||||
|
|
||||||
void Toolbar::push_back(View* v, unsigned unit) {
|
void Toolbar::push_back(View* v, unsigned unit) {
|
||||||
if (unit >= m_units)
|
if (unit >= m_units)
|
||||||
Log.report(logvisor::Fatal, "unit %u out of range %u", unit, m_units);
|
Log.report(logvisor::Fatal, fmt("unit {} out of range {}"), unit, m_units);
|
||||||
std::vector<ViewChild<View*>>& u = m_children[unit];
|
std::vector<ViewChild<View*>>& u = m_children[unit];
|
||||||
u.emplace_back();
|
u.emplace_back();
|
||||||
u.back().m_view = v;
|
u.back().m_view = v;
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
#include "specter/Translator.hpp"
|
|
||||||
#include "logvisor/logvisor.hpp"
|
|
||||||
|
|
||||||
namespace specter {
|
|
||||||
static logvisor::Module Log("specter::Translator");
|
|
||||||
|
|
||||||
Locale::Locale(std::string_view name, std::string_view fullName, const unsigned char* yamlSource, size_t yamlLength)
|
|
||||||
: m_name(name), m_fullName(fullName) {
|
|
||||||
athena::io::YAMLDocReader reader;
|
|
||||||
yaml_parser_set_input_string(reader.getParser(), yamlSource, yamlLength);
|
|
||||||
reader.parse(nullptr);
|
|
||||||
m_rootNode = reader.releaseRootNode();
|
|
||||||
if (m_rootNode) {
|
|
||||||
m_langNode = m_rootNode->findMapChild(name.data());
|
|
||||||
if (!m_langNode)
|
|
||||||
Log.report(logvisor::Fatal, "no root node '%s' found in locale", name.data());
|
|
||||||
} else
|
|
||||||
Log.report(logvisor::Warning, "locale empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Translator::setLocale(const Locale* targetLocale) {
|
|
||||||
if (!targetLocale)
|
|
||||||
Log.report(logvisor::Fatal, "null locale");
|
|
||||||
m_targetLocale = targetLocale;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string_view RecursiveLookup(const athena::io::YAMLNode* node, std::string_view::const_iterator start,
|
|
||||||
std::string_view::const_iterator end) {
|
|
||||||
for (std::string_view::const_iterator it = start; it != end; ++it) {
|
|
||||||
if (*it == '/') {
|
|
||||||
const athena::io::YAMLNode* ch = node->findMapChild(std::string(start, it));
|
|
||||||
if (!ch)
|
|
||||||
return {};
|
|
||||||
return RecursiveLookup(ch, it + 1, end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const athena::io::YAMLNode* ch = node->findMapChild(std::string(start, end));
|
|
||||||
if (!ch)
|
|
||||||
return {};
|
|
||||||
return ch->m_scalarString;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view Translator::translate(std::string_view key) const {
|
|
||||||
if (!m_targetLocale->rootNode())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
return RecursiveLookup(m_targetLocale->rootNode(), key.cbegin(), key.cend());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view Translator::translateOr(std::string_view key, std::string_view vor) const {
|
|
||||||
std::string_view find = translate(key);
|
|
||||||
if (!find.empty())
|
|
||||||
return find;
|
|
||||||
return vor;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace specter
|
|
|
@ -5,7 +5,7 @@ static logvisor::Module Log("specter::ViewResources");
|
||||||
|
|
||||||
void ViewResources::init(boo::IGraphicsDataFactory* factory, FontCache* fcache, const IThemeData* theme, float pf) {
|
void ViewResources::init(boo::IGraphicsDataFactory* factory, FontCache* fcache, const IThemeData* theme, float pf) {
|
||||||
if (!factory || !fcache || !theme)
|
if (!factory || !fcache || !theme)
|
||||||
Log.report(logvisor::Fatal, "all arguments of ViewResources::init() must be non-null");
|
Log.report(logvisor::Fatal, fmt("all arguments of ViewResources::init() must be non-null"));
|
||||||
m_pixelFactor = pf;
|
m_pixelFactor = pf;
|
||||||
m_factory = factory;
|
m_factory = factory;
|
||||||
m_theme = theme;
|
m_theme = theme;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
include_directories(../../hecl/include
|
include_directories(../../hecl/include
|
||||||
../../hecl/extern/boo/include
|
../../hecl/extern/boo/include
|
||||||
../../hecl/extern/boo/logvisor/include)
|
../../hecl/extern/boo/logvisor/include
|
||||||
|
../../nod/logvisor/fmt/include)
|
||||||
add_shader(SpecterViewShaders)
|
add_shader(SpecterViewShaders)
|
||||||
add_shader(SpecterTextViewShaders)
|
add_shader(SpecterTextViewShaders)
|
||||||
|
|
Loading…
Reference in New Issue