Compile-time locale refactor

This commit is contained in:
Jack Andersen 2019-07-19 18:26:59 -10:00
parent afb1162c43
commit 70ce423d00
18 changed files with 58 additions and 154 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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,

View File

@ -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();

View File

@ -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

View File

@ -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);
} }

View File

@ -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);

View File

@ -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();
} }

View File

@ -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());

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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};

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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)