Files
imhex/plugins/fonts/source/font_settings.cpp

308 lines
10 KiB
C++

#include <font_settings.hpp>
#include <hex/api/content_registry.hpp>
#include <wolv/utils/string.hpp>
#include <hex/helpers/utils.hpp>
#include <imgui.h>
#include <fonts/fonts.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/ui/imgui_imhex_extensions.h>
#include <romfs/romfs.hpp>
#include "hex/api/imhex_api.hpp"
namespace hex::fonts {
constexpr static auto PixelPerfectFontName = "Pixel-Perfect Default Font (Proggy Clean)";
constexpr static auto SmoothFontName = "Smooth Default Font (JetBrains Mono)";
constexpr static auto CustomFontName = "Custom Font";
static std::map<std::fs::path, ImFont*> s_previewFonts, s_usedFonts;
static void pushPreviewFont(const std::fs::path &fontPath) {
if (fontPath.empty()) {
return pushPreviewFont(SmoothFontName);
}
auto atlas = ImGui::GetIO().Fonts;
auto it = s_previewFonts.find(fontPath);
if (it == s_previewFonts.end()) {
ImFont *font = nullptr;
if (fontPath == PixelPerfectFontName) {
font = atlas->AddFontDefault();
} else if (fontPath == SmoothFontName || fontPath.empty()) {
static auto jetbrainsFont = romfs::get("fonts/JetBrainsMono.ttf");
ImFontConfig config = {};
config.FontDataOwnedByAtlas = false;
font = atlas->AddFontFromMemoryTTF(const_cast<u8 *>(jetbrainsFont.data<u8>()), jetbrainsFont.size(), 0.0F, &config);
} else {
font = atlas->AddFontFromFileTTF(wolv::util::toUTF8String(fontPath).c_str());
}
it = s_previewFonts.emplace(fontPath, font).first;
}
const auto &[path, font] = *it;
if (font == nullptr)
return pushPreviewFont(SmoothFontName);
else {
ImGui::PushFont(font, 0.0F);
s_usedFonts.emplace(path, font);
}
}
static void cleanUnusedPreviewFonts() {
auto atlas = ImGui::GetIO().Fonts;
for (const auto &[path, font] : s_previewFonts) {
if (font == nullptr)
continue;
if (!s_usedFonts.contains(path)) {
atlas->RemoveFont(font);
}
}
s_previewFonts = std::move(s_usedFonts);
s_usedFonts = {};
}
bool FontFilePicker::draw(const std::string &name) {
bool changed = false;
const bool pixelPerfectFont = isPixelPerfectFontSelected();
bool customFont = updateSelectedFontName();
if (ImGui::BeginCombo(name.c_str(), m_selectedFontName.c_str())) {
pushPreviewFont(PixelPerfectFontName);
if (ImGui::Selectable(PixelPerfectFontName, m_path.empty() && pixelPerfectFont)) {
m_path.clear();
m_pixelPerfectFont = true;
changed = true;
}
ImGui::PopFont();
pushPreviewFont(SmoothFontName);
if (ImGui::Selectable(SmoothFontName, m_path.empty() && !pixelPerfectFont)) {
m_path.clear();
m_pixelPerfectFont = false;
changed = true;
}
ImGui::PopFont();
pushPreviewFont(m_path);
if (ImGui::Selectable(CustomFontName, customFont)) {
changed = fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } }, [this](const std::fs::path &path) {
m_path = path;
m_pixelPerfectFont = false;
});
}
ImGui::PopFont();
{
u32 index = 0;
ImGuiListClipper clipper;
const auto fonts = hex::getFonts();
clipper.Begin(fonts.size());
while (clipper.Step())
for (const auto &[path, fontName] : fonts | std::views::drop(clipper.DisplayStart) | std::views::take(clipper.DisplayEnd - clipper.DisplayStart)) {
ImGui::PushID(index);
pushPreviewFont(path);
if (ImGui::Selectable(limitStringLength(fontName, 50).c_str(), m_path == path)) {
m_path = path;
m_pixelPerfectFont = false;
changed = true;
}
ImGui::SetItemTooltip("%s", fontName.c_str());
ImGui::PopFont();
ImGui::PopID();
index += 1;
}
}
ImGui::EndCombo();
}
TaskManager::doLaterOnce([] {
cleanUnusedPreviewFonts();
});
return changed;
}
bool FontFilePicker::isPixelPerfectFontSelected() const {
return m_pixelPerfectFont;
}
const std::string& FontFilePicker::getSelectedFontName() const {
return m_selectedFontName;
}
void FontFilePicker::load(const nlohmann::json& data) {
FilePicker::load(data["path"]);
m_pixelPerfectFont = data["pixel_perfect_font"];
updateSelectedFontName();
}
nlohmann::json FontFilePicker::store() {
nlohmann::json data = nlohmann::json::object();
data["path"] = FilePicker::store();
data["pixel_perfect_font"] = m_pixelPerfectFont;
return data;
}
bool FontFilePicker::updateSelectedFontName() {
const auto &fonts = hex::getFonts();
bool customFont = false;
const bool pixelPerfectFont = isPixelPerfectFontSelected();
if (m_path.empty() && pixelPerfectFont) {
m_selectedFontName = PixelPerfectFontName;
} else if (m_path.empty() && !pixelPerfectFont) {
m_selectedFontName = SmoothFontName;
} else if (fonts.contains(m_path)) {
m_selectedFontName = fonts.at(m_path);
} else {
m_selectedFontName = wolv::util::toUTF8String(m_path.filename());
customFont = true;
}
return customFont;
}
bool SliderPoints::draw(const std::string &name) {
auto scaleFactor = ImHexApi::System::getBackingScaleFactor();
float value = ImHexApi::Fonts::pixelsToPoints(m_value) * scaleFactor;
float min = ImHexApi::Fonts::pixelsToPoints(m_min) * scaleFactor;
float max = ImHexApi::Fonts::pixelsToPoints(m_max) * scaleFactor;
auto changed = ImGui::SliderFloat(name.c_str(), &value, min, max, "%.0f pt");
m_value = ImHexApi::Fonts::pointsToPixels(value / scaleFactor);
return changed;
}
bool FontSelector::draw(const std::string &name) {
ImGui::PushID(name.c_str());
ON_SCOPE_EXIT { ImGui::PopID(); };
bool changed = false;
if (ImGui::CollapsingHeader(name.c_str())) {
if (ImGuiExt::BeginBox()) {
if (m_fontFilePicker.draw("hex.fonts.setting.font.custom_font"_lang)) changed = true;
ImGui::BeginDisabled(m_fontFilePicker.isPixelPerfectFontSelected());
{
if (m_fontSize.draw("hex.fonts.setting.font.font_size"_lang)) changed = true;
if (m_bold.draw("hex.fonts.setting.font.font_bold"_lang)) changed = true;
if (m_italic.draw("hex.fonts.setting.font.font_italic"_lang)) changed = true;
if (m_antiAliased.draw("hex.fonts.setting.font.font_antialias"_lang)) changed = true;
}
ImGui::EndDisabled();
ImGuiExt::EndBox();
}
}
return changed;
}
nlohmann::json FontSelector::store() {
nlohmann::json json = nlohmann::json::object();
json["font_file"] = m_fontFilePicker.store();
json["font_size"] = m_fontSize.store();
json["bold"] = m_bold.store();
json["italic"] = m_italic.store();
json["antialiased"] = m_antiAliased.store();
return json;
}
void FontSelector::load(const nlohmann::json& data) {
m_fontFilePicker.load(data["font_file"]);
m_fontSize.load(data["font_size"]);
m_bold.load(data["bold"]);
m_italic.load(data["italic"]);
m_antiAliased.load(data["antialiased"]);
}
bool FontSelector::drawPopup() {
bool changed = false;
if (ImGui::BeginPopup("Fonts")) {
if (m_fontFilePicker.draw("hex.fonts.setting.font.custom_font"_lang)) m_applyEnabled = true;
ImGui::BeginDisabled(m_fontFilePicker.isPixelPerfectFontSelected());
{
if (m_fontSize.draw("hex.fonts.setting.font.font_size"_lang)) m_applyEnabled = true;
if (m_bold.draw("hex.fonts.setting.font.font_bold"_lang)) m_applyEnabled = true;
if (m_italic.draw("hex.fonts.setting.font.font_italic"_lang)) m_applyEnabled = true;
if (m_antiAliased.draw("hex.fonts.setting.font.font_antialias"_lang)) m_applyEnabled = true;
}
ImGui::EndDisabled();
ImGui::NewLine();
ImGui::BeginDisabled(!m_applyEnabled);
if (ImGui::Button("hex.ui.common.apply"_lang)) {
changed = true;
m_applyEnabled = false;
}
ImGui::EndDisabled();
ImGui::EndPopup();
}
return changed;
}
[[nodiscard]] const std::fs::path& FontSelector::getFontPath() const {
return m_fontFilePicker.getPath();
}
[[nodiscard]] bool FontSelector::isPixelPerfectFont() const {
return m_fontFilePicker.isPixelPerfectFontSelected();
}
[[nodiscard]] float FontSelector::getFontSize() const {
return m_fontSize.getValue();
}
[[nodiscard]] bool FontSelector::isBold() const {
return m_bold.isChecked();
}
[[nodiscard]] bool FontSelector::isItalic() const {
return m_italic.isChecked();
}
[[nodiscard]] AntialiasingType FontSelector::getAntialiasingType() const {
if (isPixelPerfectFont())
return AntialiasingType::None;
auto value = m_antiAliased.getValue();
if (value == "none")
return AntialiasingType::None;
else if (value == "grayscale")
return AntialiasingType::Grayscale;
else if (value == "subpixel")
return AntialiasingType::Lcd;
else
return AntialiasingType::Grayscale;
}
ContentRegistry::Settings::Widgets::Widget::Interface& addFontSettingsWidget(UnlocalizedString name) {
return ContentRegistry::Settings::add<FontSelector>("hex.fonts.setting.font", "hex.fonts.setting.font.custom_font", std::move(name));
}
}