mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-26 04:59:54 +03:00
Added text visibility ratio and worked on ui text.
This commit is contained in:
parent
d47e02b1d3
commit
e6263492b8
5 changed files with 112 additions and 100 deletions
14
TOUR.md
14
TOUR.md
|
@ -144,12 +144,13 @@ Draw options are used for configuring drawing parameters. The data structure loo
|
|||
|
||||
```d
|
||||
struct DrawOptions {
|
||||
Vec2 origin = Vec2(0.0f); /// The origin point of the drawn object.
|
||||
Vec2 scale = Vec2(1.0f); /// The scale of the drawn object.
|
||||
float rotation = 0.0f; /// The rotation of the drawn object, in degrees.
|
||||
Color color = white; /// The color of the drawn object.
|
||||
Hook hook = Hook.topLeft; /// An value representing the origin point of the drawn object when origin is set to zero.
|
||||
Flip flip = Flip.none; /// An value representing flipping orientations.
|
||||
float rotation = 0.0f; /// The rotation of the drawn object, in degrees.
|
||||
Color color = white; /// The color of the drawn object.
|
||||
Hook hook = Hook.topLeft; /// A value representing the origin point of the drawn object when origin is set to zero.
|
||||
Flip flip = Flip.none; /// A value representing flipping orientations.
|
||||
Alignment alignment = Alignment.left; /// A value represeting alignment orientations.
|
||||
int alignmentWidth = 0; /// The width of the aligned object. Used as a hint and it is not enforced. Mostly used for text drawing.
|
||||
float visibilityRatio = 1.0f; /// Controls the visibility ratio of the object, where 0.0 means fully hidden and 1.0 means fully visible. Mostly used for text drawing.
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -161,6 +162,7 @@ this(Vec2 scale);
|
|||
this(Color color);
|
||||
this(Hook hook);
|
||||
this(Flip flip);
|
||||
this(Alignment alignment, int alignmentWidth = 0);
|
||||
```
|
||||
|
||||
## Loading and Saving Resources
|
||||
|
|
|
@ -11,6 +11,7 @@ void ready() {
|
|||
|
||||
bool update(float dt) {
|
||||
prepareUi();
|
||||
setUiFocus(0);
|
||||
setUiStartPoint(Vec2(8));
|
||||
// Toggle the limit of the drag handle.
|
||||
if (uiButton(Vec2(80, 30), "Limit: {}".format(handleOptions.dragLimit))) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import parin;
|
||||
|
||||
auto buttonSize = Vec2(32);
|
||||
auto buttonSize = Vec2(20);
|
||||
|
||||
void ready() {
|
||||
lockResolution(320, 180);
|
||||
|
@ -15,12 +15,12 @@ bool update(float dt) {
|
|||
setUiStartPoint(Vec2(8));
|
||||
// Create a layout for arranging subsequent UI items.
|
||||
useUiLayout(Layout.h);
|
||||
if (uiButton(buttonSize, "1")) println(1);
|
||||
if (uiButton(buttonSize, "2")) println(2);
|
||||
uiText(Vec2(70, buttonSize.y), "Button 1", UiButtonOptions(Alignment.left));
|
||||
if (uiButton(buttonSize, "")) println(1);
|
||||
// Create a new layout under the previous layout.
|
||||
useUiLayout(Layout.h);
|
||||
if (uiButton(buttonSize, "3")) println(3);
|
||||
if (uiButton(buttonSize, "4")) println(4);
|
||||
uiText(Vec2(70, buttonSize.y), "Button 22", UiButtonOptions(Alignment.left));
|
||||
if (uiButton(buttonSize, "")) println(22);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,10 +36,12 @@ Sz engineEnvArgsBufferLength;
|
|||
IStr[64] engineDroppedFilePathsBuffer;
|
||||
rl.FilePathList engineDroppedFilePathsDataBuffer;
|
||||
|
||||
/// A type representing layout orientations.
|
||||
enum Layout : ubyte {
|
||||
v, /// Vertical layout.
|
||||
h, /// Horizontal layout.
|
||||
/// A type representing flipping orientations.
|
||||
enum Flip : ubyte {
|
||||
none, /// No flipping.
|
||||
x, /// Flipped along the X-axis.
|
||||
y, /// Flipped along the Y-axis.
|
||||
xy, /// Flipped along both X and Y axes.
|
||||
}
|
||||
|
||||
/// A type representing alignment orientations.
|
||||
|
@ -49,12 +51,10 @@ enum Alignment : ubyte {
|
|||
right, /// Align to the right.
|
||||
}
|
||||
|
||||
/// A type representing flipping orientations.
|
||||
enum Flip : ubyte {
|
||||
none, /// No flipping.
|
||||
x, /// Flipped along the X-axis.
|
||||
y, /// Flipped along the Y-axis.
|
||||
xy, /// Flipped along both X and Y axes.
|
||||
/// A type representing layout orientations.
|
||||
enum Layout : ubyte {
|
||||
v, /// Vertical layout.
|
||||
h, /// Horizontal layout.
|
||||
}
|
||||
|
||||
/// A type representing texture filtering modes.
|
||||
|
@ -194,8 +194,10 @@ struct DrawOptions {
|
|||
float rotation = 0.0f; /// The rotation of the drawn object, in degrees.
|
||||
Color color = white; /// The color of the drawn object.
|
||||
Hook hook = Hook.topLeft; /// A value representing the origin point of the drawn object when origin is set to zero.
|
||||
Alignment alignment = Alignment.left; /// A value represeting alignment orientations.
|
||||
Flip flip = Flip.none; /// A value representing flipping orientations.
|
||||
Alignment alignment = Alignment.left; /// A value represeting alignment orientations.
|
||||
int alignmentWidth = 0; /// The width of the aligned object. Used as a hint and it is not enforced. Mostly used for text drawing.
|
||||
float visibilityRatio = 1.0f; /// Controls the visibility ratio of the object, where 0.0 means fully hidden and 1.0 means fully visible. Mostly used for text drawing.
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
|
@ -219,15 +221,16 @@ struct DrawOptions {
|
|||
this.hook = hook;
|
||||
}
|
||||
|
||||
/// Initializes the options with the given alignment.
|
||||
this(Alignment alignment) {
|
||||
this.alignment = alignment;
|
||||
}
|
||||
|
||||
/// Initializes the options with the given flip.
|
||||
this(Flip flip) {
|
||||
this.flip = flip;
|
||||
}
|
||||
|
||||
/// Initializes the options with the given alignment.
|
||||
this(Alignment alignment, int alignmentWidth = 0) {
|
||||
this.alignment = alignment;
|
||||
this.alignmentWidth = alignmentWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an identifier for a managed resource.
|
||||
|
@ -1863,49 +1866,39 @@ float deltaWheel() {
|
|||
Vec2 measureTextSize(Font font, IStr text, DrawOptions options = DrawOptions()) {
|
||||
if (font.isEmpty || text.length == 0) return Vec2();
|
||||
|
||||
// NOTE: No idea what is happening here. Maybe I should try to make it look more like the drawText function.
|
||||
|
||||
auto result = Vec2();
|
||||
auto tempByteCounter = 0; // Used to count longer text line num chars.
|
||||
auto byteCounter = 0;
|
||||
auto textWidth = 0.0f;
|
||||
auto tempTextWidth = 0.0f; // Used to count longer text line width.
|
||||
auto lineCodepointCount = 0;
|
||||
auto maxLineCodepointCount = 0;
|
||||
auto textWidth = 0;
|
||||
auto maxTextWidth = 0;
|
||||
auto textHeight = font.size;
|
||||
|
||||
auto letter = 0; // Current character.
|
||||
auto index = 0; // Index position in texture font.
|
||||
auto i = 0;
|
||||
while (i < text.length) {
|
||||
byteCounter += 1;
|
||||
|
||||
auto next = 0;
|
||||
letter = rl.GetCodepointNext(&text[i], &next);
|
||||
index = rl.GetGlyphIndex(font.data, letter);
|
||||
i += next;
|
||||
if (letter != '\n') {
|
||||
if (font.data.glyphs[index].advanceX != 0) {
|
||||
textWidth += font.data.glyphs[index].advanceX;
|
||||
auto codepointIndex = 0;
|
||||
while (codepointIndex < text.length) {
|
||||
lineCodepointCount += 1;
|
||||
auto codepointByteCount = 0;
|
||||
auto codepoint = rl.GetCodepointNext(&text[codepointIndex], &codepointByteCount);
|
||||
auto glyphIndex = rl.GetGlyphIndex(font.data, codepoint);
|
||||
if (codepoint != '\n') {
|
||||
if (font.data.glyphs[glyphIndex].advanceX) {
|
||||
textWidth += font.data.glyphs[glyphIndex].advanceX;
|
||||
} else {
|
||||
textWidth += font.data.recs[index].width + font.data.glyphs[index].offsetX;
|
||||
textWidth += cast(int) (font.data.recs[glyphIndex].width + font.data.glyphs[glyphIndex].offsetX);
|
||||
}
|
||||
} else {
|
||||
if (tempTextWidth < textWidth) {
|
||||
tempTextWidth = textWidth;
|
||||
}
|
||||
byteCounter = 0;
|
||||
if (maxTextWidth < textWidth) maxTextWidth = textWidth;
|
||||
lineCodepointCount = 0;
|
||||
textWidth = 0;
|
||||
textHeight += font.lineSpacing;
|
||||
}
|
||||
if (tempByteCounter < byteCounter) {
|
||||
tempByteCounter = byteCounter;
|
||||
}
|
||||
if (maxLineCodepointCount < lineCodepointCount) maxLineCodepointCount = lineCodepointCount;
|
||||
codepointIndex += codepointByteCount;
|
||||
}
|
||||
if (tempTextWidth < textWidth) {
|
||||
tempTextWidth = textWidth;
|
||||
}
|
||||
result.x = floor(tempTextWidth * options.scale.x + ((tempByteCounter - 1) * font.runeSpacing * options.scale.x));
|
||||
result.y = floor(textHeight * options.scale.y);
|
||||
return result;
|
||||
if (maxTextWidth < textWidth) maxTextWidth = textWidth;
|
||||
if (maxTextWidth < options.alignmentWidth) maxTextWidth = options.alignmentWidth;
|
||||
return Vec2(
|
||||
floor(maxTextWidth * options.scale.x + ((maxLineCodepointCount - 1) * font.runeSpacing * options.scale.x)),
|
||||
floor(textHeight * options.scale.y),
|
||||
);
|
||||
}
|
||||
|
||||
/// Measures the size of the specified text when rendered with the given font and draw options.
|
||||
|
@ -2281,21 +2274,28 @@ void drawText(Font font, IStr text, Vec2 position, DrawOptions options = DrawOpt
|
|||
// Get some info about the text.
|
||||
linesBuffer.clear();
|
||||
linesWidthBuffer.clear();
|
||||
auto textHeight = font.size;
|
||||
auto maxLineWidth = 0;
|
||||
auto lineStartIndex = 0;
|
||||
foreach (i, c; text) {
|
||||
if (c == '\n' || i == text.length - 1) {
|
||||
linesBuffer.append(text[lineStartIndex .. i + (c != '\n')]);
|
||||
linesWidthBuffer.append(cast(short) measureTextSize(font, linesBuffer[$ - 1]).x);
|
||||
auto codepointCount = 0;
|
||||
auto codepointIndex = 0;
|
||||
while (codepointIndex < text.length) {
|
||||
codepointCount += 1;
|
||||
auto codepointByteCount = 0;
|
||||
auto codepoint = rl.GetCodepointNext(&text[codepointIndex], &codepointByteCount);
|
||||
if (codepoint == '\n' || codepointIndex == text.length - codepointByteCount) {
|
||||
linesBuffer.append(text[lineStartIndex .. codepointIndex + (codepoint != '\n')]);
|
||||
linesWidthBuffer.append(cast(short) (measureTextSize(font, linesBuffer[$ - 1]).x));
|
||||
if (maxLineWidth < linesWidthBuffer[$ - 1]) maxLineWidth = linesWidthBuffer[$ - 1];
|
||||
lineStartIndex = cast(int) i + 1;
|
||||
lineStartIndex = cast(int) (codepointIndex + 1);
|
||||
if (codepoint == '\n') textHeight += font.lineSpacing;
|
||||
}
|
||||
codepointIndex += codepointByteCount;
|
||||
}
|
||||
lineStartIndex = 0;
|
||||
if (maxLineWidth < options.alignmentWidth) maxLineWidth = options.alignmentWidth;
|
||||
|
||||
// Prepare the the text for drawing.
|
||||
auto textSize = measureTextSize(font, text);
|
||||
auto origin = Rect(textSize).origin(options.hook);
|
||||
auto origin = Rect(maxLineWidth, textHeight).origin(options.hook);
|
||||
rl.rlPushMatrix();
|
||||
if (isPixelSnapped || isPixelPerfect) {
|
||||
rl.rlTranslatef(floor(position.x), floor(position.y), 0.0f);
|
||||
|
@ -2306,19 +2306,21 @@ void drawText(Font font, IStr text, Vec2 position, DrawOptions options = DrawOpt
|
|||
rl.rlScalef(options.scale.x, options.scale.y, 1.0f);
|
||||
rl.rlTranslatef(floor(-origin.x), floor(-origin.y), 0.0f);
|
||||
// Draw the text.
|
||||
auto textOffsetY = 0; // Offset between lines (on linebreak '\n').
|
||||
codepointIndex = 0;
|
||||
auto maxCodepointCount = cast(int) (codepointCount * clamp(options.visibilityRatio, 0.0f, 1.0f));
|
||||
auto textOffsetY = 0; // Offset between lines.
|
||||
foreach (i, line; linesBuffer) {
|
||||
auto textOffsetX = 0; // Offset X to next character to draw.
|
||||
auto textOffsetX = 0; // Offset betweem characters.
|
||||
if (options.alignment == Alignment.center) {
|
||||
textOffsetX = maxLineWidth / 2 - linesWidthBuffer[i] / 2;
|
||||
} else if (options.alignment == Alignment.right) {
|
||||
textOffsetX = maxLineWidth - linesWidthBuffer[i];
|
||||
}
|
||||
auto codepointIndex = 0;
|
||||
while (codepointIndex < line.length) {
|
||||
// Get next codepoint from byte string and glyph index in font.
|
||||
auto lineCodepointIndex = 0;
|
||||
while (lineCodepointIndex < line.length) {
|
||||
if (codepointIndex >= maxCodepointCount) break; // This does break the codepoint index, but who cares.
|
||||
auto codepointByteCount = 0;
|
||||
auto codepoint = rl.GetCodepointNext(&line[codepointIndex], &codepointByteCount);
|
||||
auto codepoint = rl.GetCodepointNext(&line[lineCodepointIndex], &codepointByteCount);
|
||||
auto glyphIndex = rl.GetGlyphIndex(font.data, codepoint);
|
||||
if (codepoint != ' ' && codepoint != '\t') {
|
||||
auto runeOptions = DrawOptions();
|
||||
|
@ -2330,11 +2332,13 @@ void drawText(Font font, IStr text, Vec2 position, DrawOptions options = DrawOpt
|
|||
} else {
|
||||
textOffsetX += cast(int) (font.data.recs[glyphIndex].width) + font.runeSpacing;
|
||||
}
|
||||
// Move text bytes counter to next codepoint.
|
||||
lineCodepointIndex += codepointByteCount;
|
||||
codepointIndex += codepointByteCount;
|
||||
}
|
||||
textOffsetY += font.lineSpacing;
|
||||
codepointIndex += 1; // Adding the new line.
|
||||
}
|
||||
codepointIndex -= text[$ - 1] != '\n'; // Removing one extra new line.
|
||||
rl.rlPopMatrix();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,9 @@
|
|||
// Version: v0.0.29
|
||||
// ---
|
||||
|
||||
// TODO: Clean maybe the UiState struct and prepareUi func.
|
||||
// TODO: Think about overlapping UI items.
|
||||
// TODO: Look at the text alignment code again. Maybe add a clamp so text gets all the button always and stuff.
|
||||
// TODO: Add way to get item point for some stuff. This is nice when making lists.
|
||||
// TODO: Add focus style.
|
||||
// TODO: Add way to align text in buttons.
|
||||
// TODO: Look at the API again.
|
||||
// TODO: Test the ui code and think how to make it better while working on real stuff.
|
||||
|
||||
/// The `ui` module functions as a immediate mode UI library.
|
||||
module parin.ui;
|
||||
|
@ -50,6 +46,8 @@ struct UiButtonOptions {
|
|||
UiDragLimit dragLimit;
|
||||
Vec2 dragLimitX = Vec2(-100000.0f, 100000.0f);
|
||||
Vec2 dragLimitY = Vec2(-100000.0f, 100000.0f);
|
||||
Alignment textAlignment = Alignment.center;
|
||||
short textAlignmentMargin = 4;
|
||||
|
||||
@safe @nogc nothrow:
|
||||
|
||||
|
@ -60,6 +58,11 @@ struct UiButtonOptions {
|
|||
this(UiDragLimit dragLimit) {
|
||||
this.dragLimit = dragLimit;
|
||||
}
|
||||
|
||||
this(Alignment textAlignment, short textAlignmentMargin = 4) {
|
||||
this.textAlignment = textAlignment;
|
||||
this.textAlignmentMargin = textAlignmentMargin;
|
||||
}
|
||||
}
|
||||
|
||||
struct UiState {
|
||||
|
@ -70,7 +73,7 @@ struct UiState {
|
|||
|
||||
Vec2 viewportPoint;
|
||||
Vec2 viewportSize;
|
||||
Vec2 viewportScale = Vec2(1);
|
||||
Vec2 viewportScale = Vec2(1.0f);
|
||||
Vec2 startPoint;
|
||||
short margin;
|
||||
Layout layout;
|
||||
|
@ -352,12 +355,18 @@ void drawUiButton(Vec2 size, IStr text, Vec2 point, bool isHot, bool isActive, U
|
|||
} else {
|
||||
drawRect(area, options.idleColor);
|
||||
}
|
||||
|
||||
auto textPoint = area.centerPoint;
|
||||
if (options.textAlignment == Alignment.left) textPoint.x += options.textAlignmentMargin;
|
||||
else if (options.textAlignment == Alignment.right) textPoint.x -= options.textAlignmentMargin;
|
||||
|
||||
auto textOptions = DrawOptions(options.textAlignment, cast(int) (size.x));
|
||||
textOptions.hook = Hook.center;
|
||||
if (options.isDisabled) {
|
||||
auto tempOptions = DrawOptions(Hook.center);
|
||||
tempOptions.color.a = defaultUiAlpha / 2;
|
||||
drawText(options.font, text, area.centerPoint, tempOptions);
|
||||
textOptions.color.a = defaultUiAlpha / 2;
|
||||
drawText(options.font, text, textPoint, textOptions);
|
||||
} else {
|
||||
drawText(options.font, text, area.centerPoint, DrawOptions(Hook.center));
|
||||
drawText(options.font, text, textPoint, textOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,21 +433,17 @@ bool uiDragHandle(Vec2 size, ref Vec2 point, UiButtonOptions options = UiButtonO
|
|||
}
|
||||
}
|
||||
|
||||
void uiTexture(Texture texture, UiButtonOptions options = UiButtonOptions()) {
|
||||
auto point = uiState.layoutStartPoint + uiState.layoutStartPointOffest;
|
||||
drawRect(Rect(point, texture.size), black);
|
||||
drawTexture(texture, point);
|
||||
updateUiState(point, texture.size, false, false, false);
|
||||
}
|
||||
|
||||
void uiTexture(TextureId texture, UiButtonOptions options = UiButtonOptions()) {
|
||||
uiTexture(texture.get(), options);
|
||||
}
|
||||
|
||||
void uiText(IStr text, UiButtonOptions options = UiButtonOptions()) {
|
||||
void uiText(Vec2 size, IStr text, UiButtonOptions options = UiButtonOptions()) {
|
||||
if (options.font.isEmpty) options.font = engineFont;
|
||||
auto point = uiState.layoutStartPoint + uiState.layoutStartPointOffest;
|
||||
auto size = measureTextSize(options.font, text);
|
||||
drawText(options.font, text, point);
|
||||
|
||||
auto area = Rect(point, size);
|
||||
auto textPoint = area.centerPoint;
|
||||
if (options.textAlignment == Alignment.left) textPoint.x += options.textAlignmentMargin;
|
||||
else if (options.textAlignment == Alignment.right) textPoint.x -= options.textAlignmentMargin;
|
||||
|
||||
auto textOptions = DrawOptions(options.textAlignment, cast(int) (size.x));
|
||||
textOptions.hook = Hook.center;
|
||||
drawText(options.font, text, textPoint, textOptions);
|
||||
updateUiState(point, size, false, false, false);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue