Painting a frame re-measured text from scratch. Label re-wrapped its whole text on every paint, measuring each growing candidate line (quadratic in the paragraph length), and a line-limited label wrapped the full text before cutting it to a few lines. On the task board's Kanban tab this was 93% of paint time. - Label keeps its wrapped lines keyed by width, font size and the text metrics in use; text, wrap, line limit and reserved text reset it. wrapText takes an optional line limit and stops once it is exceeded. - RichText measures every word once per text and font and reuses those widths when the width changes; the ink width of each placed piece is recorded at layout so painting underlines and code chips needs no measuring. - TextAreaEditor caches per-paragraph prefix advances, so the width of any run inside a paragraph is a subtraction instead of a measure of a substring; lines outside the clip are not painted. - TextMetrics::prefixWidths returns the advance at every byte of a text. The default derives it from styledTextWidth; Canvas implements it with one nanovg glyph-position pass and matches direct measuring exactly. - Painter::isClippedOut lets widgets skip drawing outside the clip, and Canvas::text returns early for boxes outside the current clamp. - Canvas resolves its font ids once instead of looking fonts up by name on every text call.
159 lines
No EOL
4 KiB
C++
159 lines
No EOL
4 KiB
C++
#pragma once
|
|
|
|
#include "Window.hpp"
|
|
|
|
#include "Color.hpp"
|
|
#include "Rect.hpp"
|
|
|
|
#include "Buffer.hpp"
|
|
|
|
struct NVGcontext;
|
|
|
|
namespace tp {
|
|
|
|
class DebugGUI {
|
|
class Context;
|
|
Context* mContext;
|
|
|
|
public:
|
|
explicit DebugGUI(Window* window);
|
|
~DebugGUI();
|
|
|
|
void procBegin();
|
|
void procEnd();
|
|
|
|
void drawBegin();
|
|
void drawEnd();
|
|
};
|
|
|
|
class Canvas {
|
|
class Context;
|
|
Context* mContext;
|
|
|
|
public:
|
|
explicit Canvas(Window* window);
|
|
~Canvas();
|
|
|
|
void procBegin() {}
|
|
void procEnd() {}
|
|
|
|
void drawBegin();
|
|
void drawEnd();
|
|
void drawWindowChrome();
|
|
|
|
public:
|
|
enum Align : int2 {
|
|
CC = 0x0000,
|
|
CT = 0x0001,
|
|
CB = 0x0002,
|
|
LC = 0x0100,
|
|
LT = 0x0101,
|
|
LB = 0x0102,
|
|
RC = 0x0200,
|
|
RT = 0x0201,
|
|
RB = 0x0202,
|
|
};
|
|
|
|
enum class FontFace { Regular, Bold, Italic, BoldItalic, Monospace };
|
|
|
|
struct ImageHandle {
|
|
ualni id = 0;
|
|
};
|
|
|
|
struct ColorWheel {
|
|
HSV color = { 0.13, 0.5, 1 };
|
|
halnf thickness = 20;
|
|
halnf margin = 5;
|
|
|
|
void fromPoint(const RectF& area, const Vec2F& point);
|
|
};
|
|
|
|
void setOrigin(const Vec2F& origin);
|
|
|
|
void pushClamp(const RectF& rec);
|
|
void popClamp();
|
|
[[nodiscard]] const RectF& getClampedArea() const;
|
|
[[nodiscard]] bool isOutsideClamp(const RectF& rec) const;
|
|
|
|
void debugCross(RectF rec, const RGBA& col);
|
|
void rect(RectF rec, const RGBA& col, halnf round = 0);
|
|
void gradientRect(RectF rec, const RGBA& from, const RGBA& to, bool horizontal, halnf round = 0);
|
|
void frame(RectF rec, const RGBA& col, halnf round = 0);
|
|
void outline(RectF rec, const RGBA& col, halnf round, halnf width);
|
|
void circle(Vec2F pos, halnf size, const RGBA& col);
|
|
void circleOutline(Vec2F pos, halnf size, const RGBA& col, halnf width);
|
|
void polyline(const Vec2F* points, int count, const RGBA& col, halnf width);
|
|
void polygon(const Vec2F* points, int count, const RGBA& col);
|
|
void
|
|
text(const char*, const RectF&, halnf size, Align, halnf padding, const RGBA&, FontFace face = FontFace::Regular);
|
|
void rotatedText(const char* text, Vec2F center, halnf size, halnf angle, const RGBA& col);
|
|
[[nodiscard]] halnf textWidth(const char*, halnf size, FontFace face = FontFace::Regular);
|
|
void prefixWidths(const char* text, size_t length, halnf size, FontFace face, halnf* widths);
|
|
void colorWheel(const RectF& rec, const ColorWheel& colorWheel);
|
|
void line(Vec2F start, Vec2F end, const RGBA& col, halnf thickness);
|
|
|
|
ImageHandle createImageFromTextId(ualni id, Vec2F size);
|
|
void updateTextureID(ImageHandle handle, ualni id);
|
|
|
|
ImageHandle createImageFromFile(const char* path);
|
|
[[nodiscard]] Vec2F imageSize(ImageHandle image);
|
|
ImageHandle createImageFromMemoryRGBA(int width, int height, const unsigned char* rgba);
|
|
void updateImageRGBA(ImageHandle image, const unsigned char* rgba);
|
|
|
|
void deleteImageHandle(ImageHandle image);
|
|
void drawImage(RectF rec, ImageHandle* image, halnf angle = 0, halnf alpha = 1.f, halnf rounding = 0.f);
|
|
|
|
private:
|
|
[[nodiscard]] int fontIdFor(FontFace face) const;
|
|
void fillWindowBase(const WindowFrame& frame);
|
|
static void cutRoundedCorners(NVGcontext* vg, const WindowFrame& frame);
|
|
static void paintOutline(NVGcontext* vg, const WindowFrame& frame);
|
|
static void paintShadow(NVGcontext* vg, const WindowFrame& frame, bool focused);
|
|
|
|
Buffer<RectF> mScissors;
|
|
bool mIsClamping = false;
|
|
Vec2F mOrigin = { 0, 0 };
|
|
};
|
|
|
|
class Graphics {
|
|
public:
|
|
explicit Graphics(Window* window) :
|
|
mWindow(window),
|
|
mCanvas(window),
|
|
mGui(window) {}
|
|
|
|
void procBegin() {
|
|
mCanvas.procBegin();
|
|
mGui.procBegin();
|
|
}
|
|
|
|
void procEnd() {
|
|
mCanvas.procEnd();
|
|
mGui.procEnd();
|
|
}
|
|
|
|
void drawBegin() {
|
|
mFrameActive = mWindow->frameBegin();
|
|
if (!mFrameActive) return;
|
|
mCanvas.drawBegin();
|
|
mGui.drawBegin();
|
|
}
|
|
|
|
void drawEnd() {
|
|
if (!mFrameActive) return;
|
|
mCanvas.drawEnd();
|
|
mGui.drawEnd();
|
|
mCanvas.drawWindowChrome();
|
|
mWindow->frameEnd();
|
|
mFrameActive = false;
|
|
}
|
|
|
|
Canvas* getCanvas() { return &mCanvas; }
|
|
|
|
private:
|
|
Window* mWindow;
|
|
Canvas mCanvas;
|
|
DebugGUI mGui;
|
|
bool mFrameActive = false;
|
|
};
|
|
} |