Lua chat callbacks + better font styling
golly gee whiz!! i hope i didn't fuck something up
This commit is contained in:
parent
d5f5fb5ddf
commit
ca1426eabc
@ -1,28 +1,43 @@
|
||||
{
|
||||
"nametag" : {
|
||||
"showMasterNames" : true,
|
||||
"fontDirectives" : "?border=1;222;2224",
|
||||
// border is double here as it otherwise fades out near the end
|
||||
// (could just the end to opaque too, but then diagonals are more jaggy)
|
||||
"textStyle" : { "backDirectives" : "?border=1;222;2224?border=1;222;2224" },
|
||||
"inspectOpacityRate" : 0.15,
|
||||
"movementThreshold" : 0.5,
|
||||
"offset" : [0, 13]
|
||||
},
|
||||
"font" : {
|
||||
"defaultDirectives" : "",
|
||||
"defaultFont" : ""
|
||||
"specialDamageBar" : {
|
||||
"nameStyle" : {}
|
||||
},
|
||||
"cursorTooltip" : {
|
||||
"font" : ""
|
||||
"textStyle" : {},
|
||||
"buttonTextStyle" : {
|
||||
"shadow" : "black"
|
||||
},
|
||||
"labelTextStyle" : {},
|
||||
"paneTextStyle" : {},
|
||||
"textBoxTextStyle" : {},
|
||||
"itemSlotTextStyle" : {
|
||||
"backDirectives" : "?border=1;444;4444"
|
||||
},
|
||||
"teamBarNameStyle" : {},
|
||||
"cursorTooltip" : { "textStyle" : {} },
|
||||
|
||||
"debugFont" : "iosevka-semibold",
|
||||
"debugFontSize" : 7,
|
||||
"debugFontDirectives" : "?border=1;111a;1114",
|
||||
"debugTextStyle" : {
|
||||
"font" : "iosevka-semibold",
|
||||
"fontSize" : 7,
|
||||
"backDirectives" : "?border=2;111a;1114"
|
||||
},
|
||||
"debugSpatialClearTime" : 0.0,
|
||||
"debugOffset" : [80, 130],
|
||||
|
||||
// Change planet name to support the new internal string formatting.
|
||||
"planetNameFormatString" : "- {} -",
|
||||
"planetNameDirectives" : "?border=4;000;000",
|
||||
"planetTextStyle" : {
|
||||
"backDirectives" : "?border=6;000;000",
|
||||
"fontSize" : 24
|
||||
},
|
||||
|
||||
"buttonClickSound" : [ "/sfx/interface/button/click.wav" ],
|
||||
"buttonReleaseSound" : [ "/sfx/interface/button/release.wav" ],
|
||||
|
@ -1,10 +1,8 @@
|
||||
{
|
||||
"config" : {
|
||||
"lineHeight" : 1,
|
||||
"font" : {
|
||||
"directives" : "?border=1;111a;1114",
|
||||
"padding" : [1, 1] // Padding to prevent border clipping at the edges of the log canvas while keeping compat with mods that patch the canvas size
|
||||
},
|
||||
"padding" : [1, 1],
|
||||
"textStyle" : { "backDirectives" : "?border=1;111a;1114" },
|
||||
"colors" : {
|
||||
"local" : "^white;",
|
||||
"party" : "^blue;",
|
||||
|
@ -1,4 +1,7 @@
|
||||
{
|
||||
"movementThreshold" : 0.5,
|
||||
"bubbleOffset" : [0, 1.875]
|
||||
"movementThreshold" : 0.5,
|
||||
"bubbleOffset" : [0, 1.875],
|
||||
"textStyle" : {
|
||||
"backDirectives" : "?border=1;000a;0000?border=1;000a;0004"
|
||||
}
|
||||
}
|
@ -427,8 +427,6 @@ void Assets::queueJsons(StringSet const& paths) const {
|
||||
}
|
||||
|
||||
ImageConstPtr Assets::image(AssetPath const& path) const {
|
||||
validatePath(path, true, true);
|
||||
|
||||
return as<ImageData>(getAsset(AssetId{AssetType::Image, path}))->image;
|
||||
}
|
||||
|
||||
@ -956,7 +954,7 @@ Json Assets::checkPatchArray(String const& path, AssetSourcePtr const& source, J
|
||||
Json Assets::readJson(String const& path) const {
|
||||
ByteArray streamData = read(path);
|
||||
try {
|
||||
Json result = inputUtf8Json(streamData.begin(), streamData.end(), false);
|
||||
Json result = inputUtf8Json(streamData.begin(), streamData.end(), JsonParseType::Top);
|
||||
for (auto const& pair : m_files.get(path).patchSources) {
|
||||
auto& patchPath = pair.first;
|
||||
auto& patchSource = pair.second;
|
||||
@ -973,7 +971,7 @@ Json Assets::readJson(String const& path) const {
|
||||
if (newResult)
|
||||
result = std::move(newResult);
|
||||
} else {
|
||||
auto patchJson = inputUtf8Json(patchStream.begin(), patchStream.end(), false);
|
||||
auto patchJson = inputUtf8Json(patchStream.begin(), patchStream.end(), JsonParseType::Top);
|
||||
if (patchJson.isType(Json::Type::Array)) {
|
||||
auto patchData = patchJson.toArray();
|
||||
try {
|
||||
@ -1131,6 +1129,7 @@ shared_ptr<Assets::AssetData> Assets::loadJson(AssetPath const& path) const {
|
||||
}
|
||||
|
||||
shared_ptr<Assets::AssetData> Assets::loadImage(AssetPath const& path) const {
|
||||
validatePath(path, true, true);
|
||||
if (!path.directives.empty()) {
|
||||
shared_ptr<ImageData> source =
|
||||
as<ImageData>(loadAsset(AssetId{AssetType::Image, {path.basePath, path.subPath, {}}}));
|
||||
|
@ -667,6 +667,7 @@ void ClientApplication::changeState(MainAppState newState) {
|
||||
|
||||
m_mainInterface = make_shared<MainInterface>(m_universeClient, m_worldPainter, m_cinematicOverlay);
|
||||
m_universeClient->setLuaCallbacks("interface", LuaBindings::makeInterfaceCallbacks(m_mainInterface.get()));
|
||||
m_universeClient->setLuaCallbacks("chat", LuaBindings::makeChatCallbacks(m_mainInterface.get(), m_universeClient.get()));
|
||||
m_universeClient->startLua();
|
||||
|
||||
m_mainMixer->setWorldPainter(m_worldPainter);
|
||||
|
@ -162,7 +162,7 @@ std::ostream& operator<<(std::ostream& os, AssetPath const& rhs) {
|
||||
|
||||
rhs.directives.forEach([&](auto const& entry, Directives const& directives) {
|
||||
os << "?";
|
||||
os << entry.string(*directives.shared);
|
||||
os << entry.string(*directives);
|
||||
});
|
||||
|
||||
return os;
|
||||
|
@ -42,6 +42,8 @@ bool Directives::Shared::empty() const {
|
||||
return entries.empty();
|
||||
}
|
||||
|
||||
Directives::Shared::Shared() {}
|
||||
|
||||
Directives::Shared::Shared(List<Entry>&& givenEntries, String&& givenString, StringView givenPrefix) {
|
||||
entries = std::move(givenEntries);
|
||||
string = std::move(givenString);
|
||||
@ -74,7 +76,7 @@ Directives::Directives(Directives const& directives) {
|
||||
Directives::~Directives() {}
|
||||
|
||||
Directives& Directives::operator=(String const& s) {
|
||||
if (shared && shared->string == s)
|
||||
if (m_shared && m_shared->string == s)
|
||||
return *this;
|
||||
|
||||
parse(String(s));
|
||||
@ -82,7 +84,7 @@ Directives& Directives::operator=(String const& s) {
|
||||
}
|
||||
|
||||
Directives& Directives::operator=(String&& s) {
|
||||
if (shared && shared->string == s) {
|
||||
if (m_shared && m_shared->string == s) {
|
||||
s.clear();
|
||||
return *this;
|
||||
}
|
||||
@ -92,7 +94,7 @@ Directives& Directives::operator=(String&& s) {
|
||||
}
|
||||
|
||||
Directives& Directives::operator=(const char* s) {
|
||||
if (shared && shared->string.utf8().compare(s) == 0)
|
||||
if (m_shared && m_shared->string.utf8().compare(s) == 0)
|
||||
return *this;
|
||||
|
||||
parse(s);
|
||||
@ -100,27 +102,29 @@ Directives& Directives::operator=(const char* s) {
|
||||
}
|
||||
|
||||
Directives& Directives::operator=(Directives&& other) noexcept {
|
||||
shared = std::move(other.shared);
|
||||
m_shared = std::move(other.m_shared);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Directives& Directives::operator=(Directives const& other) {
|
||||
shared = other.shared;
|
||||
m_shared = other.m_shared;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Directives::loadOperations() const {
|
||||
if (!shared)
|
||||
if (!m_shared)
|
||||
return;
|
||||
|
||||
MutexLocker lock(shared->mutex, true);
|
||||
for (auto& entry : shared->entries)
|
||||
entry.loadOperation(*shared);
|
||||
MutexLocker locker(m_shared->mutex, false);
|
||||
if (!m_shared.unique())
|
||||
locker.lock();
|
||||
for (auto& entry : m_shared->entries)
|
||||
entry.loadOperation(*m_shared);
|
||||
}
|
||||
|
||||
void Directives::parse(String&& directives) {
|
||||
if (directives.empty()) {
|
||||
shared.reset();
|
||||
m_shared.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -147,46 +151,46 @@ void Directives::parse(String&& directives) {
|
||||
});
|
||||
|
||||
if (entries.empty() && !prefix.empty()) {
|
||||
shared.reset();
|
||||
m_shared.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
shared = std::make_shared<Shared const>(std::move(entries), std::move(directives), prefix);
|
||||
m_shared = std::make_shared<Shared const>(std::move(entries), std::move(directives), prefix);
|
||||
if (view.utf8().size() < 1000) { // Pre-load short enough directives
|
||||
for (auto& entry : shared->entries)
|
||||
entry.loadOperation(*shared);
|
||||
for (auto& entry : m_shared->entries)
|
||||
entry.loadOperation(*m_shared);
|
||||
}
|
||||
}
|
||||
|
||||
String Directives::string() const {
|
||||
if (!shared)
|
||||
if (!m_shared)
|
||||
return "";
|
||||
else
|
||||
return shared->string;
|
||||
return m_shared->string;
|
||||
}
|
||||
|
||||
StringView Directives::prefix() const {
|
||||
if (!shared)
|
||||
if (!m_shared)
|
||||
return "";
|
||||
else
|
||||
return shared->prefix;
|
||||
return m_shared->prefix;
|
||||
}
|
||||
|
||||
String const* Directives::stringPtr() const {
|
||||
if (!shared)
|
||||
if (!m_shared)
|
||||
return nullptr;
|
||||
else
|
||||
return &shared->string;
|
||||
return &m_shared->string;
|
||||
}
|
||||
|
||||
|
||||
String Directives::buildString() const {
|
||||
if (shared) {
|
||||
String built = shared->prefix;
|
||||
if (m_shared) {
|
||||
String built = m_shared->prefix;
|
||||
|
||||
for (auto& entry : shared->entries) {
|
||||
for (auto& entry : m_shared->entries) {
|
||||
built += "?";
|
||||
built += entry.string(*shared);
|
||||
built += entry.string(*m_shared);
|
||||
}
|
||||
|
||||
return built;
|
||||
@ -197,23 +201,25 @@ String Directives::buildString() const {
|
||||
|
||||
String& Directives::addToString(String& out) const {
|
||||
if (!empty())
|
||||
out += shared->string;
|
||||
out += m_shared->string;
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t Directives::hash() const {
|
||||
return shared ? shared->hash : 0;
|
||||
return m_shared ? m_shared->hash : 0;
|
||||
}
|
||||
|
||||
size_t Directives::size() const {
|
||||
return shared ? shared->entries.size() : 0;
|
||||
return m_shared ? m_shared->entries.size() : 0;
|
||||
}
|
||||
|
||||
bool Directives::empty() const {
|
||||
return !shared || shared->empty();
|
||||
return !m_shared || m_shared->empty();
|
||||
}
|
||||
|
||||
Directives::operator bool() const { return !empty(); }
|
||||
Directives::operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
bool Directives::equals(Directives const& other) const {
|
||||
return hash() == other.hash();
|
||||
@ -243,7 +249,7 @@ DataStream& operator>>(DataStream& ds, Directives& directives) {
|
||||
|
||||
DataStream& operator<<(DataStream & ds, Directives const& directives) {
|
||||
if (directives)
|
||||
ds.write(directives.shared->string);
|
||||
ds.write(directives->string);
|
||||
else
|
||||
ds.write(String());
|
||||
|
||||
@ -326,21 +332,22 @@ String DirectivesGroup::toString() const {
|
||||
}
|
||||
|
||||
void DirectivesGroup::addToString(String& string) const {
|
||||
for (auto& directives : m_directives)
|
||||
if (directives.shared) {
|
||||
auto& dirString = directives.shared->string;
|
||||
for (auto& directives : m_directives) {
|
||||
if (directives) {
|
||||
auto& dirString = directives->string;
|
||||
if (!dirString.empty()) {
|
||||
if (dirString.utf8().front() != '?')
|
||||
string += "?";
|
||||
string += dirString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DirectivesGroup::forEach(DirectivesCallback callback) const {
|
||||
for (auto& directives : m_directives) {
|
||||
if (directives.shared) {
|
||||
for (auto& entry : directives.shared->entries)
|
||||
if (directives) {
|
||||
for (auto& entry : directives->entries)
|
||||
callback(entry, directives);
|
||||
}
|
||||
}
|
||||
@ -348,8 +355,8 @@ void DirectivesGroup::forEach(DirectivesCallback callback) const {
|
||||
|
||||
bool DirectivesGroup::forEachAbortable(AbortableDirectivesCallback callback) const {
|
||||
for (auto& directives : m_directives) {
|
||||
if (directives.shared) {
|
||||
for (auto& entry : directives.shared->entries) {
|
||||
if (directives) {
|
||||
for (auto& entry : directives->entries) {
|
||||
if (!callback(entry, directives))
|
||||
return false;
|
||||
}
|
||||
@ -367,7 +374,7 @@ Image DirectivesGroup::applyNewImage(Image const& image) const {
|
||||
|
||||
void DirectivesGroup::applyExistingImage(Image& image) const {
|
||||
forEach([&](auto const& entry, Directives const& directives) {
|
||||
ImageOperation const& operation = entry.loadOperation(*directives.shared);
|
||||
ImageOperation const& operation = entry.loadOperation(*directives);
|
||||
if (auto error = operation.ptr<ErrorImageOperation>())
|
||||
std::rethrow_exception(error->exception);
|
||||
else
|
||||
|
@ -36,6 +36,7 @@ public:
|
||||
mutable Mutex mutex;
|
||||
|
||||
bool empty() const;
|
||||
Shared();
|
||||
Shared(List<Entry>&& givenEntries, String&& givenString, StringView givenPrefix);
|
||||
};
|
||||
|
||||
@ -65,6 +66,9 @@ public:
|
||||
bool empty() const;
|
||||
operator bool() const;
|
||||
|
||||
Shared const& operator*() const;
|
||||
Shared const* operator->() const;
|
||||
|
||||
bool equals(Directives const& other) const;
|
||||
bool equals(String const& string) const;
|
||||
|
||||
@ -78,7 +82,7 @@ public:
|
||||
//friend bool operator==(Directives const& directives, String const& string);
|
||||
//friend bool operator==(String const& string, Directives const& directives);
|
||||
|
||||
std::shared_ptr<Shared const> shared;
|
||||
std::shared_ptr<Shared const> m_shared;
|
||||
};
|
||||
|
||||
class DirectivesGroup {
|
||||
@ -122,7 +126,6 @@ private:
|
||||
size_t m_count;
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct hash<DirectivesGroup> {
|
||||
size_t operator()(DirectivesGroup const& s) const;
|
||||
@ -130,4 +133,13 @@ struct hash<DirectivesGroup> {
|
||||
|
||||
typedef DirectivesGroup ImageDirectives;
|
||||
|
||||
inline Directives::Shared const& Directives::operator*() const {
|
||||
return *m_shared;
|
||||
}
|
||||
inline Directives::Shared const* Directives::operator->() const {
|
||||
if (!m_shared)
|
||||
throw DirectivesException("Directives::operator-> nullptr");
|
||||
return m_shared.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -65,12 +65,12 @@ bool CommaElement::operator==(CommaElement const&) const {
|
||||
|
||||
FormattedJson FormattedJson::parse(String const& string) {
|
||||
return inputUtf32Json<String::const_iterator, FormattedJsonBuilderStream, FormattedJson>(
|
||||
string.begin(), string.end(), true);
|
||||
string.begin(), string.end(), JsonParseType::Value);
|
||||
}
|
||||
|
||||
FormattedJson FormattedJson::parseJson(String const& string) {
|
||||
return inputUtf32Json<String::const_iterator, FormattedJsonBuilderStream, FormattedJson>(
|
||||
string.begin(), string.end(), false);
|
||||
string.begin(), string.end(), JsonParseType::Top);
|
||||
}
|
||||
|
||||
FormattedJson FormattedJson::ofType(Json::Type type) {
|
||||
|
@ -105,11 +105,15 @@ Json Json::ofType(Type t) {
|
||||
}
|
||||
|
||||
Json Json::parse(String const& string) {
|
||||
return inputUtf32Json<String::const_iterator>(string.begin(), string.end(), true);
|
||||
return inputUtf32Json<String::const_iterator>(string.begin(), string.end(), JsonParseType::Value);
|
||||
}
|
||||
|
||||
Json Json::parseSequence(String const& sequence) {
|
||||
return inputUtf32Json<String::const_iterator>(sequence.begin(), sequence.end(), JsonParseType::Sequence);
|
||||
}
|
||||
|
||||
Json Json::parseJson(String const& json) {
|
||||
return inputUtf32Json<String::const_iterator>(json.begin(), json.end(), false);
|
||||
return inputUtf32Json<String::const_iterator>(json.begin(), json.end(), JsonParseType::Top);
|
||||
}
|
||||
|
||||
Json::Json() {}
|
||||
|
@ -51,6 +51,9 @@ public:
|
||||
// Parses JSON or JSON sub-type
|
||||
static Json parse(String const& string);
|
||||
|
||||
// Parses JSON sequence
|
||||
static Json parseSequence(String const& sequence);
|
||||
|
||||
// Parses JSON object or array only (the only top level types allowed by
|
||||
// JSON)
|
||||
static Json parseJson(String const& json);
|
||||
|
@ -50,7 +50,7 @@ public:
|
||||
};
|
||||
|
||||
template <typename InputIterator>
|
||||
Json inputUtf8Json(InputIterator begin, InputIterator end, bool fragment) {
|
||||
Json inputUtf8Json(InputIterator begin, InputIterator end, JsonParseType parseType) {
|
||||
typedef U8ToU32Iterator<InputIterator> Utf32Input;
|
||||
typedef JsonParser<Utf32Input> Parser;
|
||||
|
||||
@ -58,7 +58,7 @@ Json inputUtf8Json(InputIterator begin, InputIterator end, bool fragment) {
|
||||
Parser parser(stream);
|
||||
Utf32Input wbegin(begin);
|
||||
Utf32Input wend(end);
|
||||
Utf32Input pend = parser.parse(wbegin, wend, fragment);
|
||||
Utf32Input pend = parser.parse(wbegin, wend, parseType);
|
||||
|
||||
if (parser.error())
|
||||
throw JsonParsingException(strf("Error parsing json: {} at {}:{}", parser.error(), parser.line(), parser.column()));
|
||||
@ -77,11 +77,11 @@ void outputUtf8Json(Json const& val, OutputIterator out, int pretty, bool sort)
|
||||
}
|
||||
|
||||
template <typename InputIterator, typename Stream = JsonBuilderStream, typename Jsonlike = Json>
|
||||
Jsonlike inputUtf32Json(InputIterator begin, InputIterator end, bool fragment) {
|
||||
Jsonlike inputUtf32Json(InputIterator begin, InputIterator end, JsonParseType parseType) {
|
||||
Stream stream;
|
||||
JsonParser<InputIterator> parser(stream);
|
||||
|
||||
InputIterator pend = parser.parse(begin, end, fragment);
|
||||
InputIterator pend = parser.parse(begin, end, parseType);
|
||||
|
||||
if (parser.error()) {
|
||||
throw JsonParsingException(strf("Error parsing json: {} at {}:{}", parser.error(), parser.line(), parser.column()));
|
||||
|
@ -27,6 +27,12 @@ struct JsonStream {
|
||||
virtual void putComma() = 0;
|
||||
};
|
||||
|
||||
enum class JsonParseType : uint8_t {
|
||||
Top, // Top-level Object or Array
|
||||
Value, // Any singular Json value
|
||||
Sequence // Like an array, but without needing the [] or commas.
|
||||
};
|
||||
|
||||
// Will parse JSON and output to a given JsonStream. Parses an *extension* to
|
||||
// the JSON format that includes comments.
|
||||
template <typename InputIterator>
|
||||
@ -39,15 +45,17 @@ public:
|
||||
// Does not throw. On error, returned iterator will not be equal to end, and
|
||||
// error() will be non-null. Set fragment to true to parse any JSON type
|
||||
// rather than just object or array.
|
||||
InputIterator parse(InputIterator begin, InputIterator end, bool fragment = false) {
|
||||
InputIterator parse(InputIterator begin, InputIterator end, JsonParseType parseType = JsonParseType::Top) {
|
||||
init(begin, end);
|
||||
|
||||
try {
|
||||
white();
|
||||
if (fragment)
|
||||
value();
|
||||
else
|
||||
if (parseType == JsonParseType::Top)
|
||||
top();
|
||||
else if (parseType == JsonParseType::Value)
|
||||
value();
|
||||
else if (parseType == JsonParseType::Sequence)
|
||||
sequence();
|
||||
white();
|
||||
} catch (ParsingException const&) {
|
||||
}
|
||||
@ -190,6 +198,56 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void sequence() {
|
||||
m_stream.beginArray();
|
||||
while (true) {
|
||||
if (isSpace(m_char)) {
|
||||
next();
|
||||
continue;
|
||||
} else if (m_char == '{')
|
||||
object();
|
||||
else if (m_char == '[')
|
||||
array();
|
||||
else if (m_char == '"')
|
||||
string();
|
||||
else if (m_char == '-')
|
||||
number();
|
||||
else if (m_char == 0)
|
||||
break;
|
||||
else {
|
||||
auto begin = m_current;
|
||||
if (m_char >= '0' && m_char <= '9') {
|
||||
number();
|
||||
if (m_error.empty()) {
|
||||
next();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
m_error.clear();
|
||||
m_current = begin;
|
||||
if (m_char == 't' || m_char == 'f' || m_char == 'n') {
|
||||
word();
|
||||
if (m_error.empty()) {
|
||||
next();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
m_error.clear();
|
||||
m_current = begin;
|
||||
// well, shit. do a simple string parse until we hit whitespace
|
||||
// no fancy things like \n, do a proper string if you want that
|
||||
CharArray str;
|
||||
do {
|
||||
str += m_char;
|
||||
next();
|
||||
} while (m_char && !isSpace(m_char));
|
||||
m_stream.putString(str.c_str(), str.length());
|
||||
}
|
||||
next();
|
||||
}
|
||||
m_stream.endArray();
|
||||
}
|
||||
|
||||
void string() {
|
||||
CharArray s = parseString();
|
||||
m_stream.putString(s.c_str(), s.length());
|
||||
|
@ -60,6 +60,10 @@ LuaInt LuaTable::rawLength() const {
|
||||
return engine().tableLength(true, handleIndex());
|
||||
}
|
||||
|
||||
void LuaCallbacks::copyCallback(String srcName, String dstName) {
|
||||
m_callbacks.set(dstName, m_callbacks.get(srcName));
|
||||
}
|
||||
|
||||
LuaCallbacks& LuaCallbacks::merge(LuaCallbacks const& callbacks) {
|
||||
try {
|
||||
for (auto const& pair : callbacks.m_callbacks)
|
||||
|
@ -267,6 +267,8 @@ LuaValue const LuaNil = LuaValue();
|
||||
|
||||
class LuaCallbacks {
|
||||
public:
|
||||
void copyCallback(String srcName, String dstName);
|
||||
|
||||
template <typename Function>
|
||||
void registerCallback(String name, Function&& func);
|
||||
|
||||
|
@ -1,9 +1,35 @@
|
||||
#include "StarText.hpp"
|
||||
|
||||
#include "StarJsonExtra.hpp"
|
||||
#include <regex>
|
||||
|
||||
namespace Star {
|
||||
|
||||
TextStyle::TextStyle(Json const& config) : TextStyle() {
|
||||
if (config.isType(Json::Type::String))
|
||||
font = config.toString();
|
||||
else
|
||||
loadJson(config);
|
||||
}
|
||||
TextStyle& TextStyle::loadJson(Json const& config) {
|
||||
if (!config)
|
||||
return *this;
|
||||
|
||||
lineSpacing = config.getFloat("lineSpacing", lineSpacing);
|
||||
if (auto jColor = config.opt("color"))
|
||||
color = jsonToColor(*jColor).toRgba();
|
||||
if (auto jShadow = config.opt("shadow"))
|
||||
shadow = jsonToColor(*jShadow).toRgba();
|
||||
fontSize = config.getUInt("fontSize", fontSize);
|
||||
if (auto jFont = config.optString("font"))
|
||||
font = *jFont;
|
||||
if (auto jDirectives = config.optString("directives"))
|
||||
directives = *jDirectives;
|
||||
if (auto jBackDirectives = config.optString("backDirectives"))
|
||||
backDirectives = *jBackDirectives;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace Text {
|
||||
static auto stripEscapeRegex = std::regex(strf("\\{:c}[^;]*{:c}", CmdEsc, EndEsc));
|
||||
String stripEscapeCodes(String const& s) {
|
||||
|
@ -2,9 +2,29 @@
|
||||
|
||||
#include "StarString.hpp"
|
||||
#include "StarStringView.hpp"
|
||||
#include "StarVector.hpp"
|
||||
#include "StarDirectives.hpp"
|
||||
#include "StarJson.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
unsigned const DefaultFontSize = 8;
|
||||
float const DefaultLineSpacing = 1.3f;
|
||||
|
||||
struct TextStyle {
|
||||
float lineSpacing = DefaultLineSpacing;
|
||||
Vec4B color = Vec4B::filled(255);
|
||||
Vec4B shadow = Vec4B::filled(0);
|
||||
unsigned fontSize = DefaultFontSize;
|
||||
String font = "";
|
||||
Directives directives;
|
||||
Directives backDirectives;
|
||||
|
||||
TextStyle() = default;
|
||||
TextStyle(Json const& config);
|
||||
TextStyle& loadJson(Json const& config);
|
||||
};
|
||||
|
||||
namespace Text {
|
||||
unsigned char const StartEsc = '\x1b';
|
||||
unsigned char const EndEsc = ';';
|
||||
|
@ -118,12 +118,14 @@ LuaCallbacks LuaBindings::makeUtilityCallbacks() {
|
||||
callbacks.registerCallback("print", UtilityCallbacks::print);
|
||||
callbacks.registerCallback("interpolateSinEase", UtilityCallbacks::interpolateSinEase);
|
||||
callbacks.registerCallback("replaceTags", UtilityCallbacks::replaceTags);
|
||||
callbacks.registerCallback("jsonParse", [](String const& json) { return Json::parse(json); });
|
||||
callbacks.registerCallback("parseJsonSequence", [](String const& json) { return Json::parseSequence(json); });
|
||||
callbacks.registerCallback("jsonMerge", [](Json const& a, Json const& b) { return jsonMerge(a, b); });
|
||||
callbacks.registerCallback("jsonQuery", [](Json const& json, String const& path, Json const& def) { return json.query(path, def); });
|
||||
callbacks.registerCallback("makeRandomSource", [](Maybe<uint64_t> seed) { return seed ? RandomSource(*seed) : RandomSource(); });
|
||||
callbacks.registerCallback("makePerlinSource", [](Json const& config) { return PerlinF(config); });
|
||||
|
||||
callbacks.copyCallback("parseJson", "jsonFromString"); // SE compat
|
||||
|
||||
auto hash64LuaValues = [](LuaVariadic<LuaValue> const& values) -> uint64_t {
|
||||
XXHash64 hash;
|
||||
|
||||
|
@ -83,9 +83,7 @@ ActionBar::ActionBar(MainInterfacePaneManager* paneManager, PlayerPtr player) {
|
||||
|
||||
TextPositioning countPosition = {jsonToVec2F(m_config.get("countMidAnchor")), HorizontalAnchor::HMidAnchor};
|
||||
customBarLeft->setCountPosition(countPosition);
|
||||
customBarLeft->setCountFontMode(FontMode::Shadow);
|
||||
customBarRight->setCountPosition(countPosition);
|
||||
customBarRight->setCountFontMode(FontMode::Shadow);
|
||||
|
||||
m_customBarWidgets.append({customBarLeft, customBarRight, customBarLeftOverlay, customBarRightOverlay});
|
||||
}
|
||||
|
@ -20,32 +20,30 @@ Chat::Chat(UniverseClientPtr client) : m_client(client) {
|
||||
m_historyOffset = 0;
|
||||
|
||||
auto assets = Root::singleton().assets();
|
||||
auto config = assets->json("/interface/chat/chat.config:config");
|
||||
m_timeChatLastActive = Time::monotonicMilliseconds();
|
||||
auto fontConfig = assets->json("/interface/chat/chat.config:config.font");
|
||||
m_fontSize = fontConfig.getInt("baseSize");
|
||||
m_fontDirectives = fontConfig.queryString("directives", "");
|
||||
m_font = fontConfig.queryString("type", "");
|
||||
m_chatLineHeight = assets->json("/interface/chat/chat.config:config.lineHeight").toFloat();
|
||||
m_chatVisTime = assets->json("/interface/chat/chat.config:config.visTime").toFloat();
|
||||
m_fadeRate = assets->json("/interface/chat/chat.config:config.fadeRate").toDouble();
|
||||
m_chatHistoryLimit = assets->json("/interface/chat/chat.config:config.chatHistoryLimit").toInt();
|
||||
m_chatTextStyle = config.get("textStyle");
|
||||
m_chatTextStyle.lineSpacing = config.get("lineHeight").toFloat();
|
||||
m_chatVisTime = config.get("visTime").toFloat();
|
||||
m_fadeRate = config.get("fadeRate").toDouble();
|
||||
m_chatHistoryLimit = config.get("chatHistoryLimit").toInt();
|
||||
|
||||
m_portraitTextOffset = jsonToVec2I(assets->json("/interface/chat/chat.config:config.portraitTextOffset"));
|
||||
m_portraitImageOffset = jsonToVec2I(assets->json("/interface/chat/chat.config:config.portraitImageOffset"));
|
||||
m_portraitScale = assets->json("/interface/chat/chat.config:config.portraitScale").toFloat();
|
||||
m_portraitVerticalMargin = assets->json("/interface/chat/chat.config:config.portraitVerticalMargin").toFloat();
|
||||
m_portraitBackground = assets->json("/interface/chat/chat.config:config.portraitBackground").toString();
|
||||
m_portraitTextOffset = jsonToVec2I(config.get("portraitTextOffset"));
|
||||
m_portraitImageOffset = jsonToVec2I(config.get("portraitImageOffset"));
|
||||
m_portraitScale = config.get("portraitScale").toFloat();
|
||||
m_portraitVerticalMargin = config.get("portraitVerticalMargin").toFloat();
|
||||
m_portraitBackground = config.get("portraitBackground").toString();
|
||||
|
||||
m_bodyHeight = assets->json("/interface/chat/chat.config:config.bodyHeight").toInt();
|
||||
m_expandedBodyHeight = assets->json("/interface/chat/chat.config:config.expandedBodyHeight").toInt();
|
||||
m_bodyHeight = config.get("bodyHeight").toInt();
|
||||
m_expandedBodyHeight = config.get("expandedBodyHeight").toInt();
|
||||
|
||||
m_colorCodes[MessageContext::Local] = assets->json("/interface/chat/chat.config:config.colors.local").toString();
|
||||
m_colorCodes[MessageContext::Party] = assets->json("/interface/chat/chat.config:config.colors.party").toString();
|
||||
m_colorCodes[MessageContext::Broadcast] = assets->json("/interface/chat/chat.config:config.colors.broadcast").toString();
|
||||
m_colorCodes[MessageContext::Whisper] = assets->json("/interface/chat/chat.config:config.colors.whisper").toString();
|
||||
m_colorCodes[MessageContext::CommandResult] = assets->json("/interface/chat/chat.config:config.colors.commandResult").toString();
|
||||
m_colorCodes[MessageContext::RadioMessage] = assets->json("/interface/chat/chat.config:config.colors.radioMessage").toString();
|
||||
m_colorCodes[MessageContext::World] = assets->json("/interface/chat/chat.config:config.colors.world").toString();
|
||||
m_colorCodes[MessageContext::Local] = config.query("colors.local").toString();
|
||||
m_colorCodes[MessageContext::Party] = config.query("colors.party").toString();
|
||||
m_colorCodes[MessageContext::Broadcast] = config.query("colors.broadcast").toString();
|
||||
m_colorCodes[MessageContext::Whisper] = config.query("colors.whisper").toString();
|
||||
m_colorCodes[MessageContext::CommandResult] = config.query("colors.commandResult").toString();
|
||||
m_colorCodes[MessageContext::RadioMessage] = config.query("colors.radioMessage").toString();
|
||||
m_colorCodes[MessageContext::World] = config.query("colors.world").toString();
|
||||
|
||||
GuiReader reader;
|
||||
|
||||
@ -72,7 +70,7 @@ Chat::Chat(UniverseClientPtr client) : m_client(client) {
|
||||
m_say = fetchChild<LabelWidget>("say");
|
||||
|
||||
m_chatLog = fetchChild<CanvasWidget>("chatLog");
|
||||
if (auto logPadding = fontConfig.optQuery("padding")) {
|
||||
if (auto logPadding = config.optQuery("padding")) {
|
||||
m_chatLogPadding = jsonToVec2I(logPadding.get());
|
||||
m_chatLog->setSize(m_chatLog->size() + m_chatLogPadding * 2);
|
||||
m_chatLog->setPosition(m_chatLog->position() - m_chatLogPadding);
|
||||
@ -133,8 +131,8 @@ String Chat::currentChat() const {
|
||||
return m_textBox->getText();
|
||||
}
|
||||
|
||||
void Chat::setCurrentChat(String const& chat) {
|
||||
m_textBox->setText(chat);
|
||||
bool Chat::setCurrentChat(String const& chat, bool moveCursor) {
|
||||
return m_textBox->setText(chat, true, moveCursor);
|
||||
}
|
||||
|
||||
void Chat::clearCurrentChat() {
|
||||
@ -179,8 +177,7 @@ void Chat::addMessages(List<ChatReceivedMessage> const& messages, bool showPane)
|
||||
if (message.portrait.empty())
|
||||
wrapWidth = m_chatLog->size()[0] - m_chatLogPadding[0] * 2;
|
||||
|
||||
guiContext.setFont(m_font);
|
||||
guiContext.setFontSize(m_fontSize);
|
||||
guiContext.setTextStyle(m_chatTextStyle);
|
||||
StringList lines;
|
||||
if (message.fromNick != "" && message.portrait == "")
|
||||
lines = guiContext.wrapInterfaceText(strf("<{}> {}", message.fromNick, message.text), wrapWidth);
|
||||
@ -239,10 +236,10 @@ void Chat::renderImpl() {
|
||||
int messageIndex = -m_historyOffset;
|
||||
|
||||
GuiContext& guiContext = GuiContext::singleton();
|
||||
guiContext.setFont(m_font);
|
||||
guiContext.setFontSize(m_fontSize);
|
||||
guiContext.setLineSpacing(m_chatLineHeight);
|
||||
for (auto message : m_receivedMessages) {
|
||||
float lineHeight = m_chatTextStyle.lineSpacing;
|
||||
float fontSize = m_chatTextStyle.fontSize;
|
||||
guiContext.setTextStyle(m_chatTextStyle);
|
||||
for (auto& message : m_receivedMessages) {
|
||||
if (!m_modeFilter.empty() && !m_modeFilter.contains(message.mode))
|
||||
continue;
|
||||
|
||||
@ -260,7 +257,7 @@ void Chat::renderImpl() {
|
||||
String messageString = channelColorCode + message.text;
|
||||
|
||||
float messageHeight = 0;
|
||||
float lineHeightMargin = + ((m_chatLineHeight * m_fontSize) - m_fontSize);
|
||||
float lineHeightMargin = ((lineHeight * fontSize) - fontSize);
|
||||
unsigned wrapWidth = m_chatLog->size()[0] - m_chatLogPadding[0] * 2;
|
||||
|
||||
if (message.portrait != "") {
|
||||
@ -274,13 +271,14 @@ void Chat::renderImpl() {
|
||||
m_chatLog->drawImage(m_portraitBackground, Vec2F(imagePosition), 1.0f, fade);
|
||||
m_chatLog->drawImage(message.portrait, Vec2F(imagePosition + m_portraitImageOffset), m_portraitScale, fade);
|
||||
tp.pos += Vec2F(0, floor(messageHeight / 2));
|
||||
m_chatLog->drawText(messageString, tp, m_fontSize, fade, FontMode::Normal, m_chatLineHeight, m_font, m_fontDirectives);
|
||||
m_chatTextStyle.color = fade;
|
||||
m_chatLog->drawText(messageString, tp, m_chatTextStyle);
|
||||
|
||||
} else {
|
||||
TextPositioning tp = {Vec2F(chatMin), HorizontalAnchor::LeftAnchor, VerticalAnchor::BottomAnchor, wrapWidth};
|
||||
messageHeight = guiContext.determineInterfaceTextSize(messageString, tp).size()[1] + lineHeightMargin;
|
||||
|
||||
m_chatLog->drawText(messageString, tp, m_fontSize, fade, FontMode::Normal, m_chatLineHeight, m_font, m_fontDirectives);
|
||||
m_chatTextStyle.color = fade;
|
||||
m_chatLog->drawText(messageString, tp, m_chatTextStyle);
|
||||
}
|
||||
|
||||
chatMin[1] += ceil(messageHeight);
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
void addHistory(String const& chat);
|
||||
|
||||
String currentChat() const;
|
||||
void setCurrentChat(String const& chat);
|
||||
bool setCurrentChat(String const& chat, bool moveCursor = false);
|
||||
void clearCurrentChat();
|
||||
|
||||
ChatSendMode sendMode() const;
|
||||
@ -66,10 +66,7 @@ private:
|
||||
int64_t m_timeChatLastActive;
|
||||
float m_chatVisTime;
|
||||
float m_fadeRate;
|
||||
unsigned m_fontSize;
|
||||
String m_fontDirectives;
|
||||
String m_font;
|
||||
float m_chatLineHeight;
|
||||
TextStyle m_chatTextStyle;
|
||||
unsigned m_chatHistoryLimit;
|
||||
int m_historyOffset;
|
||||
|
||||
|
@ -20,9 +20,8 @@ ChatBubbleManager::ChatBubbleManager()
|
||||
|
||||
auto jsonData = assets->json("/interface/windowconfig/chatbubbles.config");
|
||||
|
||||
m_color = jsonToColor(jsonData.get("textColor"));
|
||||
|
||||
m_fontSize = jsonData.getInt("fontSize");
|
||||
m_textStyle.color = jsonToColor(jsonData.get("textColor")).toRgba();
|
||||
m_textStyle.loadJson(jsonData.get("textStyle"));
|
||||
m_textPadding = jsonToVec2F(jsonData.get("textPadding"));
|
||||
|
||||
m_zoom = jsonData.getInt("textZoom");
|
||||
@ -196,9 +195,7 @@ void ChatBubbleManager::addChatActions(List<ChatAction> chatActions, bool silent
|
||||
// bother me so bad if it weren't so fucking easy to do right.
|
||||
|
||||
// yea I agree
|
||||
m_guiContext->setFontSize(m_fontSize, m_zoom);
|
||||
m_guiContext->setFontProcessingDirectives("");
|
||||
m_guiContext->setDefaultFont();
|
||||
m_guiContext->setTextStyle(m_textStyle);
|
||||
auto result = m_guiContext->determineTextSize(sayAction.text, m_textTemplate);
|
||||
float textWidth = result.width() / m_zoom + m_textPadding[0];
|
||||
float textHeight = result.height() / m_zoom + m_textPadding[1];
|
||||
@ -252,14 +249,17 @@ void ChatBubbleManager::addChatActions(List<ChatAction> chatActions, bool silent
|
||||
float verticalShift = (partSize * innerTiles[1] - textMultiLineShift) * 0.5f + textMultiLineShift;
|
||||
Vec2F position = Vec2F(horizontalCenter, verticalShift);
|
||||
List<BubbleText> bubbleTexts;
|
||||
auto fontSize = config.getUInt("fontSize", m_fontSize);
|
||||
auto color = config.opt("color").apply(jsonToColor).value(m_color);
|
||||
bubbleTexts.append(make_tuple(sayAction.text, fontSize, color.toRgba(), true, position));
|
||||
TextStyle textStyle = m_textStyle;
|
||||
textStyle.fontSize = config.getUInt("fontSize", textStyle.fontSize);
|
||||
if (auto jColor = config.opt("color"))
|
||||
textStyle.color = jsonToColor(*jColor).toRgba();
|
||||
textStyle.loadJson(config.get("style", Json()));
|
||||
bubbleTexts.append(make_tuple(sayAction.text, textStyle, true, position));
|
||||
|
||||
for (auto& backgroundImage : backgroundImages)
|
||||
get<1>(backgroundImage) += Vec2F(-horizontalCenter, partSize);
|
||||
for (auto& bubbleText : bubbleTexts)
|
||||
get<4>(bubbleText) += Vec2F(-horizontalCenter, partSize);
|
||||
get<3>(bubbleText) += Vec2F(-horizontalCenter, partSize);
|
||||
|
||||
auto pos = m_camera.worldToScreen(sayAction.position + m_bubbleOffset);
|
||||
RectF boundBox = fold(backgroundImages, RectF::null(), [pos, this](RectF const& boundBox, BubbleImage const& bubbleImage) {
|
||||
@ -272,7 +272,7 @@ void ChatBubbleManager::addChatActions(List<ChatAction> chatActions, bool silent
|
||||
m_bubbles.filter([&sayAction](BubbleState<Bubble> const&, Bubble const& bubble) { return bubble.entity != sayAction.entity; });
|
||||
m_bubbles.addBubble(pos, boundBox, std::move(bubble), m_interBubbleMargin * m_zoom);
|
||||
oldBubbles.sort([](BubbleState<Bubble> const& a, BubbleState<Bubble> const& b) { return a.contents.age < b.contents.age; });
|
||||
for (auto bubble : oldBubbles.slice(0, m_maxMessagePerEntity - 1))
|
||||
for (auto& bubble : oldBubbles.slice(0, m_maxMessagePerEntity - 1))
|
||||
m_bubbles.addBubble(bubble.idealDestination, bubble.boundBox, bubble.contents, 0);
|
||||
|
||||
} else if (action.is<PortraitChatAction>()) {
|
||||
@ -286,12 +286,12 @@ void ChatBubbleManager::addChatActions(List<ChatAction> chatActions, bool silent
|
||||
backgroundImages.append(make_tuple(m_portraitMoreImage, Vec2F(m_portraitMorePosition)));
|
||||
backgroundImages.append(make_tuple(portraitAction.portrait, Vec2F(m_portraitPosition)));
|
||||
List<BubbleText> bubbleTexts;
|
||||
bubbleTexts.append(make_tuple(portraitAction.text, m_fontSize, m_color.toRgba(), false, Vec2F(m_portraitTextPosition)));
|
||||
bubbleTexts.append(make_tuple(portraitAction.text, m_textStyle, false, Vec2F(m_portraitTextPosition)));
|
||||
|
||||
for (auto& backgroundImage : backgroundImages)
|
||||
get<1>(backgroundImage) += Vec2F(-m_portraitBackgroundSize[0] / 2, 0);
|
||||
for (auto& bubbleText : bubbleTexts)
|
||||
get<4>(bubbleText) += Vec2F(-m_portraitBackgroundSize[0] / 2, 0);
|
||||
get<3>(bubbleText) += Vec2F(-m_portraitBackgroundSize[0] / 2, 0);
|
||||
|
||||
m_portraitBubbles.prepend({
|
||||
portraitAction.entity,
|
||||
@ -321,28 +321,19 @@ void ChatBubbleManager::addChatActions(List<ChatAction> chatActions, bool silent
|
||||
|
||||
RectF ChatBubbleManager::bubbleImageRect(Vec2F screenPos, BubbleImage const& bubbleImage, int pixelRatio) {
|
||||
auto imgMetadata = Root::singleton().imageMetadataDatabase();
|
||||
auto image = get<0>(bubbleImage);
|
||||
auto& image = get<0>(bubbleImage);
|
||||
return RectF::withSize(screenPos + get<1>(bubbleImage) * pixelRatio, Vec2F(imgMetadata->imageSize(image)) * pixelRatio);
|
||||
}
|
||||
|
||||
void ChatBubbleManager::drawBubbleImage(Vec2F screenPos, BubbleImage const& bubbleImage, int pixelRatio, int alpha) {
|
||||
auto image = get<0>(bubbleImage);
|
||||
auto& image = get<0>(bubbleImage);
|
||||
auto offset = get<1>(bubbleImage) * pixelRatio;
|
||||
m_guiContext->drawQuad(image, screenPos + offset, pixelRatio, {255, 255, 255, alpha});
|
||||
}
|
||||
|
||||
void ChatBubbleManager::drawBubbleText(Vec2F screenPos, BubbleText const& bubbleText, int pixelRatio, int alpha, bool isPortrait) {
|
||||
Vec4B const& baseColor = get<2>(bubbleText);
|
||||
|
||||
// use the alpha as a blend value for the text colour as pulled from data.
|
||||
Vec4B const& displayColor = Vec4B(baseColor[0], baseColor[1], baseColor[2], (baseColor[3] * alpha) / 255);
|
||||
|
||||
m_guiContext->setDefaultFont();
|
||||
m_guiContext->setFontProcessingDirectives("");
|
||||
m_guiContext->setFontColor(displayColor);
|
||||
m_guiContext->setFontSize(get<1>(bubbleText), m_zoom);
|
||||
|
||||
auto offset = get<4>(bubbleText) * pixelRatio;
|
||||
m_guiContext->setTextStyle(get<1>(bubbleText), m_zoom);
|
||||
auto offset = get<3>(bubbleText) * pixelRatio;
|
||||
TextPositioning tp = isPortrait ? m_portraitTextTemplate : m_textTemplate;
|
||||
tp.pos = screenPos + offset;
|
||||
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
|
||||
private:
|
||||
typedef tuple<String, Vec2F> BubbleImage;
|
||||
typedef tuple<String, unsigned, Vec4B, bool, Vec2F> BubbleText;
|
||||
typedef tuple<String, TextStyle, bool, Vec2F> BubbleText;
|
||||
|
||||
struct Bubble {
|
||||
EntityId entity;
|
||||
@ -64,8 +64,7 @@ private:
|
||||
|
||||
TextPositioning m_textTemplate;
|
||||
TextPositioning m_portraitTextTemplate;
|
||||
Color m_color;
|
||||
int m_fontSize;
|
||||
TextStyle m_textStyle;
|
||||
Vec2F m_textPadding;
|
||||
|
||||
BubbleSeparator<Bubble> m_bubbles;
|
||||
|
@ -45,8 +45,9 @@ void Cinematic::load(Json const& definition) {
|
||||
panel->animationFrames = panelDefinition.getInt("animationFrames", std::numeric_limits<int>::max());
|
||||
panel->text = panelDefinition.getString("text", "");
|
||||
panel->textPosition = TextPositioning(panelDefinition.get("textPosition", JsonObject()));
|
||||
panel->fontColor = panelDefinition.opt("fontColor").apply(jsonToVec4B).value(Vec4B(255, 255, 255, 255));
|
||||
panel->fontSize = panelDefinition.getUInt("fontSize", 8);
|
||||
panel->textStyle = panelDefinition.get("textStyle", Json());
|
||||
panel->textStyle.color = panelDefinition.opt("fontColor").apply(jsonToVec4B).value(panel->textStyle.color);
|
||||
panel->textStyle.fontSize = panelDefinition.getUInt("fontSize", panel->textStyle.fontSize);
|
||||
panel->avatar = panelDefinition.getString("avatar", "");
|
||||
panel->startTime = panelDefinition.getFloat("startTime", 0);
|
||||
panel->endTime = panelDefinition.getFloat("endTime", 0);
|
||||
@ -208,8 +209,9 @@ void Cinematic::render() {
|
||||
}
|
||||
}
|
||||
if (!panel->text.empty()) {
|
||||
textPainter->setFontSize(floor(panel->fontSize * drawableScale));
|
||||
auto fontColor = panel->fontColor;
|
||||
textPainter->setTextStyle(panel->textStyle);
|
||||
textPainter->setFontSize(floor(panel->textStyle.fontSize * drawableScale));
|
||||
auto fontColor = panel->textStyle.color;
|
||||
fontColor[3] *= values.alpha;
|
||||
textPainter->setFontColor(fontColor);
|
||||
Vec2F position = (m_offset + values.position + Vec2F(panel->textPosition.pos)) * drawableScale + drawableTranslation;
|
||||
|
@ -62,8 +62,7 @@ private:
|
||||
int animationFrames;
|
||||
String text;
|
||||
TextPositioning textPosition;
|
||||
Vec4B fontColor;
|
||||
unsigned fontSize;
|
||||
TextStyle textStyle;
|
||||
List<KeyFrame> keyFrames;
|
||||
float startTime;
|
||||
float endTime;
|
||||
|
@ -91,7 +91,7 @@ StringList ClientCommandProcessor::handleCommand(String const& commandLine) {
|
||||
}
|
||||
} else {
|
||||
auto player = m_universeClient->mainPlayer();
|
||||
if (auto messageResult = player->receiveMessage(connectionForEntity(player->entityId()), strf("/{}", command), { allArguments }))
|
||||
if (auto messageResult = player->receiveMessage(connectionForEntity(player->entityId()), "/" + command, { allArguments }))
|
||||
result.append(messageResult->isType(Json::Type::String) ? *messageResult->stringPtr() : messageResult->repr(1, true));
|
||||
else
|
||||
m_universeClient->sendChat(commandLine, ChatSendMode::Broadcast);
|
||||
|
@ -4,6 +4,9 @@
|
||||
#include "StarLuaGameConverters.hpp"
|
||||
#include "StarMainInterface.hpp"
|
||||
#include "StarGuiContext.hpp"
|
||||
#include "StarChat.hpp"
|
||||
#include "StarUniverseClient.hpp"
|
||||
#include "StarClientCommandProcessor.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -42,4 +45,49 @@ LuaCallbacks LuaBindings::makeInterfaceCallbacks(MainInterface* mainInterface) {
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
LuaCallbacks LuaBindings::makeChatCallbacks(MainInterface* mainInterface, UniverseClient* client) {
|
||||
LuaCallbacks callbacks;
|
||||
|
||||
auto chat = as<Chat>(mainInterface->paneManager()->registeredPane(MainInterfacePanes::Chat).get());
|
||||
|
||||
callbacks.registerCallback("send", [chat, client](String const& message, Maybe<String> modeName, Maybe<bool> speak) {
|
||||
auto sendMode = modeName ? ChatSendModeNames.getLeft(*modeName) : ChatSendMode::Broadcast;
|
||||
client->sendChat(message, sendMode, speak);
|
||||
});
|
||||
|
||||
// just for SE compat - this shoulda been a utility callback :moyai:
|
||||
callbacks.registerCallback("parseArguments", [](String const& args) {
|
||||
return Json::parseSequence(args);
|
||||
});
|
||||
|
||||
callbacks.registerCallback("command", [mainInterface](String const& command) -> StringList {
|
||||
return mainInterface->commandProcessor()->handleCommand(command);
|
||||
});
|
||||
|
||||
callbacks.registerCallback("addMessage", [client, chat](String const& text, Maybe<Json> config) {
|
||||
ChatReceivedMessage message({MessageContext::Mode::CommandResult, ""}, client->clientContext()->connectionId(), "", text);
|
||||
if (config) {
|
||||
if (auto mode = config->optString("mode"))
|
||||
message.context.mode = MessageContextModeNames.getLeft(*mode);
|
||||
if (auto channelName = config->optString("channelName"))
|
||||
message.context.channelName = std::move(*channelName);
|
||||
if (auto portrait = config->optString("portrait"))
|
||||
message.portrait = std::move(*portrait);
|
||||
if (auto fromNick = config->optString("fromNick"))
|
||||
message.fromNick = std::move(*fromNick);
|
||||
}
|
||||
chat->addMessages({std::move(message)}, config ? config->getBool("showPane", true) : true);
|
||||
});
|
||||
|
||||
callbacks.registerCallback("input", [chat]() -> String {
|
||||
return chat->currentChat();
|
||||
});
|
||||
|
||||
callbacks.registerCallback("setInput", [chat](String const& text, Maybe<bool> moveCursor) -> bool {
|
||||
return chat->setCurrentChat(text, moveCursor.value(false));
|
||||
});
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,9 +5,11 @@
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(MainInterface);
|
||||
STAR_CLASS(UniverseClient);
|
||||
|
||||
namespace LuaBindings {
|
||||
LuaCallbacks makeInterfaceCallbacks(MainInterface* mainInterface);
|
||||
LuaCallbacks makeChatCallbacks(MainInterface* mainInterface, UniverseClient* client);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -160,10 +160,8 @@ MainInterface::MainInterface(UniverseClientPtr client, WorldPainterPtr painter,
|
||||
|
||||
auto planetName = make_shared<Pane>();
|
||||
m_planetText = make_shared<LabelWidget>();
|
||||
m_planetText->setFontSize(m_config->planetNameFontSize);
|
||||
m_planetText->setFontMode(FontMode::Normal);
|
||||
m_planetText->setTextStyle(m_config->planetNameTextStyle);
|
||||
m_planetText->setAnchor(HorizontalAnchor::HMidAnchor, VerticalAnchor::VMidAnchor);
|
||||
m_planetText->setDirectives(m_config->planetNameDirectives);
|
||||
planetName->disableScissoring();
|
||||
planetName->setPosition(m_config->planetNameOffset);
|
||||
planetName->setAnchor(PaneAnchor::Center);
|
||||
@ -810,9 +808,7 @@ void MainInterface::renderInWorldElements() {
|
||||
if (m_disableHud)
|
||||
return;
|
||||
|
||||
m_guiContext->setDefaultFont();
|
||||
m_guiContext->setFontProcessingDirectives("");
|
||||
m_guiContext->setFontColor(Vec4B::filled(255));
|
||||
m_guiContext->clearTextStyle();
|
||||
m_questIndicatorPainter->render();
|
||||
m_nameplatePainter->render();
|
||||
m_chatBubbleManager->render();
|
||||
@ -822,9 +818,7 @@ void MainInterface::render() {
|
||||
if (m_disableHud)
|
||||
return;
|
||||
|
||||
m_guiContext->setDefaultFont();
|
||||
m_guiContext->setFontProcessingDirectives("");
|
||||
m_guiContext->setFontColor(Vec4B::filled(255));
|
||||
m_guiContext->clearTextStyle();
|
||||
renderBreath();
|
||||
renderMessages();
|
||||
renderMonsterHealthBar();
|
||||
@ -950,6 +944,10 @@ CanvasWidgetPtr MainInterface::fetchCanvas(String const& canvasName, bool ignore
|
||||
return canvas;
|
||||
}
|
||||
|
||||
ClientCommandProcessorPtr MainInterface::commandProcessor() const {
|
||||
return m_clientCommandProcessor;
|
||||
}
|
||||
|
||||
// For when the player swaps characters. We need to completely reload ScriptPanes,
|
||||
// because a lot of ScriptPanes do not expect the character to suddenly change and may break or spill data over.
|
||||
void MainInterface::takeScriptPanes(List<ScriptPaneInfo>& out) {
|
||||
@ -1084,9 +1082,7 @@ void MainInterface::renderMessages() {
|
||||
m_guiContext->drawQuad(m_config->messageTextContainer,
|
||||
RectF::withCenter(backgroundTextCenterPos, Vec2F(imgMetadata->imageSize(m_config->messageTextContainer) * interfaceScale())));
|
||||
|
||||
m_guiContext->setFont(m_config->font);
|
||||
m_guiContext->setFontSize(m_config->fontSize);
|
||||
m_guiContext->setFontColor(Color::White.toRgba());
|
||||
m_guiContext->setTextStyle(m_config->textStyle);
|
||||
m_guiContext->renderText(message->message, {messageTextOffset, HorizontalAnchor::HMidAnchor, VerticalAnchor::VMidAnchor});
|
||||
}
|
||||
}
|
||||
@ -1112,9 +1108,7 @@ void MainInterface::renderMonsterHealthBar() {
|
||||
m_guiContext->drawQuad(container, RectF::withCenter(backgroundCenterPos + offset, Vec2F(imgMetadata->imageSize(container) * interfaceScale())));
|
||||
|
||||
auto nameTextOffset = jsonToVec2F(assets->json("/interface.config:monsterHealth.nameTextOffset")) * interfaceScale();
|
||||
m_guiContext->setFont(m_config->font);
|
||||
m_guiContext->setFontSize(m_config->fontSize);
|
||||
m_guiContext->setFontColor(Color::White.toRgba());
|
||||
m_guiContext->setTextStyle(m_config->textStyle);
|
||||
m_guiContext->renderText(showDamageEntity->name(), backgroundCenterPos + nameTextOffset);
|
||||
|
||||
auto empty = assets->json("/interface.config:monsterHealth.progressEmpty").toString();
|
||||
@ -1176,11 +1170,11 @@ void MainInterface::renderSpecialDamageBar() {
|
||||
m_guiContext->drawQuad(fill, RectF::withSize(bottomCenter + fillOffset, size * interfaceScale()));
|
||||
|
||||
auto nameOffset = jsonToVec2F(barConfig.get("nameOffset")) * interfaceScale();
|
||||
m_guiContext->setFontColor(jsonToColor(barConfig.get("nameColor")).toRgba());
|
||||
m_guiContext->setFontColor(jsonToColor(barConfig.get("nameStyle")).toRgba());
|
||||
m_guiContext->setFontSize(barConfig.getUInt("nameSize"));
|
||||
m_guiContext->setFontProcessingDirectives(barConfig.getString("nameDirectives"));
|
||||
m_guiContext->renderText(target->name(), TextPositioning(bottomCenter + nameOffset, HorizontalAnchor::HMidAnchor, VerticalAnchor::BottomAnchor));
|
||||
m_guiContext->setFontProcessingDirectives("");
|
||||
m_guiContext->clearTextStyle();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1341,12 +1335,8 @@ void MainInterface::renderDebug() {
|
||||
|
||||
if (m_clientCommandProcessor->debugHudEnabled()) {
|
||||
auto assets = Root::singleton().assets();
|
||||
m_guiContext->setFontSize(m_config->debugFontSize);
|
||||
m_guiContext->setFont(m_config->debugFont);
|
||||
m_guiContext->setTextStyle(m_config->debugTextStyle);
|
||||
m_guiContext->setLineSpacing(0.5f);
|
||||
m_guiContext->setFontProcessingDirectives(m_config->debugFontDirectives);
|
||||
m_guiContext->setFontColor(Color::White.toRgba());
|
||||
m_guiContext->setFontMode(FontMode::Normal);
|
||||
|
||||
bool clearMap = m_debugMapClearTimer.wrapTick();
|
||||
auto logMapValues = LogMap::getValues();
|
||||
@ -1358,7 +1348,7 @@ void MainInterface::renderDebug() {
|
||||
|
||||
int counter = 0;
|
||||
for (auto const& pair : logMapValues) {
|
||||
TextPositioning positioning = { Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * counter++) };
|
||||
TextPositioning positioning = { Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->textStyle.fontSize * interfaceScale() * counter++) };
|
||||
String& text = formatted.emplace_back(strf("{}^lightgray;:^green,set; {}", pair.first, pair.second));
|
||||
m_debugTextRect.combine(m_guiContext->determineTextSize(text, positioning).padded(m_config->debugBackgroundPad));
|
||||
}
|
||||
@ -1373,15 +1363,9 @@ void MainInterface::renderDebug() {
|
||||
m_debugTextRect = RectF::null();
|
||||
|
||||
for (size_t index = 0; index != formatted.size(); ++index) {
|
||||
TextPositioning positioning = { Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * index) };
|
||||
TextPositioning positioning = { Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->textStyle.fontSize * interfaceScale() * index) };
|
||||
m_guiContext->renderText(formatted[index], positioning);
|
||||
}
|
||||
|
||||
m_guiContext->setFontSize(8);
|
||||
m_guiContext->setDefaultFont();
|
||||
m_guiContext->setDefaultLineSpacing();
|
||||
m_guiContext->setFontColor(Vec4B::filled(255));
|
||||
m_guiContext->setFontProcessingDirectives("");
|
||||
}
|
||||
|
||||
auto const& camera = m_worldPainter->camera();
|
||||
@ -1413,7 +1397,7 @@ void MainInterface::renderDebug() {
|
||||
m_guiContext->drawLine(position + Vec2F(2, -2), position + Vec2F(-2, -2), point.color, 1);
|
||||
}
|
||||
|
||||
m_guiContext->setFontSize(m_config->debugFontSize);
|
||||
m_guiContext->setTextStyle(m_config->debugTextStyle);
|
||||
|
||||
for (auto const& logText : SpatialLogger::getText("world", clearSpatial)) {
|
||||
m_guiContext->setFontColor(logText.color);
|
||||
@ -1424,7 +1408,7 @@ void MainInterface::renderDebug() {
|
||||
m_guiContext->setFontColor(logText.color);
|
||||
m_guiContext->renderText(logText.text.utf8Ptr(), logText.position);
|
||||
}
|
||||
m_guiContext->setFontColor(Vec4B::filled(255));
|
||||
m_guiContext->clearTextStyle();
|
||||
}
|
||||
|
||||
void MainInterface::updateCursor() {
|
||||
@ -1483,24 +1467,26 @@ void MainInterface::renderCursor() {
|
||||
auto assets = Root::singleton().assets();
|
||||
auto imgDb = Root::singleton().imageMetadataDatabase();
|
||||
|
||||
auto backgroundImage = assets->json("/interface.config:cursorTooltip.background").toString();
|
||||
auto rawCursorOffset = jsonToVec2I(assets->json("/interface.config:cursorTooltip.offset"));
|
||||
auto config = assets->json("/interface.config:cursorTooltip");
|
||||
auto backgroundImage = config.getString("background");
|
||||
auto rawCursorOffset = jsonToVec2I(config.get("offset"));
|
||||
|
||||
Vec2I tooltipSize = Vec2I(imgDb->imageSize(backgroundImage)) * interfaceScale();
|
||||
Vec2I cursorOffset = (Vec2I{0, -m_cursor.size().y()} + rawCursorOffset) * cursorScale;
|
||||
Vec2I tooltipOffset = m_cursorScreenPos + cursorOffset;
|
||||
size_t fontSize = assets->json("/interface.config:cursorTooltip.fontSize").toUInt();
|
||||
String font = assets->json("/interface.config:cursorTooltip.font").toString();
|
||||
Vec4B fontColor = jsonToColor(assets->json("/interface.config:cursorTooltip.color")).toRgba();
|
||||
TextStyle textStyle = config.get("textStyle");
|
||||
size_t fontSize = config.get("fontSize").toUInt();
|
||||
Vec4B fontColor = jsonToColor(config.get("color")).toRgba();
|
||||
|
||||
m_guiContext->drawQuad(backgroundImage, Vec2F(tooltipOffset) + Vec2F(-tooltipSize.x(), 0), interfaceScale());
|
||||
m_guiContext->setTextStyle(textStyle);
|
||||
m_guiContext->setFontSize(fontSize);
|
||||
m_guiContext->setFontColor(fontColor);
|
||||
m_guiContext->setFont(font);
|
||||
m_guiContext->renderText(*m_cursorTooltip,
|
||||
TextPositioning(Vec2F(tooltipOffset) + Vec2F(-tooltipSize.x(), tooltipSize.y()) / 2,
|
||||
HorizontalAnchor::HMidAnchor,
|
||||
VerticalAnchor::VMidAnchor));
|
||||
m_guiContext->clearTextStyle();
|
||||
}
|
||||
|
||||
m_cursorItem->setPosition(m_cursorScreenPos / interfaceScale() + m_config->inventoryItemMouseOffset);
|
||||
|
@ -119,6 +119,8 @@ public:
|
||||
|
||||
CanvasWidgetPtr fetchCanvas(String const& canvasName, bool ignoreInterfaceScale = false);
|
||||
|
||||
ClientCommandProcessorPtr commandProcessor() const;
|
||||
|
||||
struct ScriptPaneInfo {
|
||||
ScriptPanePtr scriptPane;
|
||||
Json config;
|
||||
|
@ -39,8 +39,7 @@ MainInterfaceConfigPtr MainInterfaceConfig::loadFromAssets() {
|
||||
|
||||
auto config = make_shared<MainInterfaceConfig>();
|
||||
|
||||
config->fontSize = assets->json("/interface.config:font.baseSize").toInt();
|
||||
config->font = assets->json("/interface.config:font.defaultFont").toString();
|
||||
config->textStyle = assets->json("/interface.config:textStyle");
|
||||
config->inventoryImage = assets->json("/interface.config:mainBar.inventory.base").toString();
|
||||
config->inventoryImageHover = assets->json("/interface.config:mainBar.inventory.hover").toString();
|
||||
config->inventoryImageGlow = assets->json("/interface.config:mainBar.inventory.glow").toString();
|
||||
@ -130,17 +129,14 @@ MainInterfaceConfigPtr MainInterfaceConfig::loadFromAssets() {
|
||||
config->planetNameTime = assets->json("/interface.config:planetNameTime").toFloat();
|
||||
config->planetNameFadeTime = assets->json("/interface.config:planetNameFadeTime").toFloat();
|
||||
config->planetNameFormatString = assets->json("/interface.config:planetNameFormatString").toString();
|
||||
config->planetNameFontSize = assets->json("/interface.config:font.planetSize").toInt();
|
||||
config->planetNameDirectives = assets->json("/interface.config:planetNameDirectives").toString();
|
||||
config->planetNameTextStyle = assets->json("/interface.config:planetTextStyle");
|
||||
config->planetNameOffset = jsonToVec2I(assets->json("/interface.config:planetTextOffset"));
|
||||
|
||||
config->renderVirtualCursor = assets->json("/interface.config:renderVirtualCursor").toBool();
|
||||
config->cursorItemSlot = assets->json("/interface.config:cursorItemSlot");
|
||||
|
||||
config->debugOffset = jsonToVec2I(assets->json("/interface.config:debugOffset"));
|
||||
config->debugFontSize = assets->json("/interface.config:debugFontSize").toUInt();
|
||||
config->debugFont = assets->json("/interface.config:debugFont").toString();
|
||||
config->debugFontDirectives = assets->json("/interface.config:debugFontDirectives").toString();
|
||||
config->debugTextStyle = assets->json("/interface.config:debugTextStyle");
|
||||
config->debugSpatialClearTime = assets->json("/interface.config:debugSpatialClearTime").toFloat();
|
||||
config->debugMapClearTime = assets->json("/interface.config:debugMapClearTime").toFloat();
|
||||
config->debugBackgroundColor = jsonToColor(assets->json("/interface.config:debugBackgroundColor"));
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "StarBiMap.hpp"
|
||||
#include "StarRegisteredPaneManager.hpp"
|
||||
#include "StarAnimation.hpp"
|
||||
#include "StarText.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
@ -43,8 +44,7 @@ typedef RegisteredPaneManager<MainInterfacePanes> MainInterfacePaneManager;
|
||||
struct MainInterfaceConfig {
|
||||
static MainInterfaceConfigPtr loadFromAssets();
|
||||
|
||||
unsigned fontSize;
|
||||
String font;
|
||||
TextStyle textStyle;
|
||||
|
||||
String inventoryImage;
|
||||
String inventoryImageHover;
|
||||
@ -136,17 +136,14 @@ struct MainInterfaceConfig {
|
||||
float planetNameTime;
|
||||
float planetNameFadeTime;
|
||||
String planetNameFormatString;
|
||||
unsigned planetNameFontSize;
|
||||
String planetNameDirectives;
|
||||
TextStyle planetNameTextStyle;
|
||||
Vec2I planetNameOffset;
|
||||
|
||||
bool renderVirtualCursor;
|
||||
Json cursorItemSlot;
|
||||
|
||||
Vec2I debugOffset;
|
||||
unsigned debugFontSize;
|
||||
String debugFont;
|
||||
String debugFontDirectives;
|
||||
TextStyle debugTextStyle;
|
||||
float debugSpatialClearTime;
|
||||
float debugMapClearTime;
|
||||
Color debugBackgroundColor;
|
||||
|
@ -15,14 +15,11 @@ NameplatePainter::NameplatePainter() {
|
||||
m_opacityRate = nametagConfig.getFloat("opacityRate");
|
||||
m_inspectOpacityRate = nametagConfig.queryFloat("inspectOpacityRate", m_opacityRate);
|
||||
m_offset = jsonToVec2F(nametagConfig.get("offset"));
|
||||
m_font = nametagConfig.queryString("font", "");
|
||||
m_fontDirectives = nametagConfig.queryString("fontDirectives", "");
|
||||
m_fontSize = nametagConfig.getFloat("fontSize");
|
||||
m_statusFont = nametagConfig.queryString("font", m_font);
|
||||
m_statusFontDirectives = nametagConfig.queryString("fontDirectives", m_fontDirectives);
|
||||
m_statusFontSize = nametagConfig.queryFloat("statusFontSize", m_fontSize);
|
||||
Json textStyle = nametagConfig.get("textStyle");
|
||||
m_textStyle = textStyle;
|
||||
m_statusTextStyle = nametagConfig.get("statusTextStyle", textStyle);
|
||||
m_statusOffset = jsonToVec2F(nametagConfig.get("statusOffset"));
|
||||
m_statusColor = jsonToColor(nametagConfig.get("statusColor"));
|
||||
m_statusTextStyle.color = jsonToColor(nametagConfig.get("statusColor")).toRgba();
|
||||
m_opacityBoost = nametagConfig.getFloat("opacityBoost");
|
||||
m_nametags.setTweenFactor(nametagConfig.getFloat("tweenFactor"));
|
||||
m_nametags.setMovementThreshold(nametagConfig.getFloat("movementThreshold"));
|
||||
@ -80,31 +77,18 @@ void NameplatePainter::render() {
|
||||
if (nametag.opacity == 0.0f)
|
||||
return;
|
||||
|
||||
context.setFont(m_font);
|
||||
context.setFontProcessingDirectives(m_fontDirectives);
|
||||
context.setFontSize(m_fontSize);
|
||||
|
||||
auto& setStyle = context.setTextStyle(m_textStyle);
|
||||
auto color = Color::rgb(nametag.color);
|
||||
color.setAlphaF(nametag.opacity);
|
||||
context.setFontColor(color.toRgba());
|
||||
context.setFontMode(FontMode::Normal);
|
||||
setStyle.color = color.toRgba();
|
||||
|
||||
context.renderText(nametag.name, namePosition(bubble.currentPosition));
|
||||
|
||||
if (nametag.statusText) {
|
||||
auto statusColor = m_statusColor;
|
||||
statusColor.setAlphaF(nametag.opacity);
|
||||
context.setFontColor(statusColor.toRgba());
|
||||
|
||||
context.setFontSize(m_statusFontSize);
|
||||
context.setFontProcessingDirectives(m_statusFontDirectives);
|
||||
context.setFont(m_statusFont);
|
||||
|
||||
context.setTextStyle(m_statusTextStyle).color[3] *= nametag.opacity;
|
||||
context.renderText(*nametag.statusText, statusPosition(bubble.currentPosition));
|
||||
}
|
||||
|
||||
context.setDefaultFont();
|
||||
context.setFontProcessingDirectives("");
|
||||
context.clearTextStyle();
|
||||
});
|
||||
}
|
||||
|
||||
@ -121,16 +105,13 @@ TextPositioning NameplatePainter::statusPosition(Vec2F bubblePosition) const {
|
||||
|
||||
RectF NameplatePainter::determineBoundBox(Vec2F bubblePosition, Nametag const& nametag) const {
|
||||
auto& context = GuiContext::singleton();
|
||||
context.setFontSize(m_fontSize);
|
||||
context.setFontProcessingDirectives(m_fontDirectives);
|
||||
context.setFont(m_font);
|
||||
context.setTextStyle(m_textStyle);
|
||||
RectF nametagBox = context.determineTextSize(nametag.name, namePosition(bubblePosition));
|
||||
if (nametag.statusText) {
|
||||
context.setFontSize(m_statusFontSize);
|
||||
context.setFontProcessingDirectives(m_statusFontDirectives);
|
||||
context.setFont(m_statusFont);
|
||||
context.setTextStyle(m_statusTextStyle);
|
||||
nametagBox.combine(context.determineTextSize(*nametag.statusText, statusPosition(bubblePosition)));
|
||||
}
|
||||
context.clearTextStyle();
|
||||
return nametagBox;
|
||||
}
|
||||
|
||||
|
@ -34,14 +34,9 @@ private:
|
||||
float m_opacityRate;
|
||||
float m_inspectOpacityRate;
|
||||
Vec2F m_offset;
|
||||
String m_font;
|
||||
String m_statusFont;
|
||||
String m_fontDirectives;
|
||||
String m_statusFontDirectives;
|
||||
float m_fontSize;
|
||||
float m_statusFontSize;
|
||||
Vec2F m_statusOffset;
|
||||
Color m_statusColor;
|
||||
TextStyle m_textStyle;
|
||||
TextStyle m_statusTextStyle;
|
||||
float m_opacityBoost;
|
||||
|
||||
WorldCamera m_camera;
|
||||
|
@ -29,7 +29,8 @@ TeamBar::TeamBar(MainInterface* mainInterface, UniverseClientPtr client) {
|
||||
m_teamInvitation = make_shared<TeamInvitation>(this);
|
||||
m_teamMemberMenu = make_shared<TeamMemberMenu>(this);
|
||||
|
||||
m_nameFontSize = assets->json("/interface.config:font.nameSize").toInt();
|
||||
m_nameStyle = assets->json("/interface.config:teamBarNameStyle");
|
||||
m_nameStyle.fontSize = assets->json("/interface.config:font.nameSize").toInt();
|
||||
m_nameOffset = jsonToVec2F(assets->json("/interface.config:nameOffset"));
|
||||
|
||||
GuiReader reader;
|
||||
|
@ -93,7 +93,7 @@ private:
|
||||
|
||||
GuiContext* m_guiContext;
|
||||
|
||||
int m_nameFontSize;
|
||||
TextStyle m_nameStyle;
|
||||
Vec2F m_nameOffset;
|
||||
|
||||
TeamInvitePtr m_teamInvite;
|
||||
|
@ -96,4 +96,12 @@ ByteArray ClientContext::writeUpdate() {
|
||||
return m_rpc->send();
|
||||
}
|
||||
|
||||
void ClientContext::setConnectionId(ConnectionId connectionId) {
|
||||
m_connectionId = connectionId;
|
||||
}
|
||||
|
||||
ConnectionId ClientContext::connectionId() const {
|
||||
return m_connectionId;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,9 +43,13 @@ public:
|
||||
void readUpdate(ByteArray data);
|
||||
ByteArray writeUpdate();
|
||||
|
||||
void setConnectionId(ConnectionId connectionId);
|
||||
ConnectionId connectionId() const;
|
||||
|
||||
private:
|
||||
Uuid m_serverUuid;
|
||||
Uuid m_playerUuid;
|
||||
ConnectionId m_connectionId = 0;
|
||||
|
||||
JsonRpcPtr m_rpc;
|
||||
|
||||
|
@ -1382,11 +1382,11 @@ pair<Vec2F, Directives> Humanoid::extractScaleFromDirectives(Directives const& d
|
||||
size_t totalLength = 0;
|
||||
Maybe<Vec2F> scale;
|
||||
|
||||
for (auto& entry : directives.shared->entries) {
|
||||
auto string = entry.string(*directives.shared);
|
||||
for (auto& entry : directives->entries) {
|
||||
auto string = entry.string(*directives);
|
||||
const ScaleImageOperation* op = nullptr;
|
||||
if (string.beginsWith("scalenearest") && string.utf8().find("skip") == NPos)
|
||||
op = entry.loadOperation(*directives.shared).ptr<ScaleImageOperation>();
|
||||
op = entry.loadOperation(*directives).ptr<ScaleImageOperation>();
|
||||
|
||||
if (op)
|
||||
scale = scale.value(Vec2F::filled(1.f)).piecewiseMultiply(op->scale);
|
||||
|
@ -132,7 +132,7 @@ AssetPath ImageMetadataDatabase::filterProcessing(AssetPath const& path) {
|
||||
operation.is<ScanLinesImageOperation>() ||
|
||||
operation.is<SetColorImageOperation>())) {
|
||||
filtered += "?";
|
||||
filtered += entry.string(*directives.shared);
|
||||
filtered += entry.string(*directives);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -62,7 +62,7 @@ void ParallaxLayer::addImageDirectives(Directives const& newDirectives) {
|
||||
if (directives) {
|
||||
String dirString = directives.string();
|
||||
|
||||
auto& newString = newDirectives.shared->string;
|
||||
auto& newString = newDirectives->string;
|
||||
if (!newString.empty()) {
|
||||
if (newString.utf8().front() != '?')
|
||||
dirString += "?";
|
||||
|
@ -440,8 +440,8 @@ bool UniverseClient::flying() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void UniverseClient::sendChat(String const& text, ChatSendMode sendMode) {
|
||||
if (!text.beginsWith("/"))
|
||||
void UniverseClient::sendChat(String const& text, ChatSendMode sendMode, Maybe<bool> speak) {
|
||||
if (speak.value(!text.beginsWith("/")))
|
||||
m_mainPlayer->addChatMessage(text);
|
||||
m_connection->pushSingle(make_shared<ChatSendPacket>(text, sendMode));
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public:
|
||||
SkyConstPtr currentSky() const;
|
||||
bool flying() const;
|
||||
|
||||
void sendChat(String const& text, ChatSendMode sendMode);
|
||||
void sendChat(String const& text, ChatSendMode sendMode, Maybe<bool> speak = {});
|
||||
List<ChatReceivedMessage> pullChatMessages();
|
||||
|
||||
uint16_t players();
|
||||
|
@ -1734,6 +1734,7 @@ void WorldClient::initWorld(WorldStartPacket const& startPacket) {
|
||||
m_entityUpdateTimer = GameTimer(m_interpolationTracker.entityUpdateDelta());
|
||||
|
||||
m_clientId = startPacket.clientId;
|
||||
m_mainPlayer->clientContext()->setConnectionId(startPacket.clientId);
|
||||
auto entitySpace = connectionEntitySpace(startPacket.clientId);
|
||||
m_worldTemplate = make_shared<WorldTemplate>(startPacket.templateData);
|
||||
m_entityMap = make_shared<EntityMap>(m_worldTemplate->size(), entitySpace.first, entitySpace.second);
|
||||
|
@ -47,11 +47,12 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha
|
||||
m_font->setPixelSize(size);
|
||||
auto pair = m_font->render(c);
|
||||
Image& image = pair.first;
|
||||
if (processingDirectives && *processingDirectives) {
|
||||
if (processingDirectives) {
|
||||
try {
|
||||
Directives const& directives = *processingDirectives;
|
||||
Vec2F preSize = Vec2F(image.size());
|
||||
|
||||
for (auto& entry : processingDirectives->shared->entries)
|
||||
for (auto& entry : directives->entries)
|
||||
processImageOperation(entry.operation, image);
|
||||
|
||||
res.first->second.offset = (preSize - Vec2F(image.size())) / 2;
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "StarTextPainter.hpp"
|
||||
#include "StarJsonExtra.hpp"
|
||||
#include "StarText.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace Star {
|
||||
@ -40,34 +38,42 @@ TextPositioning TextPositioning::translated(Vec2F translation) const {
|
||||
TextPainter::TextPainter(RendererPtr renderer, TextureGroupPtr textureGroup)
|
||||
: m_renderer(renderer),
|
||||
m_fontTextureGroup(textureGroup),
|
||||
m_fontSize(8),
|
||||
m_lineSpacing(1.30f),
|
||||
m_renderSettings({FontMode::Normal, Vec4B::filled(255), "hobo", ""}) {
|
||||
m_defaultRenderSettings(),
|
||||
m_renderSettings(),
|
||||
m_savedRenderSettings() {
|
||||
reloadFonts();
|
||||
m_reloadTracker = make_shared<TrackerListener>();
|
||||
Root::singleton().registerReloadListener(m_reloadTracker);
|
||||
}
|
||||
|
||||
RectF TextPainter::renderText(StringView s, TextPositioning const& position) {
|
||||
RectF rect;
|
||||
if (position.charLimit) {
|
||||
unsigned charLimit = *position.charLimit;
|
||||
return doRenderText(s, position, true, &charLimit);
|
||||
rect = doRenderText(s, position, true, &charLimit);
|
||||
} else {
|
||||
return doRenderText(s, position, true, nullptr);
|
||||
rect = doRenderText(s, position, true, nullptr);
|
||||
}
|
||||
renderPrimitives();
|
||||
return rect;
|
||||
}
|
||||
|
||||
RectF TextPainter::renderLine(StringView s, TextPositioning const& position) {
|
||||
RectF rect;
|
||||
if (position.charLimit) {
|
||||
unsigned charLimit = *position.charLimit;
|
||||
return doRenderLine(s, position, true, &charLimit);
|
||||
rect = doRenderLine(s, position, true, &charLimit);
|
||||
} else {
|
||||
return doRenderLine(s, position, true, nullptr);
|
||||
rect = doRenderLine(s, position, true, nullptr);
|
||||
}
|
||||
renderPrimitives();
|
||||
return rect;
|
||||
}
|
||||
|
||||
RectF TextPainter::renderGlyph(String::Char c, TextPositioning const& position) {
|
||||
return doRenderGlyph(c, position, true);
|
||||
auto rect = doRenderGlyph(c, position, true);
|
||||
renderPrimitives();
|
||||
return rect;
|
||||
}
|
||||
|
||||
RectF TextPainter::determineTextSize(StringView s, TextPositioning const& position) {
|
||||
@ -83,34 +89,36 @@ RectF TextPainter::determineGlyphSize(String::Char c, TextPositioning const& pos
|
||||
}
|
||||
|
||||
int TextPainter::glyphWidth(String::Char c) {
|
||||
return m_fontTextureGroup.glyphWidth(c, m_fontSize);
|
||||
return m_fontTextureGroup.glyphWidth(c, m_renderSettings.fontSize);
|
||||
}
|
||||
|
||||
int TextPainter::stringWidth(StringView s) {
|
||||
int TextPainter::stringWidth(StringView s, unsigned charLimit) {
|
||||
if (s.empty())
|
||||
return 0;
|
||||
|
||||
String font = m_renderSettings.font, setFont = font;
|
||||
m_fontTextureGroup.switchFont(font);
|
||||
|
||||
int width = 0;
|
||||
|
||||
Text::CommandsCallback commandsCallback = [&](StringView commands) {
|
||||
commands.forEachSplitView(",", [&](StringView command, size_t, size_t) {
|
||||
if (command == "reset")
|
||||
if (command == "reset") {
|
||||
m_fontTextureGroup.switchFont(font = setFont);
|
||||
else if (command == "set")
|
||||
} else if (command == "set") {
|
||||
setFont = font;
|
||||
else if (command.beginsWith("font="))
|
||||
} else if (command.beginsWith("font=")) {
|
||||
m_fontTextureGroup.switchFont(font = command.substr(5));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
int width = 0;
|
||||
Text::TextCallback textCallback = [&](StringView text) {
|
||||
for (String::Char c : text)
|
||||
for (String::Char c : text) {
|
||||
width += glyphWidth(c);
|
||||
|
||||
if (charLimit && --charLimit == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -122,7 +130,7 @@ int TextPainter::stringWidth(StringView s) {
|
||||
bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapTextCallback textFunc) {
|
||||
String font = m_renderSettings.font, setFont = font;
|
||||
m_fontTextureGroup.switchFont(font);
|
||||
int lines = 0;
|
||||
unsigned lines = 0;
|
||||
|
||||
auto it = text.begin();
|
||||
auto end = text.end();
|
||||
@ -149,14 +157,15 @@ bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapText
|
||||
|
||||
if (escIt != end) {
|
||||
if (character == Text::EndEsc) {
|
||||
StringView inner = slice(escIt, it);
|
||||
StringView inner = slice(++escIt, it);
|
||||
inner.forEachSplitView(",", [&](StringView command, size_t, size_t) {
|
||||
if (command == "reset")
|
||||
if (command == "reset") {
|
||||
m_fontTextureGroup.switchFont(font = setFont);
|
||||
else if (command == "set")
|
||||
} else if (command == "set") {
|
||||
setFont = font;
|
||||
else if (command.beginsWith("font="))
|
||||
} else if (command.beginsWith("font=")) {
|
||||
m_fontTextureGroup.switchFont(font = command.substr(5));
|
||||
}
|
||||
});
|
||||
escIt = end;
|
||||
}
|
||||
@ -177,7 +186,6 @@ bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapText
|
||||
splitIt = end;
|
||||
} else {
|
||||
int charWidth = glyphWidth(character);
|
||||
|
||||
// is it a place where we might want to split the line ?
|
||||
if (character == ' ' || character == '\t') {
|
||||
splitIt = it;
|
||||
@ -220,36 +228,20 @@ bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapText
|
||||
|
||||
List<StringView> TextPainter::wrapTextViews(StringView s, Maybe<unsigned> wrapWidth) {
|
||||
List<StringView> views = {};
|
||||
|
||||
bool active = false;
|
||||
StringView current;
|
||||
int lastLine = 0;
|
||||
|
||||
auto addText = [&active, ¤t](StringView text) {
|
||||
// Merge views if they are adjacent
|
||||
if (active && current.utf8Ptr() + current.utf8Size() == text.utf8Ptr())
|
||||
current = StringView(current.utf8Ptr(), current.utf8Size() + text.utf8Size());
|
||||
else
|
||||
current = text;
|
||||
active = true;
|
||||
};
|
||||
|
||||
TextPainter::WrapTextCallback textCallback = [&](StringView text, int line) {
|
||||
if (lastLine != line) {
|
||||
views.push_back(current);
|
||||
lastLine = line;
|
||||
active = false;
|
||||
auto last = views.end();
|
||||
unsigned curLine = 0;
|
||||
TextPainter::WrapTextCallback textCallback = [&](StringView text, unsigned line) {
|
||||
if (line == curLine && last != views.end() && last->end() == text.begin()) {
|
||||
*last = StringView(last->utf8Ptr(), last->utf8Size() + text.utf8Size());
|
||||
} else {
|
||||
last = views.insert(views.end(), text);
|
||||
curLine = line;
|
||||
}
|
||||
|
||||
addText(text);
|
||||
return true;
|
||||
};
|
||||
|
||||
processWrapText(s, wrapWidth.ptr(), textCallback);
|
||||
|
||||
if (active)
|
||||
views.push_back(current);
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
@ -258,12 +250,11 @@ StringList TextPainter::wrapText(StringView s, Maybe<unsigned> wrapWidth) {
|
||||
|
||||
String current;
|
||||
int lastLine = 0;
|
||||
TextPainter::WrapTextCallback textCallback = [&](StringView text, int line) {
|
||||
TextPainter::WrapTextCallback textCallback = [&](StringView text, unsigned line) {
|
||||
if (lastLine != line) {
|
||||
result.append(std::move(current));
|
||||
lastLine = line;
|
||||
}
|
||||
|
||||
current += text;
|
||||
return true;
|
||||
};
|
||||
@ -277,40 +268,45 @@ StringList TextPainter::wrapText(StringView s, Maybe<unsigned> wrapWidth) {
|
||||
};
|
||||
|
||||
unsigned TextPainter::fontSize() const {
|
||||
return m_fontSize;
|
||||
return m_renderSettings.fontSize;
|
||||
}
|
||||
|
||||
void TextPainter::setFontSize(unsigned size) {
|
||||
m_fontSize = size;
|
||||
m_renderSettings.fontSize = size;
|
||||
}
|
||||
|
||||
void TextPainter::setLineSpacing(float lineSpacing) {
|
||||
m_lineSpacing = lineSpacing;
|
||||
m_renderSettings.lineSpacing = lineSpacing;
|
||||
}
|
||||
|
||||
void TextPainter::setMode(FontMode mode) {
|
||||
m_renderSettings.mode = mode;
|
||||
m_renderSettings.shadow = fontModeToColor(mode).toRgba();
|
||||
}
|
||||
|
||||
void TextPainter::setFontColor(Vec4B color) {
|
||||
m_renderSettings.color = std::move(color);
|
||||
}
|
||||
|
||||
void TextPainter::setProcessingDirectives(StringView directives) {
|
||||
m_renderSettings.directives = String(directives);
|
||||
if (m_renderSettings.directives) {
|
||||
m_renderSettings.directives.loadOperations();
|
||||
for (auto& entry : m_renderSettings.directives.shared->entries) {
|
||||
if (auto border = entry.operation.ptr<BorderImageOperation>())
|
||||
border->includeTransparent = true;
|
||||
}
|
||||
}
|
||||
void TextPainter::setProcessingDirectives(StringView directives, bool back) {
|
||||
Directives& target = back ? m_renderSettings.backDirectives : m_renderSettings.directives;
|
||||
modifyDirectives(target = String(directives));
|
||||
}
|
||||
|
||||
void TextPainter::setFont(String const& font) {
|
||||
m_renderSettings.font = font;
|
||||
}
|
||||
|
||||
TextStyle& TextPainter::setTextStyle(TextStyle const& textStyle) {
|
||||
TextStyle& style = m_renderSettings = textStyle;
|
||||
modifyDirectives(style.directives);
|
||||
modifyDirectives(style.backDirectives);
|
||||
return style;
|
||||
}
|
||||
|
||||
void TextPainter::clearTextStyle() {
|
||||
m_renderSettings = m_defaultRenderSettings;
|
||||
}
|
||||
|
||||
void TextPainter::addFont(FontPtr const& font, String const& name) {
|
||||
m_fontTextureGroup.addFont(font, name);
|
||||
}
|
||||
@ -348,16 +344,19 @@ void TextPainter::applyCommands(StringView unsplitCommands) {
|
||||
m_renderSettings = m_savedRenderSettings;
|
||||
} else if (command == "set") {
|
||||
m_savedRenderSettings = m_renderSettings;
|
||||
} else if (command == "shadow") {
|
||||
m_renderSettings.mode = (FontMode)((int)m_renderSettings.mode | (int)FontMode::Shadow);
|
||||
} else if (command.beginsWith("shadow")) {
|
||||
if (command.utf8Size() == 6)
|
||||
m_renderSettings.shadow = Color::Black.toRgba();
|
||||
else if (command[6] == '=')
|
||||
m_renderSettings.shadow = Color(command.substr(7)).toRgba();
|
||||
} else if (command == "noshadow") {
|
||||
m_renderSettings.mode = (FontMode)((int)m_renderSettings.mode & (-1 ^ (int)FontMode::Shadow));
|
||||
m_renderSettings.shadow = Color::Clear.toRgba();
|
||||
} else if (command.beginsWith("font=")) {
|
||||
m_renderSettings.font = command.substr(5);
|
||||
} else if (command.beginsWith("directives=")) {
|
||||
// Honestly this is really stupid but I just couldn't help myself
|
||||
// Should probably limit in the future
|
||||
setProcessingDirectives(command.substr(11));
|
||||
} else if (command.beginsWith("backdirectives=")) {
|
||||
setProcessingDirectives(command.substr(15), true);
|
||||
} else {
|
||||
// expects both #... sequences and plain old color names.
|
||||
Color c = Color(command);
|
||||
@ -370,6 +369,16 @@ void TextPainter::applyCommands(StringView unsplitCommands) {
|
||||
});
|
||||
}
|
||||
|
||||
void TextPainter::modifyDirectives(Directives& directives) {
|
||||
if (directives) {
|
||||
directives.loadOperations();
|
||||
for (auto& entry : directives->entries) {
|
||||
if (auto border = entry.operation.ptr<BorderImageOperation>())
|
||||
border->includeTransparent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RectF TextPainter::doRenderText(StringView s, TextPositioning const& position, bool reallyRender, unsigned* charLimit) {
|
||||
Vec2F pos = position.pos;
|
||||
if (s.empty())
|
||||
@ -377,26 +386,23 @@ RectF TextPainter::doRenderText(StringView s, TextPositioning const& position, b
|
||||
|
||||
List<StringView> lines = wrapTextViews(s, position.wrapWidth);
|
||||
|
||||
int height = (lines.size() - 1) * m_lineSpacing * m_fontSize + m_fontSize;
|
||||
|
||||
RenderSettings backupRenderSettings = m_renderSettings;
|
||||
m_savedRenderSettings = m_renderSettings;
|
||||
|
||||
TextStyle backup = m_savedRenderSettings = m_renderSettings;
|
||||
int height = (lines.size() - 1) * backup.lineSpacing * backup.fontSize + backup.fontSize;
|
||||
if (position.vAnchor == VerticalAnchor::BottomAnchor)
|
||||
pos[1] += (height - m_fontSize);
|
||||
pos[1] += (height - backup.fontSize);
|
||||
else if (position.vAnchor == VerticalAnchor::VMidAnchor)
|
||||
pos[1] += (height - m_fontSize) / 2;
|
||||
pos[1] += (height - backup.fontSize) / 2;
|
||||
|
||||
RectF bounds = RectF::withSize(pos, Vec2F());
|
||||
for (auto& i : lines) {
|
||||
bounds.combine(doRenderLine(i, { pos, position.hAnchor, position.vAnchor }, reallyRender, charLimit));
|
||||
pos[1] -= m_fontSize * m_lineSpacing;
|
||||
pos[1] -= m_renderSettings.fontSize * m_renderSettings.lineSpacing;
|
||||
|
||||
if (charLimit && *charLimit == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
m_renderSettings = std::move(backupRenderSettings);
|
||||
m_renderSettings = std::move(backup);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
@ -425,9 +431,8 @@ RectF TextPainter::doRenderLine(StringView text, TextPositioning const& position
|
||||
if (*charLimit == 0)
|
||||
return false;
|
||||
else
|
||||
--* charLimit;
|
||||
--*charLimit;
|
||||
}
|
||||
|
||||
RectF glyphBounds = doRenderGlyph(c, pos, reallyRender);
|
||||
bounds.combine(glyphBounds);
|
||||
pos.pos[0] += glyphBounds.width();
|
||||
@ -461,41 +466,58 @@ RectF TextPainter::doRenderGlyph(String::Char c, TextPositioning const& position
|
||||
|
||||
float vOffset = 0;
|
||||
if (position.vAnchor == VerticalAnchor::VMidAnchor)
|
||||
vOffset = -floor((float)m_fontSize / 2);
|
||||
vOffset = -floor((float)m_renderSettings.fontSize / 2);
|
||||
else if (position.vAnchor == VerticalAnchor::TopAnchor)
|
||||
vOffset = -(float)m_fontSize;
|
||||
vOffset = -(float)m_renderSettings.fontSize;
|
||||
|
||||
Directives* directives = m_renderSettings.directives ? &m_renderSettings.directives : nullptr;
|
||||
|
||||
Vec2F pos = position.pos + Vec2F(hOffset, vOffset);
|
||||
if (reallyRender) {
|
||||
if ((int)m_renderSettings.mode & (int)FontMode::Shadow) {
|
||||
Color shadow = Color::Black;
|
||||
uint8_t alphaU = m_renderSettings.color[3];
|
||||
bool hasShadow = m_renderSettings.shadow[3] > 0;
|
||||
bool hasBackDirectives = m_renderSettings.backDirectives;
|
||||
if (hasShadow) {
|
||||
//Kae: unlike vanilla we draw only one shadow glyph instead of two, so i'm tweaking the alpha here
|
||||
Vec4B shadow = m_renderSettings.shadow;
|
||||
uint8_t alphaU = m_renderSettings.color[3] * byteToFloat(shadow[3]);
|
||||
if (alphaU != 255) {
|
||||
float alpha = byteToFloat(alphaU);
|
||||
shadow.setAlpha(floatToByte(alpha * (1.5f - 0.5f * alpha)));
|
||||
shadow[3] = floatToByte(alpha * (1.5f - 0.5f * alpha));
|
||||
}
|
||||
else
|
||||
shadow.setAlpha(alphaU);
|
||||
shadow[3] = alphaU;
|
||||
|
||||
//Kae: Draw only one shadow glyph instead of stacking two, alpha modified to appear perceptually the same as vanilla
|
||||
renderGlyph(c, position.pos + Vec2F(hOffset, vOffset - 2), m_fontSize, 1, shadow.toRgba(), directives);
|
||||
Directives const* shadowDirectives = hasBackDirectives ? &m_renderSettings.backDirectives : directives;
|
||||
renderGlyph(c, pos + Vec2F(0, -2), m_shadowPrimitives, m_renderSettings.fontSize, 1, shadow, shadowDirectives);
|
||||
}
|
||||
if (hasBackDirectives)
|
||||
renderGlyph(c, pos, m_backPrimitives, m_renderSettings.fontSize, 1, m_renderSettings.color, &m_renderSettings.backDirectives);
|
||||
|
||||
renderGlyph(c, position.pos + Vec2F(hOffset, vOffset), m_fontSize, 1, m_renderSettings.color, directives);
|
||||
auto& output = (hasShadow || hasBackDirectives) ? m_frontPrimitives : m_renderer->immediatePrimitives();
|
||||
renderGlyph(c, pos, output, m_renderSettings.fontSize, 1, m_renderSettings.color, directives);
|
||||
}
|
||||
|
||||
return RectF::withSize(position.pos + Vec2F(hOffset, vOffset), {(float)width, (int)m_fontSize});
|
||||
return RectF::withSize(pos, {(float)width, (int)m_renderSettings.fontSize});
|
||||
}
|
||||
|
||||
void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, unsigned fontSize,
|
||||
void TextPainter::renderPrimitives() {
|
||||
auto& destination = m_renderer->immediatePrimitives();
|
||||
std::move(std::begin(m_shadowPrimitives), std::end(m_shadowPrimitives), std::back_inserter(destination));
|
||||
m_shadowPrimitives.clear();
|
||||
std::move(std::begin(m_backPrimitives), std::end(m_backPrimitives), std::back_inserter(destination));
|
||||
m_backPrimitives.clear();
|
||||
std::move(std::begin(m_frontPrimitives), std::end(m_frontPrimitives), std::back_inserter(destination));
|
||||
m_frontPrimitives.clear();
|
||||
}
|
||||
|
||||
void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, List<RenderPrimitive>& out, unsigned fontSize,
|
||||
float scale, Vec4B const& color, Directives const* processingDirectives) {
|
||||
if (!fontSize)
|
||||
return;
|
||||
|
||||
const FontTextureGroup::GlyphTexture& glyphTexture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives);
|
||||
Vec2F offset = glyphTexture.offset * scale;
|
||||
m_renderer->immediatePrimitives().emplace_back(std::in_place_type_t<RenderQuad>(), glyphTexture.texture, Vec2F::round(screenPos + offset), scale, color, 0.0f);
|
||||
out.emplace_back(std::in_place_type_t<RenderQuad>(), glyphTexture.texture, Vec2F::round(screenPos + offset), scale, color, 0.0f);
|
||||
}
|
||||
|
||||
FontPtr TextPainter::loadFont(String const& fontPath, Maybe<String> fontName) {
|
||||
|
@ -4,17 +4,21 @@
|
||||
#include "StarAnchorTypes.hpp"
|
||||
#include "StarRoot.hpp"
|
||||
#include "StarStringView.hpp"
|
||||
#include "StarText.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
STAR_CLASS(TextPainter);
|
||||
|
||||
enum class FontMode {
|
||||
// deprecated in favor of explicit shadow color
|
||||
enum class FontMode : uint8_t {
|
||||
Normal,
|
||||
Shadow
|
||||
};
|
||||
|
||||
float const DefaultLineSpacing = 1.3f;
|
||||
inline Color const& fontModeToColor(FontMode mode) {
|
||||
return mode == FontMode::Shadow ? Color::Black : Color::Clear;
|
||||
}
|
||||
|
||||
STAR_CLASS(TextPainter);
|
||||
|
||||
struct TextPositioning {
|
||||
TextPositioning();
|
||||
@ -52,10 +56,10 @@ public:
|
||||
RectF determineGlyphSize(String::Char c, TextPositioning const& position);
|
||||
|
||||
int glyphWidth(String::Char c);
|
||||
int stringWidth(StringView s);
|
||||
int stringWidth(StringView s, unsigned charLimit = 0);
|
||||
|
||||
|
||||
typedef function<bool(StringView, int)> WrapTextCallback;
|
||||
typedef function<bool(StringView, unsigned)> WrapTextCallback;
|
||||
bool processWrapText(StringView s, unsigned* wrapWidth, WrapTextCallback textFunc);
|
||||
|
||||
List<StringView> wrapTextViews(StringView s, Maybe<unsigned> wrapWidth);
|
||||
@ -66,39 +70,36 @@ public:
|
||||
void setLineSpacing(float lineSpacing);
|
||||
void setMode(FontMode mode);
|
||||
void setFontColor(Vec4B color);
|
||||
void setProcessingDirectives(StringView directives);
|
||||
void setProcessingDirectives(StringView directives, bool back = false);
|
||||
void setFont(String const& font);
|
||||
TextStyle& setTextStyle(TextStyle const& textStyle);
|
||||
void clearTextStyle();
|
||||
void addFont(FontPtr const& font, String const& name);
|
||||
void reloadFonts();
|
||||
|
||||
void cleanup(int64_t textureTimeout);
|
||||
void applyCommands(StringView unsplitCommands);
|
||||
private:
|
||||
struct RenderSettings {
|
||||
FontMode mode;
|
||||
Vec4B color;
|
||||
String font;
|
||||
Directives directives;
|
||||
};
|
||||
|
||||
void modifyDirectives(Directives& directives);
|
||||
RectF doRenderText(StringView s, TextPositioning const& position, bool reallyRender, unsigned* charLimit);
|
||||
RectF doRenderLine(StringView s, TextPositioning const& position, bool reallyRender, unsigned* charLimit);
|
||||
RectF doRenderGlyph(String::Char c, TextPositioning const& position, bool reallyRender);
|
||||
|
||||
void renderGlyph(String::Char c, Vec2F const& screenPos, unsigned fontSize, float scale, Vec4B const& color, Directives const* processingDirectives = nullptr);
|
||||
void renderPrimitives();
|
||||
void renderGlyph(String::Char c, Vec2F const& screenPos, List<RenderPrimitive>& out, unsigned fontSize, float scale, Vec4B const& color, Directives const* processingDirectives = nullptr);
|
||||
static FontPtr loadFont(String const& fontPath, Maybe<String> fontName = {});
|
||||
|
||||
RendererPtr m_renderer;
|
||||
List<RenderPrimitive> m_shadowPrimitives;
|
||||
List<RenderPrimitive> m_backPrimitives;
|
||||
List<RenderPrimitive> m_frontPrimitives;
|
||||
FontTextureGroup m_fontTextureGroup;
|
||||
|
||||
unsigned m_fontSize;
|
||||
float m_lineSpacing;
|
||||
|
||||
RenderSettings m_renderSettings;
|
||||
RenderSettings m_savedRenderSettings;
|
||||
TextStyle m_defaultRenderSettings;
|
||||
TextStyle m_renderSettings;
|
||||
TextStyle m_savedRenderSettings;
|
||||
|
||||
String m_nonRenderedCharacters;
|
||||
|
||||
TrackerListenerPtr m_reloadTracker;
|
||||
};
|
||||
|
||||
|
@ -24,14 +24,11 @@ ButtonWidget::ButtonWidget() {
|
||||
|
||||
auto interfaceConfig = assets->json("/interface.config");
|
||||
m_pressedOffset = jsonToVec2I(interfaceConfig.get("buttonPressedOffset"));
|
||||
m_fontSize = interfaceConfig.query("font.buttonSize").toInt();
|
||||
m_fontDirectives = interfaceConfig.queryString("font.defaultDirectives", "");
|
||||
m_font = interfaceConfig.query("font.defaultFont").toString();
|
||||
|
||||
m_clickSounds = jsonToStringList(assets->json("/interface.config:buttonClickSound"));
|
||||
m_releaseSounds = jsonToStringList(assets->json("/interface.config:buttonReleaseSound"));
|
||||
m_hoverSounds = jsonToStringList(assets->json("/interface.config:buttonHoverSound"));
|
||||
m_hoverOffSounds = jsonToStringList(assets->json("/interface.config:buttonHoverOffSound"));
|
||||
m_textStyle = interfaceConfig.get("buttonTextStyle");
|
||||
m_clickSounds = jsonToStringList(interfaceConfig.get("buttonClickSound"));
|
||||
m_releaseSounds = jsonToStringList(interfaceConfig.get("buttonReleaseSound"));
|
||||
m_hoverSounds = jsonToStringList(interfaceConfig.get("buttonHoverSound"));
|
||||
m_hoverOffSounds = jsonToStringList(interfaceConfig.get("buttonHoverOffSound"));
|
||||
}
|
||||
|
||||
ButtonWidget::ButtonWidget(WidgetCallbackFunc callback,
|
||||
@ -95,19 +92,15 @@ void ButtonWidget::renderImpl() {
|
||||
|
||||
if (!m_text.empty()) {
|
||||
auto& guiContext = GuiContext::singleton();
|
||||
guiContext.setFontProcessingDirectives(m_fontDirectives);
|
||||
guiContext.setFontSize(m_fontSize);
|
||||
guiContext.setFont(m_font);
|
||||
guiContext.setTextStyle(m_textStyle);
|
||||
if (m_disabled)
|
||||
guiContext.setFontColor(m_fontColorDisabled.toRgba());
|
||||
else if (m_fontColorChecked && m_checked)
|
||||
guiContext.setFontColor(m_fontColorChecked.value().toRgba());
|
||||
else
|
||||
guiContext.setFontColor(m_fontColor.toRgba());
|
||||
guiContext.setFontMode(FontMode::Shadow);
|
||||
guiContext.renderInterfaceText(m_text, {textPosition, m_hTextAnchor, VerticalAnchor::VMidAnchor});
|
||||
guiContext.setFontMode(FontMode::Normal);
|
||||
guiContext.setFontProcessingDirectives("");
|
||||
guiContext.clearTextStyle();
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,11 +316,11 @@ void ButtonWidget::setText(String const& text) {
|
||||
}
|
||||
|
||||
void ButtonWidget::setFontSize(int size) {
|
||||
m_fontSize = size;
|
||||
m_textStyle.fontSize = size;
|
||||
}
|
||||
|
||||
void ButtonWidget::setFontDirectives(String directives) {
|
||||
m_fontDirectives = directives;
|
||||
m_textStyle.directives = directives;
|
||||
}
|
||||
|
||||
void ButtonWidget::setTextOffset(Vec2I textOffset) {
|
||||
@ -339,7 +332,7 @@ void ButtonWidget::setTextAlign(HorizontalAnchor hAnchor) {
|
||||
}
|
||||
|
||||
void ButtonWidget::setFontColor(Color color) {
|
||||
m_fontColor = color;
|
||||
m_textStyle.color = (m_fontColor = color).toRgba();
|
||||
}
|
||||
|
||||
void ButtonWidget::setFontColorDisabled(Color color) {
|
||||
|
@ -121,9 +121,7 @@ protected:
|
||||
Vec2I m_pressedOffset;
|
||||
Vec2U m_buttonBoundSize;
|
||||
|
||||
int m_fontSize;
|
||||
String m_font;
|
||||
String m_fontDirectives;
|
||||
TextStyle m_textStyle;
|
||||
String m_text;
|
||||
Vec2I m_textOffset;
|
||||
|
||||
|
@ -68,7 +68,18 @@ void CanvasWidget::drawTriangles(List<tuple<Vec2F, Vec2F, Vec2F>> const& triangl
|
||||
}
|
||||
|
||||
void CanvasWidget::drawText(String s, TextPositioning position, unsigned fontSize, Vec4B const& color, FontMode mode, float lineSpacing, String font, String processingDirectives) {
|
||||
m_renderOps.append(make_tuple(std::move(s), std::move(position), fontSize, color, mode, lineSpacing, std::move(font), std::move(processingDirectives)));
|
||||
TextStyle style;
|
||||
style.fontSize = fontSize;
|
||||
style.color = color;
|
||||
style.shadow = fontModeToColor(mode).toRgba();
|
||||
style.lineSpacing = lineSpacing;
|
||||
style.font = font;
|
||||
style.directives = processingDirectives;
|
||||
m_renderOps.append(make_tuple(std::move(s), std::move(position), std::move(style)));
|
||||
}
|
||||
|
||||
void CanvasWidget::drawText(String s, TextPositioning position, TextStyle style) {
|
||||
m_renderOps.append(make_tuple(std::move(s), std::move(position), std::move(style)));
|
||||
}
|
||||
|
||||
Vec2I CanvasWidget::mousePosition() const {
|
||||
@ -145,7 +156,7 @@ void CanvasWidget::renderImpl() {
|
||||
if (auto args = op.ptr<TrianglesOp>())
|
||||
tupleUnpackFunction(bind(&CanvasWidget::renderTriangles, this, renderingOffset, _1, _2), *args);
|
||||
if (auto args = op.ptr<TextOp>())
|
||||
tupleUnpackFunction(bind(&CanvasWidget::renderText, this, renderingOffset, _1, _2, _3, _4, _5, _6, _7, _8), *args);
|
||||
tupleUnpackFunction(bind(&CanvasWidget::renderText, this, renderingOffset, _1, _2, _3), *args);
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,14 +267,9 @@ void CanvasWidget::renderTriangles(Vec2F const& renderingOffset, List<tuple<Vec2
|
||||
context.drawInterfaceTriangles(translated, color);
|
||||
}
|
||||
|
||||
void CanvasWidget::renderText(Vec2F const& renderingOffset, String const& s, TextPositioning const& position, unsigned fontSize, Vec4B const& color, FontMode mode, float lineSpacing, String const& font, String const& directives) {
|
||||
void CanvasWidget::renderText(Vec2F const& renderingOffset, String const& s, TextPositioning const& position, TextStyle const& style) {
|
||||
auto& context = GuiContext::singleton();
|
||||
context.setFontProcessingDirectives(directives);
|
||||
context.setFontSize(fontSize, m_ignoreInterfaceScale ? 1 : context.interfaceScale());
|
||||
context.setFontColor(color);
|
||||
context.setFontMode(mode);
|
||||
context.setFont(font);
|
||||
context.setLineSpacing(lineSpacing);
|
||||
context.setTextStyle(style, m_ignoreInterfaceScale ? 1 : context.interfaceScale());
|
||||
|
||||
TextPositioning translatedPosition = position;
|
||||
translatedPosition.pos += renderingOffset;
|
||||
@ -272,10 +278,7 @@ void CanvasWidget::renderText(Vec2F const& renderingOffset, String const& s, Tex
|
||||
else
|
||||
context.renderInterfaceText(s, translatedPosition);
|
||||
|
||||
context.setDefaultLineSpacing();
|
||||
context.setDefaultFont();
|
||||
context.setFontMode(FontMode::Normal);
|
||||
context.setFontProcessingDirectives("");
|
||||
context.clearTextStyle();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ public:
|
||||
void drawTriangles(List<tuple<Vec2F, Vec2F, Vec2F>> const& poly, Vec4B const& color = Vec4B(255, 255, 255, 255));
|
||||
|
||||
void drawText(String s, TextPositioning position, unsigned fontSize, Vec4B const& color = Vec4B(255, 255, 255, 255), FontMode mode = FontMode::Normal, float lineSpacing = Star::DefaultLineSpacing, String font = "", String processingDirectives = "");
|
||||
void drawText(String s, TextPositioning position, TextStyle style);
|
||||
|
||||
protected:
|
||||
void renderImpl() override;
|
||||
@ -79,7 +80,7 @@ protected:
|
||||
void renderRect(Vec2F const& renderingOffset, RectF const& coords, Vec4B const& color);
|
||||
void renderPoly(Vec2F const& renderingOffset, PolyF poly, Vec4B const& color, float lineWidth);
|
||||
void renderTriangles(Vec2F const& renderingOffset, List<tuple<Vec2F, Vec2F, Vec2F>> const& triangles, Vec4B const& color);
|
||||
void renderText(Vec2F const& renderingOffset, String const& s, TextPositioning const& position, unsigned fontSize, Vec4B const& color, FontMode mode, float lineSpacing, String const& font, String const& directives);
|
||||
void renderText(Vec2F const& renderingOffset, String const& s, TextPositioning const& position, TextStyle const& style);
|
||||
|
||||
private:
|
||||
bool m_ignoreInterfaceScale;
|
||||
@ -97,7 +98,7 @@ private:
|
||||
typedef tuple<Vec2F, Vec2F, Vec4B, float> LineOp;
|
||||
typedef tuple<PolyF, Vec4B, float> PolyOp;
|
||||
typedef tuple<List<tuple<Vec2F, Vec2F, Vec2F>>, Vec4B> TrianglesOp;
|
||||
typedef tuple<String, TextPositioning, unsigned, Vec4B, FontMode, float, String, String> TextOp;
|
||||
typedef tuple<String, TextPositioning, TextStyle> TextOp;
|
||||
|
||||
typedef MVariant<RectOp, ImageOp, ImageRectOp, DrawableOp, TiledImageOp, LineOp, PolyOp, TrianglesOp, TextOp> RenderOp;
|
||||
List<RenderOp> m_renderOps;
|
||||
|
@ -9,8 +9,8 @@ namespace Star {
|
||||
FuelWidget::FuelWidget() {
|
||||
auto assets = Root::singleton().assets();
|
||||
|
||||
m_fontSize = assets->json("/interface.config:font.buttonSize").toInt();
|
||||
m_font = assets->json("/interface.config:font.defaultFont").toString();
|
||||
m_textStyle.fontSize = assets->json("/interface.config:font.buttonSize").toInt();
|
||||
m_textStyle.loadJson(assets->json("/interface.config:textStyle"));
|
||||
|
||||
m_fuelLevel = 0;
|
||||
m_maxLevel = 0;
|
||||
@ -72,8 +72,7 @@ void FuelWidget::renderImpl() {
|
||||
context()->drawInterfaceQuad("/interface/fuel/fuelgaugemarkings.png", shift(0, 1, entireTex), shift(0, 1, entirePosition));
|
||||
|
||||
auto& guiContext = GuiContext::singleton();
|
||||
guiContext.setFontSize(m_fontSize);
|
||||
guiContext.setFont(m_font);
|
||||
guiContext.setTextStyle(m_textStyle);
|
||||
if (m_potential != 0) {
|
||||
guiContext.setFontColor(Color::White.toRgba());
|
||||
} else if (m_fuelLevel == 0) {
|
||||
|
@ -30,8 +30,7 @@ protected:
|
||||
|
||||
float m_pingTimeout;
|
||||
|
||||
unsigned m_fontSize;
|
||||
String m_font;
|
||||
TextStyle m_textStyle;
|
||||
|
||||
private:
|
||||
};
|
||||
|
@ -377,6 +377,20 @@ void GuiContext::setDefaultFont() {
|
||||
textPainter()->setFont("");
|
||||
}
|
||||
|
||||
TextStyle& GuiContext::setTextStyle(TextStyle const& textStyle, int pixelRatio) {
|
||||
TextStyle& setStyle = textPainter()->setTextStyle(textStyle);
|
||||
setStyle.fontSize *= pixelRatio;
|
||||
return setStyle;
|
||||
}
|
||||
|
||||
TextStyle& GuiContext::setTextStyle(TextStyle const& textStyle) {
|
||||
return setTextStyle(textStyle, interfaceScale());
|
||||
}
|
||||
|
||||
void GuiContext::clearTextStyle() {
|
||||
textPainter()->clearTextStyle();
|
||||
}
|
||||
|
||||
void GuiContext::setLineSpacing(float lineSpacing) {
|
||||
textPainter()->setLineSpacing(lineSpacing);
|
||||
}
|
||||
|
@ -106,6 +106,9 @@ public:
|
||||
void setFontProcessingDirectives(String const& directives);
|
||||
void setFont(String const& font);
|
||||
void setDefaultFont();
|
||||
TextStyle& setTextStyle(TextStyle const& textStyle, int pixelRatio);
|
||||
TextStyle& setTextStyle(TextStyle const& textStyle);
|
||||
void clearTextStyle();
|
||||
|
||||
void setLineSpacing(float lineSpacing);
|
||||
void setDefaultLineSpacing();
|
||||
|
@ -14,16 +14,13 @@ ItemSlotWidget::ItemSlotWidget(ItemPtr const& item, String const& backingImage)
|
||||
: m_item(item), m_backingImage(backingImage) {
|
||||
m_drawBackingImageWhenFull = false;
|
||||
m_drawBackingImageWhenEmpty = true;
|
||||
m_fontSize = 0;
|
||||
m_progress = 1;
|
||||
|
||||
auto assets = Root::singleton().assets();
|
||||
auto interfaceConfig = assets->json("/interface.config");
|
||||
m_countPosition = TextPositioning(jsonToVec2F(interfaceConfig.get("itemCountRightAnchor")), HorizontalAnchor::RightAnchor);
|
||||
m_countFontMode = FontMode::Normal;
|
||||
m_fontSize = interfaceConfig.query("font.itemSize").toInt();
|
||||
m_font = interfaceConfig.query("font.defaultFont").toString();
|
||||
m_fontColor = Color::rgb(jsonToVec3B(interfaceConfig.query("font.defaultColor")));
|
||||
m_textStyle = interfaceConfig.get("itemSlotTextStyle");
|
||||
m_itemDraggableArea = jsonToRectI(interfaceConfig.get("itemDraggableArea"));
|
||||
m_durabilityOffset = jsonToVec2I(interfaceConfig.get("itemIconDurabilityOffset"));
|
||||
|
||||
@ -192,13 +189,10 @@ void ItemSlotWidget::renderImpl() {
|
||||
context()->drawInterfaceQuad(String(strf("/interface/cooldown.png:{}", frame)), Vec2F(screenPosition()));
|
||||
|
||||
if (m_item->count() > 1 && m_showCount) { // we don't need to tell people that there's only 1 of something
|
||||
context()->setFont(m_font);
|
||||
context()->setFontSize(m_fontSize);
|
||||
context()->setFontColor(m_fontColor.toRgba());
|
||||
context()->setTextStyle(m_textStyle);
|
||||
context()->setFontMode(m_countFontMode);
|
||||
context()->renderInterfaceText(toString(m_item->count()), m_countPosition.translated(Vec2F(screenPosition())));
|
||||
context()->setFontMode(FontMode::Normal);
|
||||
context()->setDefaultFont();
|
||||
context()->clearTextStyle();
|
||||
}
|
||||
|
||||
} else if (m_drawBackingImageWhenEmpty && m_backingImage != "") {
|
||||
|
@ -56,9 +56,7 @@ private:
|
||||
Vec2I m_durabilityOffset;
|
||||
RectI m_itemDraggableArea;
|
||||
|
||||
int m_fontSize;
|
||||
String m_font;
|
||||
Color m_fontColor;
|
||||
TextStyle m_textStyle;
|
||||
|
||||
WidgetCallbackFunc m_callback;
|
||||
WidgetCallbackFunc m_rightClickCallback;
|
||||
|
@ -10,17 +10,14 @@ LabelWidget::LabelWidget(String text,
|
||||
VerticalAnchor const& vAnchor,
|
||||
Maybe<unsigned> wrapWidth,
|
||||
Maybe<float> lineSpacing)
|
||||
: m_fontMode(FontMode::Normal),
|
||||
m_color(color),
|
||||
m_hAnchor(hAnchor),
|
||||
: m_hAnchor(hAnchor),
|
||||
m_vAnchor(vAnchor),
|
||||
m_wrapWidth(std::move(wrapWidth)),
|
||||
m_lineSpacing(std::move(lineSpacing)) {
|
||||
m_wrapWidth(std::move(wrapWidth)) {
|
||||
auto assets = Root::singleton().assets();
|
||||
auto fontConfig = assets->json("/interface.config:font");
|
||||
m_fontSize = fontConfig.getInt("baseSize");
|
||||
m_processingDirectives = fontConfig.getString("defaultDirectives");
|
||||
m_font = fontConfig.queryString("defaultFont", "");
|
||||
m_style = assets->json("/interface.config:labelTextStyle");
|
||||
m_style.color = color.toRgba();
|
||||
if (lineSpacing)
|
||||
m_style.lineSpacing = *lineSpacing;
|
||||
setText(std::move(text));
|
||||
}
|
||||
|
||||
@ -38,16 +35,16 @@ void LabelWidget::setText(String newText) {
|
||||
}
|
||||
|
||||
void LabelWidget::setFontSize(int fontSize) {
|
||||
m_fontSize = fontSize;
|
||||
m_style.fontSize = fontSize;
|
||||
updateTextRegion();
|
||||
}
|
||||
|
||||
void LabelWidget::setFontMode(FontMode fontMode) {
|
||||
m_fontMode = fontMode;
|
||||
m_style.shadow = fontModeToColor(fontMode).toRgba();
|
||||
}
|
||||
|
||||
void LabelWidget::setColor(Color newColor) {
|
||||
m_color = std::move(newColor);
|
||||
m_style.color = newColor.toRgba();
|
||||
}
|
||||
|
||||
void LabelWidget::setAnchor(HorizontalAnchor hAnchor, VerticalAnchor vAnchor) {
|
||||
@ -62,12 +59,12 @@ void LabelWidget::setWrapWidth(Maybe<unsigned> wrapWidth) {
|
||||
}
|
||||
|
||||
void LabelWidget::setLineSpacing(Maybe<float> lineSpacing) {
|
||||
m_lineSpacing = std::move(lineSpacing);
|
||||
m_style.lineSpacing = lineSpacing.value(DefaultLineSpacing);
|
||||
updateTextRegion();
|
||||
}
|
||||
|
||||
void LabelWidget::setDirectives(String const& directives) {
|
||||
m_processingDirectives = directives;
|
||||
m_style.directives = directives;
|
||||
updateTextRegion();
|
||||
}
|
||||
|
||||
@ -76,6 +73,12 @@ void LabelWidget::setTextCharLimit(Maybe<unsigned> charLimit) {
|
||||
updateTextRegion();
|
||||
}
|
||||
|
||||
void LabelWidget::setTextStyle(TextStyle const& textStyle) {
|
||||
m_style = textStyle;
|
||||
updateTextRegion();
|
||||
}
|
||||
|
||||
|
||||
RectI LabelWidget::relativeBoundRect() const {
|
||||
return RectI(m_textRegion).translated(relativePosition());
|
||||
}
|
||||
@ -85,41 +88,14 @@ RectI LabelWidget::getScissorRect() const {
|
||||
}
|
||||
|
||||
void LabelWidget::renderImpl() {
|
||||
context()->setFont(m_font);
|
||||
context()->setFontSize(m_fontSize);
|
||||
context()->setFontMode(m_fontMode);
|
||||
context()->setFontColor(m_color.toRgba());
|
||||
context()->setFontProcessingDirectives(m_processingDirectives);
|
||||
|
||||
if (m_lineSpacing)
|
||||
context()->setLineSpacing(*m_lineSpacing);
|
||||
else
|
||||
context()->setDefaultLineSpacing();
|
||||
|
||||
context()->setTextStyle(m_style);
|
||||
context()->renderInterfaceText(m_text, {Vec2F(screenPosition()), m_hAnchor, m_vAnchor, m_wrapWidth, m_textCharLimit});
|
||||
|
||||
context()->setDefaultFont();
|
||||
context()->setFontMode(FontMode::Normal);
|
||||
context()->setFontProcessingDirectives("");
|
||||
context()->setDefaultLineSpacing();
|
||||
}
|
||||
|
||||
void LabelWidget::updateTextRegion() {
|
||||
context()->setFontSize(m_fontSize);
|
||||
context()->setFontColor(m_color.toRgba());
|
||||
context()->setFontProcessingDirectives(m_processingDirectives);
|
||||
context()->setFont(m_font);
|
||||
if (m_lineSpacing)
|
||||
context()->setLineSpacing(*m_lineSpacing);
|
||||
else
|
||||
context()->setDefaultLineSpacing();
|
||||
|
||||
context()->setTextStyle(m_style);
|
||||
m_textRegion = RectI(context()->determineInterfaceTextSize(m_text, {Vec2F(), m_hAnchor, m_vAnchor, m_wrapWidth, m_textCharLimit}));
|
||||
setSize(m_textRegion.size());
|
||||
|
||||
context()->setDefaultFont();
|
||||
context()->setFontProcessingDirectives("");
|
||||
context()->setDefaultLineSpacing();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ public:
|
||||
void setLineSpacing(Maybe<float> lineSpacing);
|
||||
void setDirectives(String const& directives);
|
||||
void setTextCharLimit(Maybe<unsigned> charLimit);
|
||||
void setTextStyle(TextStyle const& style);
|
||||
|
||||
RectI relativeBoundRect() const override;
|
||||
|
||||
@ -36,13 +37,9 @@ private:
|
||||
void updateTextRegion();
|
||||
|
||||
String m_text;
|
||||
int m_fontSize;
|
||||
FontMode m_fontMode;
|
||||
Color m_color;
|
||||
TextStyle m_style;
|
||||
HorizontalAnchor m_hAnchor;
|
||||
VerticalAnchor m_vAnchor;
|
||||
String m_processingDirectives;
|
||||
String m_font;
|
||||
Maybe<unsigned> m_wrapWidth;
|
||||
Maybe<float> m_lineSpacing;
|
||||
Maybe<unsigned> m_textCharLimit;
|
||||
|
@ -32,8 +32,7 @@ Pane::Pane() {
|
||||
m_hasDisplayed = false;
|
||||
|
||||
auto assets = Root::singleton().assets();
|
||||
m_fontSize = assets->json("/interface.config:font.baseSize").toInt();
|
||||
m_font = assets->json("/interface.config:font.defaultFont").toString();
|
||||
m_textStyle = assets->json("/interface.config:paneTextStyle");
|
||||
m_iconOffset = jsonToVec2I(assets->json("/interface.config:paneIconOffset"));
|
||||
m_titleOffset = jsonToVec2I(assets->json("/interface.config:paneTitleOffset"));
|
||||
m_subTitleOffset = jsonToVec2I(assets->json("/interface.config:paneSubTitleOffset"));
|
||||
@ -436,15 +435,13 @@ void Pane::renderImpl() {
|
||||
m_context->resetInterfaceScissorRect();
|
||||
}
|
||||
|
||||
m_context->setFont(m_font);
|
||||
m_context->setFontSize(m_fontSize);
|
||||
m_context->setTextStyle(m_textStyle);
|
||||
m_context->setFontColor(m_titleColor.toRgba());
|
||||
m_context->setFontMode(FontMode::Shadow);
|
||||
m_context->renderInterfaceText(m_title, {headerPos + Vec2F(m_titleOffset)});
|
||||
m_context->setFontColor(m_subTitleColor.toRgba());
|
||||
m_context->renderInterfaceText(m_subTitle, {headerPos + Vec2F(m_subTitleOffset)});
|
||||
m_context->setFontMode(FontMode::Normal);
|
||||
m_context->setDefaultFont();
|
||||
m_context->clearTextStyle();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,8 +117,7 @@ protected:
|
||||
WidgetPtr m_icon;
|
||||
String m_title;
|
||||
String m_subTitle;
|
||||
String m_font;
|
||||
unsigned m_fontSize;
|
||||
TextStyle m_textStyle;
|
||||
Vec2I m_iconOffset;
|
||||
Vec2I m_titleOffset;
|
||||
Vec2I m_subTitleOffset;
|
||||
|
@ -23,14 +23,11 @@ TextBoxWidget::TextBoxWidget(String const& startingText, String const& hint, Wid
|
||||
m_overfillMode = true;
|
||||
|
||||
m_maxWidth = assets->json("/interface.config:textBoxDefaultWidth").toInt();
|
||||
auto fontConfig = assets->json("/interface.config:font");
|
||||
m_fontSize = fontConfig.getInt("baseSize");
|
||||
m_processingDirectives = fontConfig.getString("defaultDirectives");
|
||||
m_font = fontConfig.queryString("defaultFont", "");
|
||||
m_color = Color::rgb(jsonToVec3B(fontConfig.getArray("defaultColor")));
|
||||
auto fontConfig = assets->json("/interface.config:textBoxTextStyle");
|
||||
m_textStyle = fontConfig;
|
||||
|
||||
// Meh, padding is hard-coded here
|
||||
setSize({m_maxWidth + 6, m_fontSize + 2});
|
||||
setSize({m_maxWidth + 6, m_textStyle.fontSize + 2});
|
||||
m_cursorHoriz = jsonToVec2I(assets->json("/interface.config:textboxCursorHorizontal"));
|
||||
m_cursorVert = jsonToVec2I(assets->json("/interface.config:textboxCursorVertical"));
|
||||
}
|
||||
@ -48,20 +45,17 @@ void TextBoxWidget::renderImpl() {
|
||||
else if (m_hAnchor == HorizontalAnchor::RightAnchor)
|
||||
pos += Vec2F(size()[0], 0);
|
||||
|
||||
context()->setFont(m_font);
|
||||
context()->setTextStyle(m_textStyle);
|
||||
if ((m_maxWidth != -1) && m_overfillMode) {
|
||||
context()->setFontSize(m_fontSize);
|
||||
int shift = std::max(0, getCursorOffset() - m_maxWidth);
|
||||
pos += Vec2F(-shift, 0);
|
||||
}
|
||||
|
||||
context()->setFontSize(m_fontSize);
|
||||
context()->setFontProcessingDirectives(m_processingDirectives);
|
||||
if (m_text.empty()) {
|
||||
context()->setFontColor(m_color.mix(Color::rgbf(0.3f, 0.3f, 0.3f), 0.8f).mix(Color::rgbf(0.0f, 0.0f, 1.0f), blueRate).toRgba());
|
||||
context()->setFontColor(Color::rgba(m_textStyle.color).mix(Color::rgbf(0.3f, 0.3f, 0.3f), 0.8f).mix(Color::rgbf(0.0f, 0.0f, 1.0f), blueRate).toRgba());
|
||||
context()->renderInterfaceText(m_hint, {pos, m_hAnchor, m_vAnchor});
|
||||
} else {
|
||||
context()->setFontColor(m_color.mix(Color::rgbf(0, 0, 1), blueRate).toRgba());
|
||||
context()->setFontColor(Color::rgba(m_textStyle.color).mix(Color::rgbf(0, 0, 1), blueRate).toRgba());
|
||||
if (m_textHidden) {
|
||||
String hiddenText('*', m_text.length());
|
||||
context()->renderInterfaceText(hiddenText, { pos, m_hAnchor, m_vAnchor });
|
||||
@ -69,22 +63,21 @@ void TextBoxWidget::renderImpl() {
|
||||
else
|
||||
context()->renderInterfaceText(m_text, { pos, m_hAnchor, m_vAnchor });
|
||||
}
|
||||
context()->setDefaultFont();
|
||||
context()->setFontProcessingDirectives("");
|
||||
context()->setFontColor(Vec4B::filled(255));
|
||||
context()->clearTextStyle();
|
||||
|
||||
if (hasFocus()) {
|
||||
// render cursor
|
||||
float cc = 0.6f + 0.4f * std::sin((double)Time::monotonicMilliseconds() / 300.0);
|
||||
Color cursorColor = Color::rgbf(cc, cc, cc);
|
||||
|
||||
float fontSize = m_textStyle.fontSize;
|
||||
context()->drawInterfaceLine(
|
||||
pos + Vec2F(getCursorOffset(), m_fontSize * m_cursorVert[0]),
|
||||
pos + Vec2F(getCursorOffset(), m_fontSize * m_cursorVert[1]),
|
||||
pos + Vec2F(getCursorOffset(), fontSize * m_cursorVert[0]),
|
||||
pos + Vec2F(getCursorOffset(), fontSize * m_cursorVert[1]),
|
||||
cursorColor.toRgba());
|
||||
context()->drawInterfaceLine(
|
||||
pos + Vec2F(getCursorOffset() + m_fontSize * m_cursorHoriz[0], m_fontSize * m_cursorVert[0]),
|
||||
pos + Vec2F(getCursorOffset() + m_fontSize * m_cursorHoriz[1], m_fontSize * m_cursorVert[0]),
|
||||
pos + Vec2F(getCursorOffset() + fontSize * m_cursorHoriz[0], fontSize * m_cursorVert[0]),
|
||||
pos + Vec2F(getCursorOffset() + fontSize * m_cursorHoriz[1], fontSize * m_cursorVert[0]),
|
||||
cursorColor.toRgba());
|
||||
}
|
||||
|
||||
@ -94,8 +87,7 @@ void TextBoxWidget::renderImpl() {
|
||||
|
||||
int TextBoxWidget::getCursorOffset() { // horizontal only
|
||||
float scale;
|
||||
context()->setFont(m_font);
|
||||
context()->setFontSize(m_fontSize);
|
||||
context()->setTextStyle(m_textStyle);
|
||||
if (m_hAnchor == HorizontalAnchor::LeftAnchor) {
|
||||
scale = 1.0;
|
||||
} else if (m_hAnchor == HorizontalAnchor::HMidAnchor) {
|
||||
@ -158,7 +150,7 @@ String const& TextBoxWidget::getText() const {
|
||||
return m_text;
|
||||
}
|
||||
|
||||
bool TextBoxWidget::setText(String const& text, bool callback) {
|
||||
bool TextBoxWidget::setText(String const& text, bool callback, bool moveCursor) {
|
||||
if (m_text == text)
|
||||
return true;
|
||||
|
||||
@ -166,7 +158,10 @@ bool TextBoxWidget::setText(String const& text, bool callback) {
|
||||
return false;
|
||||
|
||||
m_text = text;
|
||||
m_cursorOffset = m_text.size();
|
||||
size_t size = m_text.size();
|
||||
if (moveCursor || m_cursorOffset > size)
|
||||
m_cursorOffset = size;
|
||||
|
||||
m_repeatCode = SpecialRepeatKeyCodes::None;
|
||||
if (callback)
|
||||
m_callback(this);
|
||||
@ -190,20 +185,20 @@ void TextBoxWidget::setRegex(String const& regex) {
|
||||
}
|
||||
|
||||
void TextBoxWidget::setColor(Color const& color) {
|
||||
m_color = color;
|
||||
m_textStyle.color = color.toRgba();
|
||||
}
|
||||
|
||||
void TextBoxWidget::setDirectives(String const& directives) {
|
||||
m_processingDirectives = directives;
|
||||
m_textStyle.directives = directives;
|
||||
}
|
||||
|
||||
void TextBoxWidget::setFontSize(int fontSize) {
|
||||
m_fontSize = fontSize;
|
||||
m_textStyle.fontSize = fontSize;
|
||||
}
|
||||
|
||||
void TextBoxWidget::setMaxWidth(int maxWidth) {
|
||||
m_maxWidth = maxWidth;
|
||||
setSize({m_maxWidth + 6, m_fontSize + 2});
|
||||
setSize({m_maxWidth + 6, m_textStyle.fontSize + 2});
|
||||
}
|
||||
|
||||
void TextBoxWidget::setOverfillMode(bool overtype) {
|
||||
@ -426,8 +421,7 @@ bool TextBoxWidget::newTextValid(String const& text) const {
|
||||
if (!text.regexMatch(m_regex))
|
||||
return false;
|
||||
if ((m_maxWidth != -1) && !m_overfillMode) {
|
||||
context()->setFont(m_font);
|
||||
context()->setFontSize(m_fontSize);
|
||||
context()->setTextStyle(m_textStyle);
|
||||
return context()->stringInterfaceWidth(text) <= m_maxWidth;
|
||||
}
|
||||
return true;
|
||||
|
@ -14,7 +14,7 @@ public:
|
||||
virtual void update(float dt) override;
|
||||
|
||||
String const& getText() const;
|
||||
bool setText(String const& text, bool callback = true);
|
||||
bool setText(String const& text, bool callback = true, bool moveCursor = true);
|
||||
|
||||
bool getHidden() const;
|
||||
void setHidden(bool hidden);
|
||||
@ -64,10 +64,7 @@ private:
|
||||
String m_regex;
|
||||
HorizontalAnchor m_hAnchor;
|
||||
VerticalAnchor m_vAnchor;
|
||||
Color m_color;
|
||||
String m_processingDirectives;
|
||||
String m_font;
|
||||
int m_fontSize;
|
||||
TextStyle m_textStyle;
|
||||
int m_maxWidth;
|
||||
int m_cursorOffset;
|
||||
bool m_isHover;
|
||||
|
Loading…
Reference in New Issue
Block a user