@@ -20,7 +20,7 @@ struct LedDisplaySeparator : widget::Widget { | |||
struct LedDisplayChoice : widget::OpaqueWidget { | |||
std::string text; | |||
std::shared_ptr<Font> font; | |||
std::string fontPath; | |||
math::Vec textOffset; | |||
NVGcolor color; | |||
NVGcolor bgColor; | |||
@@ -30,7 +30,7 @@ struct LedDisplayChoice : widget::OpaqueWidget { | |||
}; | |||
struct LedDisplayTextField : ui::TextField { | |||
std::shared_ptr<Font> font; | |||
std::string fontPath; | |||
math::Vec textOffset; | |||
NVGcolor color; | |||
LedDisplayTextField(); | |||
@@ -58,10 +58,6 @@ struct Window { | |||
float windowRatio = 1.f; | |||
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(); | |||
void run(); | |||
@@ -24,6 +24,7 @@ LedDisplaySeparator::LedDisplaySeparator() { | |||
box.size = math::Vec(); | |||
} | |||
void LedDisplaySeparator::draw(const DrawArgs& args) { | |||
nvgBeginPath(args.vg); | |||
nvgMoveTo(args.vg, 0, 0); | |||
@@ -36,12 +37,13 @@ void LedDisplaySeparator::draw(const DrawArgs& args) { | |||
LedDisplayChoice::LedDisplayChoice() { | |||
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); | |||
bgColor = nvgRGBAf(0, 0, 0, 0); | |||
textOffset = math::Vec(10, 18); | |||
} | |||
void LedDisplayChoice::draw(const DrawArgs& args) { | |||
nvgScissor(args.vg, RECT_ARGS(args.clipBox)); | |||
if (bgColor.a > 0.0) { | |||
@@ -51,7 +53,8 @@ void LedDisplayChoice::draw(const DrawArgs& args) { | |||
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); | |||
nvgFontFaceId(args.vg, font->handle); | |||
nvgTextLetterSpacing(args.vg, 0.0); | |||
@@ -62,6 +65,7 @@ void LedDisplayChoice::draw(const DrawArgs& args) { | |||
nvgResetScissor(args.vg); | |||
} | |||
void LedDisplayChoice::onButton(const ButtonEvent& e) { | |||
OpaqueWidget::onButton(e); | |||
@@ -74,7 +78,7 @@ void LedDisplayChoice::onButton(const ButtonEvent& e) { | |||
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); | |||
textOffset = math::Vec(5, 5); | |||
} | |||
@@ -90,16 +94,18 @@ void LedDisplayTextField::draw(const DrawArgs& args) { | |||
nvgFill(args.vg); | |||
// Text | |||
if (font->handle >= 0) { | |||
std::shared_ptr<Font> font = APP->window->loadFont(fontPath); | |||
if (font && font->handle >= 0) { | |||
bndSetFont(font->handle); | |||
NVGcolor highlightColor = color; | |||
highlightColor.a = 0.5; | |||
int begin = std::min(cursor, selection); | |||
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); | |||
} | |||
@@ -107,11 +113,17 @@ void LedDisplayTextField::draw(const DrawArgs& args) { | |||
nvgResetScissor(args.vg); | |||
} | |||
int LedDisplayTextField::getTextPosition(math::Vec mousePos) { | |||
std::shared_ptr<Font> font = APP->window->loadFont(fontPath); | |||
if (!font || !font->handle) | |||
return 0; | |||
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); | |||
return textPos; | |||
} | |||
@@ -1,6 +1,7 @@ | |||
#include <svg.hpp> | |||
#include <map> | |||
#include <math.hpp> | |||
#include <string.hpp> | |||
// #define DEBUG_ONLY(x) x | |||
@@ -18,12 +19,9 @@ Svg::~Svg() { | |||
void Svg::loadFile(const std::string& filename) { | |||
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 | |||
std::string strCopy = str; | |||
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) { | |||
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) { | |||
this->vg = vg; | |||
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) { | |||
this->vg = vg; | |||
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; | |||
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) { | |||
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) { | |||
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; | |||
} | |||