@@ -20,7 +20,7 @@ struct LedDisplaySeparator : widget::Widget { | |||||
struct LedDisplayChoice : widget::OpaqueWidget { | struct LedDisplayChoice : widget::OpaqueWidget { | ||||
std::string text; | std::string text; | ||||
std::shared_ptr<Font> font; | |||||
std::string fontPath; | |||||
math::Vec textOffset; | math::Vec textOffset; | ||||
NVGcolor color; | NVGcolor color; | ||||
NVGcolor bgColor; | NVGcolor bgColor; | ||||
@@ -30,7 +30,7 @@ struct LedDisplayChoice : widget::OpaqueWidget { | |||||
}; | }; | ||||
struct LedDisplayTextField : ui::TextField { | struct LedDisplayTextField : ui::TextField { | ||||
std::shared_ptr<Font> font; | |||||
std::string fontPath; | |||||
math::Vec textOffset; | math::Vec textOffset; | ||||
NVGcolor color; | NVGcolor color; | ||||
LedDisplayTextField(); | LedDisplayTextField(); | ||||
@@ -58,10 +58,6 @@ struct Window { | |||||
float windowRatio = 1.f; | float windowRatio = 1.f; | ||||
std::shared_ptr<Font> uiFont; | std::shared_ptr<Font> uiFont; | ||||
/** Use load*() instead of modifying these directly. */ | |||||
std::map<std::string, std::weak_ptr<Font>> fontCache; | |||||
std::map<std::string, std::weak_ptr<Image>> imageCache; | |||||
Window(); | Window(); | ||||
~Window(); | ~Window(); | ||||
void run(); | void run(); | ||||
@@ -24,6 +24,7 @@ LedDisplaySeparator::LedDisplaySeparator() { | |||||
box.size = math::Vec(); | box.size = math::Vec(); | ||||
} | } | ||||
void LedDisplaySeparator::draw(const DrawArgs& args) { | void LedDisplaySeparator::draw(const DrawArgs& args) { | ||||
nvgBeginPath(args.vg); | nvgBeginPath(args.vg); | ||||
nvgMoveTo(args.vg, 0, 0); | nvgMoveTo(args.vg, 0, 0); | ||||
@@ -36,12 +37,13 @@ void LedDisplaySeparator::draw(const DrawArgs& args) { | |||||
LedDisplayChoice::LedDisplayChoice() { | LedDisplayChoice::LedDisplayChoice() { | ||||
box.size = mm2px(math::Vec(0, 28.0 / 3)); | box.size = mm2px(math::Vec(0, 28.0 / 3)); | ||||
font = APP->window->loadFont(asset::system("res/fonts/ShareTechMono-Regular.ttf")); | |||||
fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf"); | |||||
color = nvgRGB(0xff, 0xd7, 0x14); | color = nvgRGB(0xff, 0xd7, 0x14); | ||||
bgColor = nvgRGBAf(0, 0, 0, 0); | bgColor = nvgRGBAf(0, 0, 0, 0); | ||||
textOffset = math::Vec(10, 18); | textOffset = math::Vec(10, 18); | ||||
} | } | ||||
void LedDisplayChoice::draw(const DrawArgs& args) { | void LedDisplayChoice::draw(const DrawArgs& args) { | ||||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | ||||
if (bgColor.a > 0.0) { | if (bgColor.a > 0.0) { | ||||
@@ -51,7 +53,8 @@ void LedDisplayChoice::draw(const DrawArgs& args) { | |||||
nvgFill(args.vg); | nvgFill(args.vg); | ||||
} | } | ||||
if (font->handle >= 0) { | |||||
std::shared_ptr<Font> font = APP->window->loadFont(fontPath); | |||||
if (font && font->handle >= 0) { | |||||
nvgFillColor(args.vg, color); | nvgFillColor(args.vg, color); | ||||
nvgFontFaceId(args.vg, font->handle); | nvgFontFaceId(args.vg, font->handle); | ||||
nvgTextLetterSpacing(args.vg, 0.0); | nvgTextLetterSpacing(args.vg, 0.0); | ||||
@@ -62,6 +65,7 @@ void LedDisplayChoice::draw(const DrawArgs& args) { | |||||
nvgResetScissor(args.vg); | nvgResetScissor(args.vg); | ||||
} | } | ||||
void LedDisplayChoice::onButton(const ButtonEvent& e) { | void LedDisplayChoice::onButton(const ButtonEvent& e) { | ||||
OpaqueWidget::onButton(e); | OpaqueWidget::onButton(e); | ||||
@@ -74,7 +78,7 @@ void LedDisplayChoice::onButton(const ButtonEvent& e) { | |||||
LedDisplayTextField::LedDisplayTextField() { | LedDisplayTextField::LedDisplayTextField() { | ||||
font = APP->window->loadFont(asset::system("res/fonts/ShareTechMono-Regular.ttf")); | |||||
fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf"); | |||||
color = nvgRGB(0xff, 0xd7, 0x14); | color = nvgRGB(0xff, 0xd7, 0x14); | ||||
textOffset = math::Vec(5, 5); | textOffset = math::Vec(5, 5); | ||||
} | } | ||||
@@ -90,16 +94,18 @@ void LedDisplayTextField::draw(const DrawArgs& args) { | |||||
nvgFill(args.vg); | nvgFill(args.vg); | ||||
// Text | // Text | ||||
if (font->handle >= 0) { | |||||
std::shared_ptr<Font> font = APP->window->loadFont(fontPath); | |||||
if (font && font->handle >= 0) { | |||||
bndSetFont(font->handle); | bndSetFont(font->handle); | ||||
NVGcolor highlightColor = color; | NVGcolor highlightColor = color; | ||||
highlightColor.a = 0.5; | highlightColor.a = 0.5; | ||||
int begin = std::min(cursor, selection); | int begin = std::min(cursor, selection); | ||||
int end = (this == APP->event->selectedWidget) ? std::max(cursor, selection) : -1; | int end = (this == APP->event->selectedWidget) ? std::max(cursor, selection) : -1; | ||||
bndIconLabelCaret(args.vg, textOffset.x, textOffset.y, | |||||
box.size.x - 2 * textOffset.x, box.size.y - 2 * textOffset.y, | |||||
-1, color, 12, text.c_str(), highlightColor, begin, end); | |||||
bndIconLabelCaret(args.vg, | |||||
textOffset.x, textOffset.y, | |||||
box.size.x - 2 * textOffset.x, box.size.y - 2 * textOffset.y, | |||||
-1, color, 12, text.c_str(), highlightColor, begin, end); | |||||
bndSetFont(APP->window->uiFont->handle); | bndSetFont(APP->window->uiFont->handle); | ||||
} | } | ||||
@@ -107,11 +113,17 @@ void LedDisplayTextField::draw(const DrawArgs& args) { | |||||
nvgResetScissor(args.vg); | nvgResetScissor(args.vg); | ||||
} | } | ||||
int LedDisplayTextField::getTextPosition(math::Vec mousePos) { | int LedDisplayTextField::getTextPosition(math::Vec mousePos) { | ||||
std::shared_ptr<Font> font = APP->window->loadFont(fontPath); | |||||
if (!font || !font->handle) | |||||
return 0; | |||||
bndSetFont(font->handle); | bndSetFont(font->handle); | ||||
int textPos = bndIconLabelTextPosition(APP->window->vg, textOffset.x, textOffset.y, | |||||
box.size.x - 2 * textOffset.x, box.size.y - 2 * textOffset.y, | |||||
-1, 12, text.c_str(), mousePos.x, mousePos.y); | |||||
int textPos = bndIconLabelTextPosition(APP->window->vg, | |||||
textOffset.x, textOffset.y, | |||||
box.size.x - 2 * textOffset.x, box.size.y - 2 * textOffset.y, | |||||
-1, 12, text.c_str(), mousePos.x, mousePos.y); | |||||
bndSetFont(APP->window->uiFont->handle); | bndSetFont(APP->window->uiFont->handle); | ||||
return textPos; | return textPos; | ||||
} | } | ||||
@@ -1,6 +1,7 @@ | |||||
#include <svg.hpp> | #include <svg.hpp> | ||||
#include <map> | #include <map> | ||||
#include <math.hpp> | #include <math.hpp> | ||||
#include <string.hpp> | |||||
// #define DEBUG_ONLY(x) x | // #define DEBUG_ONLY(x) x | ||||
@@ -18,12 +19,9 @@ Svg::~Svg() { | |||||
void Svg::loadFile(const std::string& filename) { | void Svg::loadFile(const std::string& filename) { | ||||
handle = nsvgParseFromFile(filename.c_str(), "px", SVG_DPI); | handle = nsvgParseFromFile(filename.c_str(), "px", SVG_DPI); | ||||
if (handle) { | |||||
INFO("Loaded SVG %s", filename.c_str()); | |||||
} | |||||
else { | |||||
WARN("Failed to load SVG %s", filename.c_str()); | |||||
} | |||||
if (!handle) | |||||
throw Exception("Failed to load SVG %s", filename.c_str()); | |||||
INFO("Loaded SVG %s", filename.c_str()); | |||||
} | } | ||||
@@ -31,12 +29,10 @@ void Svg::loadString(const std::string& str) { | |||||
// nsvgParse modifies the input string | // nsvgParse modifies the input string | ||||
std::string strCopy = str; | std::string strCopy = str; | ||||
handle = nsvgParse(&strCopy[0], "px", SVG_DPI); | handle = nsvgParse(&strCopy[0], "px", SVG_DPI); | ||||
if (handle) { | |||||
INFO("Loaded SVG"); | |||||
} | |||||
else { | |||||
WARN("Failed to load SVG"); | |||||
} | |||||
std::string strEllip = string::ellipsize(str, 40); | |||||
if (!handle) | |||||
throw Exception("Failed to load SVG \"%s\"", strEllip.c_str()); | |||||
INFO("Loaded SVG \"%s\"", strEllip.c_str()); | |||||
} | } | ||||
@@ -46,16 +42,26 @@ void Svg::draw(NVGcontext* vg) { | |||||
static std::map<std::string, std::weak_ptr<Svg>> svgCache; | |||||
static std::map<std::string, std::shared_ptr<Svg>> svgCache; | |||||
std::shared_ptr<Svg> Svg::load(const std::string& filename) { | std::shared_ptr<Svg> Svg::load(const std::string& filename) { | ||||
auto sp = svgCache[filename].lock(); | |||||
if (!sp) { | |||||
svgCache[filename] = sp = std::make_shared<Svg>(); | |||||
sp->loadFile(filename); | |||||
const auto& pair = svgCache.find(filename); | |||||
if (pair != svgCache.end()) | |||||
return pair->second; | |||||
// Load svg | |||||
std::shared_ptr<Svg> svg; | |||||
try { | |||||
svg = std::make_shared<Svg>(); | |||||
svg->loadFile(filename); | |||||
} | |||||
catch (Exception& e) { | |||||
WARN("%s", e.what()); | |||||
svg = NULL; | |||||
} | } | ||||
return sp; | |||||
svgCache[filename] = svg; | |||||
return svg; | |||||
} | } | ||||
@@ -29,12 +29,9 @@ namespace rack { | |||||
void Font::loadFile(const std::string& filename, NVGcontext* vg) { | void Font::loadFile(const std::string& filename, NVGcontext* vg) { | ||||
this->vg = vg; | this->vg = vg; | ||||
handle = nvgCreateFont(vg, filename.c_str(), filename.c_str()); | handle = nvgCreateFont(vg, filename.c_str(), filename.c_str()); | ||||
if (handle >= 0) { | |||||
INFO("Loaded font %s", filename.c_str()); | |||||
} | |||||
else { | |||||
WARN("Failed to load font %s", filename.c_str()); | |||||
} | |||||
if (handle < 0) | |||||
throw Exception("Failed to load font %s", filename.c_str()); | |||||
INFO("Loaded font %s", filename.c_str()); | |||||
} | } | ||||
@@ -51,12 +48,9 @@ std::shared_ptr<Font> Font::load(const std::string& filename) { | |||||
void Image::loadFile(const std::string& filename, NVGcontext* vg) { | void Image::loadFile(const std::string& filename, NVGcontext* vg) { | ||||
this->vg = vg; | this->vg = vg; | ||||
handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); | ||||
if (handle > 0) { | |||||
INFO("Loaded image %s", filename.c_str()); | |||||
} | |||||
else { | |||||
WARN("Failed to load image %s", filename.c_str()); | |||||
} | |||||
if (handle <= 0) | |||||
throw Exception("Failed to load image %s", filename.c_str()); | |||||
INFO("Loaded image %s", filename.c_str()); | |||||
} | } | ||||
@@ -88,6 +82,9 @@ struct Window::Internal { | |||||
double lastFrameTime = 0.0; | double lastFrameTime = 0.0; | ||||
math::Vec lastMousePos; | math::Vec lastMousePos; | ||||
std::map<std::string, std::shared_ptr<Font>> fontCache; | |||||
std::map<std::string, std::shared_ptr<Image>> imageCache; | |||||
}; | }; | ||||
@@ -611,22 +608,42 @@ double Window::getFrameTimeOverdue() { | |||||
std::shared_ptr<Font> Window::loadFont(const std::string& filename) { | std::shared_ptr<Font> Window::loadFont(const std::string& filename) { | ||||
auto sp = fontCache[filename].lock(); | |||||
if (!sp) { | |||||
fontCache[filename] = sp = std::make_shared<Font>(); | |||||
sp->loadFile(filename, vg); | |||||
const auto& pair = internal->fontCache.find(filename); | |||||
if (pair != internal->fontCache.end()) | |||||
return pair->second; | |||||
// Load font | |||||
std::shared_ptr<Font> font; | |||||
try { | |||||
font = std::make_shared<Font>(); | |||||
font->loadFile(filename, vg); | |||||
} | } | ||||
return sp; | |||||
catch (Exception& e) { | |||||
WARN("%s", e.what()); | |||||
font = NULL; | |||||
} | |||||
internal->fontCache[filename] = font; | |||||
return font; | |||||
} | } | ||||
std::shared_ptr<Image> Window::loadImage(const std::string& filename) { | std::shared_ptr<Image> Window::loadImage(const std::string& filename) { | ||||
auto sp = imageCache[filename].lock(); | |||||
if (!sp) { | |||||
imageCache[filename] = sp = std::make_shared<Image>(); | |||||
sp->loadFile(filename, vg); | |||||
} | |||||
return sp; | |||||
const auto& pair = internal->imageCache.find(filename); | |||||
if (pair != internal->imageCache.end()) | |||||
return pair->second; | |||||
// Load image | |||||
std::shared_ptr<Image> image; | |||||
try { | |||||
image = std::make_shared<Image>(); | |||||
image->loadFile(filename, vg); | |||||
} | |||||
catch (Exception& e) { | |||||
WARN("%s", e.what()); | |||||
image = NULL; | |||||
} | |||||
internal->imageCache[filename] = image; | |||||
return image; | |||||
} | } | ||||