Victory. Text editor working well. Docs being written.
This commit is contained in:
parent
406a8a3a71
commit
f18795ff79
@ -5,8 +5,8 @@
|
||||
//
|
||||
NAME :: "mexplore";
|
||||
VERSION :: "0.1";
|
||||
JAI_VERSION :: "beta 0.2.016, built on 19 July 2025";
|
||||
RELEASE_DATE :: "15 September 2025, 21:57:10";
|
||||
JAI_VERSION :: "beta 0.2.018, built on 11 October 2025";
|
||||
RELEASE_DATE :: "21 October 2025, 23:02:07";
|
||||
GIT_BRANCH :: "main";
|
||||
GIT_REVISION :: "7be01af7bf0025806f36ad7c938655e432d8b81e";
|
||||
GIT_REVISION :: "406a8a3a7118b28edb568656315ea190c111c5ba";
|
||||
DEBUG :: true;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
136
README.md
Normal file
136
README.md
Normal file
@ -0,0 +1,136 @@
|
||||
# UI
|
||||
|
||||
## Overview
|
||||
|
||||
This is a small Jai library to do some basic immediate mode UI. It has a variety of widgets
|
||||
and writing new ones should be relatively easy, if you follow the existing ones.
|
||||
|
||||
As opposed to a UI library you might find on the Internet, like Dear ImGui, this library is
|
||||
sadly not platform independent. It is relatively coupled with OpenGL, so you in order to
|
||||
integrate it to a project, you will need to also provide a GL context with GLFW, RGFW, SDL,
|
||||
or the like.
|
||||
|
||||
## Architecture
|
||||
|
||||
The main concept behind this UI system is that as you call the ui_* functions like ui_begin/ui_end, etc.
|
||||
You are implicitly building the tree that represents the UI hierarchy. That is the magic of an
|
||||
immediate mode API. You take advantage of the fact that functions are called in a stack across time.
|
||||
Which is implicitly a Tree. At the end of the frame you give this tree to the rendering function, which
|
||||
recurses through it pre-order, drawing the panels, icons, text, etc., in the correct order.
|
||||
|
||||
Using this architecutre as the base places some undesirable constraints on the further design of the
|
||||
system though. For example, the most useful part of this library is the auto-layout algorithm. It's not
|
||||
super sophisticated, but it works. And in order to do this auto layout you need two basic pieces of
|
||||
information. Where are the ui elements and how big are they. Properties that the layout algorithm can
|
||||
itself change!
|
||||
|
||||
So, the easiest solution to this problem is to separate everything into distinct phases (or passes).
|
||||
In the first pass you are declaring the tree structure of the UI as you go in a bottom up fashion,
|
||||
recording the initial positions and sizes. Then after you're done you can traverse the tree from the
|
||||
top down adjusting the layout. This is only possible because at this point in time you have all the
|
||||
information including nodes' siblings, the size they are, they size they'd like to be, etc.
|
||||
|
||||
## Public API
|
||||
|
||||
Some functions have a begin/end pair, becuase you necesarily have to wait until things finish happening
|
||||
inside of them to be able to calculate their initial size, etc. Other functions don't have this begin/end
|
||||
pair, because they don't allow children, they're a leaf node, and they don't need further information to finish
|
||||
their calculations.
|
||||
|
||||
```
|
||||
ui_new_frame :: ()
|
||||
```
|
||||
|
||||
This has to be called every frame, before calling any other UI function.
|
||||
|
||||
```
|
||||
ui_begin :: (s: string)
|
||||
```
|
||||
|
||||
This begins a panel, which other widgets can go inside of.
|
||||
|
||||
```
|
||||
ui_end :: ()
|
||||
```
|
||||
|
||||
Has to be called after a ui_begin to finish its UI Node.
|
||||
|
||||
```
|
||||
ui_text_input :: (s: string, font_colour := Vector4.{1, 1, 1, 1})
|
||||
```
|
||||
|
||||
This starts a text editor!
|
||||
|
||||
```
|
||||
ui_begin_container :: (s: string, gradient := false, colour_left := Vector4.{}, colour_right := Vector4.{}, max_x: s32 = -1, max_y: s32 = -1, flags: ContainerFlags = 0)
|
||||
```
|
||||
|
||||
Similar to a normal ui_begin, but you can have this one inside the main panel, recursively.
|
||||
Very useful to change the layout direction (Left To Right vs Top to Bottom)
|
||||
|
||||
```
|
||||
ui_end_container :: ()
|
||||
```
|
||||
|
||||
Must be called after a ui_begin_container, to calculate sizing and finish the node in the UI Tree.
|
||||
|
||||
```
|
||||
ui_icon :: (s: string, texture: *Texture, size: Vector2)
|
||||
```
|
||||
|
||||
Renders an OpenGL texture.
|
||||
|
||||
```
|
||||
ui_label :: (s: string = "", colour := Vector4.{1.0, 1.0, 1.0, 1.0})
|
||||
```
|
||||
|
||||
Renders text.
|
||||
|
||||
```
|
||||
ui_spacing :: (s: string)
|
||||
```
|
||||
|
||||
Forces spacing between elements. The auto layout algorithm tries to maximize the space between
|
||||
the node before the spacing and after the spacing, taking into account the max size of the parent
|
||||
container.
|
||||
|
||||
```
|
||||
ui_button :: (s: string = "", icon_texture: *Texture = null, font_colour := Vector4.{1, 1, 1, 1}, draw_frame := true) -> bool
|
||||
```
|
||||
|
||||
Renders a panel with optional text in it and an optional OpenGL texture. When clicked it returns true.
|
||||
Very useful to run conditional code like:
|
||||
|
||||
if ui_button(...) {
|
||||
do_something();
|
||||
}
|
||||
|
||||
```
|
||||
ui_font_icon :: (font: *Font, s: string, size: Vector2, colour: Vector4 = .{1, 1, 1, 1})
|
||||
```
|
||||
|
||||
Renders an icon stored in a font. you have to figure out what the name of the glyph is and pray
|
||||
FreeType can find it. You can look up the names using a tool like [FontDrop!](https://fontdrop.info/).
|
||||
|
||||
```
|
||||
ui_progress_bar :: (s: string = "", progress: float, size: Vector2, background := Vector4.{1, 1, 1, 1}, foreground := Vector4.{1, 1, 1, 1})
|
||||
```
|
||||
|
||||
Renders a progress bar.
|
||||
|
||||
```
|
||||
ui_end_frame :: ()
|
||||
```
|
||||
|
||||
Must be called after finishing the UI Tree. You must not call any UI Tree building functions (any of the previous ones)
|
||||
after this call and before a new ui_begin_frame.
|
||||
|
||||
```
|
||||
ui_render :: ()
|
||||
```
|
||||
|
||||
Renders the UI with OpenGL.
|
||||
|
||||
## Example
|
||||
|
||||
Please look at main.jai for an example.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,51 +1,51 @@
|
||||
// Workspace: Target Program
|
||||
|
||||
//
|
||||
// #insert text. Generated from /opt/jai/modules/Bindings_Generator/module.jai:326.
|
||||
//
|
||||
([2] string).[
|
||||
.["FLT_MIN", "FLOAT32_MIN"],
|
||||
.["FLT_MAX", "FLOAT32_MAX"],
|
||||
.["DBL_MIN", "FLOAT64_MIN"],
|
||||
.["DBL_MAX", "FLOAT64_MAX"],
|
||||
|
||||
.["SCHAR_MIN", "S8_MIN"],
|
||||
.["SCHAR_MAX", "S8_MAX"],
|
||||
.["UCHAR_MIN", "0"],
|
||||
.["UCHAR_MAX", "U8_MAX"],
|
||||
|
||||
.["SHRT_MIN", "S16_MIN"],
|
||||
.["SHRT_MAX", "S16_MAX"],
|
||||
.["USHRT_MIN", "0"],
|
||||
.["USHRT_MAX", "U16_MAX"],
|
||||
|
||||
.["INT_MIN", "S32_MIN"],
|
||||
.["INT_MAX", "S32_MAX"],
|
||||
.["UINT_MIN", "0"],
|
||||
.["UINT_MAX", "U32_MAX"],
|
||||
|
||||
.["LLONG_MIN", "S64_MIN"],
|
||||
.["LLONG_MAX", "S64_MAX"],
|
||||
.["ULLONG_MIN", "0"],
|
||||
.["ULLONG_MAX", "U64_MAX"],
|
||||
|
||||
.["INT8_MIN", "S8_MIN"],
|
||||
.["INT8_MAX", "S8_MAX"],
|
||||
.["UINT8_MAX", "U8_MAX"],
|
||||
|
||||
.["INT16_MIN", "S16_MIN"],
|
||||
.["INT16_MAX", "S16_MAX"],
|
||||
.["UINT16_MAX", "U16_MAX"],
|
||||
|
||||
.["INT32_MIN", "S32_MIN"],
|
||||
.["INT32_MAX", "S32_MAX"],
|
||||
.["UINT32_MAX", "U32_MAX"],
|
||||
|
||||
.["INT64_MIN", "S64_MIN"],
|
||||
.["INT64_MAX", "S64_MAX"],
|
||||
.["UINT64_MAX", "U64_MAX"],
|
||||
.["LONG_MIN", "S64_MIN"],
|
||||
.["LONG_MAX", "S64_MAX"],
|
||||
.["ULONG_MIN", "0"],
|
||||
.["ULONG_MAX", "U64_MAX"],
|
||||
];
|
||||
// Workspace: Target Program
|
||||
|
||||
//
|
||||
// #insert text. Generated from C:/jai/modules/Bindings_Generator/module.jai:337.
|
||||
//
|
||||
([2] string).[
|
||||
.["FLT_MIN", "FLOAT32_MIN"],
|
||||
.["FLT_MAX", "FLOAT32_MAX"],
|
||||
.["DBL_MIN", "FLOAT64_MIN"],
|
||||
.["DBL_MAX", "FLOAT64_MAX"],
|
||||
|
||||
.["SCHAR_MIN", "S8_MIN"],
|
||||
.["SCHAR_MAX", "S8_MAX"],
|
||||
.["UCHAR_MIN", "0"],
|
||||
.["UCHAR_MAX", "U8_MAX"],
|
||||
|
||||
.["SHRT_MIN", "S16_MIN"],
|
||||
.["SHRT_MAX", "S16_MAX"],
|
||||
.["USHRT_MIN", "0"],
|
||||
.["USHRT_MAX", "U16_MAX"],
|
||||
|
||||
.["INT_MIN", "S32_MIN"],
|
||||
.["INT_MAX", "S32_MAX"],
|
||||
.["UINT_MIN", "0"],
|
||||
.["UINT_MAX", "U32_MAX"],
|
||||
|
||||
.["LLONG_MIN", "S64_MIN"],
|
||||
.["LLONG_MAX", "S64_MAX"],
|
||||
.["ULLONG_MIN", "0"],
|
||||
.["ULLONG_MAX", "U64_MAX"],
|
||||
|
||||
.["INT8_MIN", "S8_MIN"],
|
||||
.["INT8_MAX", "S8_MAX"],
|
||||
.["UINT8_MAX", "U8_MAX"],
|
||||
|
||||
.["INT16_MIN", "S16_MIN"],
|
||||
.["INT16_MAX", "S16_MAX"],
|
||||
.["UINT16_MAX", "U16_MAX"],
|
||||
|
||||
.["INT32_MIN", "S32_MIN"],
|
||||
.["INT32_MAX", "S32_MAX"],
|
||||
.["UINT32_MAX", "U32_MAX"],
|
||||
|
||||
.["INT64_MIN", "S64_MIN"],
|
||||
.["INT64_MAX", "S64_MAX"],
|
||||
.["UINT64_MAX", "U64_MAX"],
|
||||
.["LONG_MIN", "S32_MIN"],
|
||||
.["LONG_MAX", "S32_MAX"],
|
||||
.["ULONG_MIN", "0"],
|
||||
.["ULONG_MAX", "U32_MAX"],
|
||||
];
|
||||
|
||||
@ -108,8 +108,8 @@ generate_bindings :: (args: [] string, minimum_os_version: type_of(Build_Options
|
||||
|
||||
try_to_preserve_comments = false;
|
||||
|
||||
array_add(*libpaths, lib_directory);
|
||||
array_add(*libnames, LIB_BASE_NAME);
|
||||
array_add(*library_search_paths, lib_directory);
|
||||
array_add(*libraries, .{ filename = LIB_BASE_NAME});
|
||||
array_add(*source_files, tprint("%.h", LIB_BASE_NAME));
|
||||
|
||||
generate_library_declarations = false;
|
||||
|
||||
@ -42,12 +42,45 @@
|
||||
API
|
||||
Segmentation
|
||||
kbts_BeginBreak()
|
||||
kbts_BreakAddCodepoint() -- Feed a codepoint to the breaker, call kbts_Break() immediately after this to get results
|
||||
kbts_BreakAddCodepoint() -- Feed a codepoint to the breaker.
|
||||
You need to call Break() repeatedly after every call to BreakAddCodepoint().
|
||||
Something like:
|
||||
kbts_BreakAddCodepoint(&BreakState, ...);
|
||||
kbts_break Break;
|
||||
while(kbts_Break(&BreakState, &Break)) {...}
|
||||
|
||||
When you call Break(), We guarantee that breaks are returned in-order. On our side, this means
|
||||
that they are buffered and reordered. On your side, it means that there is a delay of a few
|
||||
characters between your current position and the Break.Position that you will see.
|
||||
|
||||
In some cases, our buffering might break. When that happens, we set
|
||||
BREAK_STATE_FLAG_RAN_OUT_OF_REORDER_BUFFER_SPACE, and kbts_BreakStateIsValid() will return false.
|
||||
This is a sticky error, so you can check it whenever you like.
|
||||
To clear the error flag and start segmenting again, you will need to call BeginBreak(&BreakState),
|
||||
which resets the entire state.
|
||||
|
||||
Note that the input configurations for which our buffering breaks should be, for all intents and
|
||||
purposes, nonsensical. If you find legitimate text that we cannot segment without running out of
|
||||
buffer space, then that is a bug.
|
||||
|
||||
The default buffer size is determined by the BREAK_REORDER_BUFFER_FLUSH_THRESHOLD. If you really
|
||||
need a bigger buffer, then you might want to consider modifying this constant and recompiling
|
||||
the library, although this should be viewed as an emergency solution and not a routine
|
||||
configuration option.
|
||||
kbts_BreakFlush()
|
||||
kbts_Break() -- Call repeatedly to get breaks
|
||||
kbts_BreakStateIsValid()
|
||||
Easy font loading
|
||||
kbts_FontFromFile() -- Open a font, byteswap it in place, and allocate auxiliary structures
|
||||
kbts_FontFromFile() -- Open a font, byteswap it in place, and allocate auxiliary structures.
|
||||
When you read a font with kb_text_shape, the library will byteswap its data in-place and perform
|
||||
a bunch of other pre-computation passes to figure out memory limits and other useful information.
|
||||
This means you cannot trivially pass our pointer to the font data to any other TTF library, since
|
||||
they will expect the data to be in big endian format, which it won't be after we are done with it.
|
||||
|
||||
You can expect font reading to be pretty slow.
|
||||
On the other hand, you can expect shaping to be pretty fast.
|
||||
|
||||
To open a font with your own IO and memory allocation, see "Manual Memory Management" below.
|
||||
kbts_FreeFont()
|
||||
kbts_FontIsValid()
|
||||
Shaping
|
||||
@ -56,7 +89,33 @@
|
||||
kbts_ShapeConfig() -- Bake a font/script-specific shaping configuration
|
||||
kbts_CodepointToGlyph()
|
||||
kbts_InferScript() -- Hacky script recognition for when no segmentation data is available
|
||||
kbts_Shape() -- Returns 1 if more memory is needed, you should probably call this in a while()
|
||||
kbts_Shape() -- Returns 1 if more memory is needed, you should probably call this in a while().
|
||||
This is how you might call this in practice:
|
||||
while(kbts_Shape(State, &Config, Direction, Direction, Glyphs, &GlyphCount, GlyphCapacity))
|
||||
{
|
||||
Glyphs = realloc(Glyphs, sizeof(kbts_glyph) * State->RequiredGlyphCapacity);
|
||||
GlyphCapacity = State->RequiredGlyphCapacity;
|
||||
}
|
||||
Once Shape() returns 0, you are done shaping. Glyph indices are in the kbts_glyph.Id field.
|
||||
Please note that, while the glyphs do also contain a Codepoint field, this field will mostly
|
||||
be meaningless whenever complex shaping operations occur. This is because fonts exclusively
|
||||
work on glyph indices, and a lot of ligatures are obviously a combination of several codepoints
|
||||
and do not have a corresponding codepoint in the Unicode world.
|
||||
The same is true when a single glyph is split into multiple glyphs. A font might decide to
|
||||
decompose a letter-with-accent into a letter glyph + an accent glyph. In that case, we will
|
||||
know what the accent glyph's index is, but we are not told what its codepoint is.
|
||||
|
||||
There is currently no way to track where in the source text each glyph originates from.
|
||||
One thing we might try is to have a "void *UserData" member on each glyph, and flow it through
|
||||
the different substitutions, but I personally have not needed this yet and I do not have good
|
||||
test cases for it. If you are interested, let me know!
|
||||
|
||||
Final positions are in font units and can be extracted with Cursor() and PositionGlyph().
|
||||
To convert font units to fractional pixels in FreeType:
|
||||
(FontX * FtSizeMetrics.x_scale) >> 16
|
||||
(FontY * FtSizeMetrics.y_scale) >> 16
|
||||
This will give you 26.6 fractional pixel units.
|
||||
See https://freetype.org/freetype2/docs/reference/ft2-sizing_and_scaling.html for more info.
|
||||
kbts_ResetShapeState()
|
||||
Shaping - feature control
|
||||
kbts_FeatureOverride() -- Describe a manual override for a font feature
|
||||
@ -74,9 +133,24 @@
|
||||
Manual memory management
|
||||
kbts_SizeOfShapeState()
|
||||
kbts_PlaceShapeState()
|
||||
kbts_ReadFontHeader() -- Read the top of the file and return how many bytes are needed to read the rest
|
||||
kbts_ReadFontData() -- Read and byteswap the rest
|
||||
kbts_ReadFontHeader() -- Read and byteswap the top of the file.
|
||||
kbts_ReadFontData() -- Read and byteswap the rest.
|
||||
kbts_PostReadFontInitialize() -- Initialize auxiliary structures
|
||||
Example code for reading a font file with this API looks like this:
|
||||
size_t ScratchSize = kbts_ReadFontHeader(&Font, Data, Size);
|
||||
size_t PermanentMemorySize = kbts_ReadFontData(&Font, malloc(ScratchSize), ScratchSize);
|
||||
kbts_PostReadFontInitialize(&Font, malloc(PermanentMemorySize), PermanentMemorySize);
|
||||
|
||||
Please note that, AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE.
|
||||
AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE.
|
||||
AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE.
|
||||
AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE!
|
||||
If you need to open the same font with another library, you need to copy the data BEFORE
|
||||
calling ReadFontHeader().
|
||||
|
||||
The buffer you pass to ReadFontData() is temporary and can be freed once the function returns.
|
||||
The buffer you pass to PostReadFontInitialize() is persistent and can only be freed once you
|
||||
are done with the font.
|
||||
Utility, etc.
|
||||
kbts_ShaperIsComplex()
|
||||
kbts_ScriptIsComplex()
|
||||
@ -98,7 +172,7 @@
|
||||
kbts_direction Direction = KBTS_DIRECTION_NONE;
|
||||
for(size_t StringAt = 0; StringAt < Length;)
|
||||
{
|
||||
kbts_decode Decode = kbts_DecodeUtf8(String, Length - StringAt);
|
||||
kbts_decode Decode = kbts_DecodeUtf8(String + StringAt, Length - StringAt);
|
||||
StringAt += Decode.SourceCharactersConsumed;
|
||||
if(Decode.Valid)
|
||||
{
|
||||
@ -174,6 +248,7 @@
|
||||
|
||||
Open a font with your own memory:
|
||||
kbts_font Font;
|
||||
// Be careful: ReadFontHeader() and ReadFontData() both byteswap font data in-place!
|
||||
size_t ScratchSize = kbts_ReadFontHeader(&Font, Data, Size);
|
||||
size_t PermanentMemorySize = kbts_ReadFontData(&Font, malloc(ScratchSize), ScratchSize);
|
||||
kbts_PostReadFontInitialize(&Font, malloc(PermanentMemorySize), PermanentMemorySize);
|
||||
@ -3038,7 +3113,7 @@ static kbts_script_properties kbts_ScriptProperties[KBTS_SCRIPT_COUNT] = {
|
||||
KBTS_EXPORT kbts_script kbts_ScriptTagToScript(kbts_script_tag Tag)
|
||||
{
|
||||
kbts_script Result = 0;
|
||||
switch(Result)
|
||||
switch(Tag)
|
||||
{
|
||||
case KBTS_SCRIPT_TAG_DONT_KNOW: Result = KBTS_SCRIPT_DONT_KNOW; break;
|
||||
case KBTS_SCRIPT_TAG_ADLAM: Result = KBTS_SCRIPT_ADLAM; break;
|
||||
@ -15999,8 +16074,7 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint
|
||||
{
|
||||
Result.Id = Cmap0->GlyphIdArray[Codepoint];
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
@ -16041,8 +16115,7 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint
|
||||
|
||||
Result.Id = GlyphId;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
@ -16052,31 +16125,36 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint
|
||||
kbts_u16 *StartCodes = EndCodes + SegmentCount + 1;
|
||||
kbts_s16 *IdDeltas = (kbts_s16 *)(StartCodes + SegmentCount);
|
||||
kbts_u16 *IdRangeOffsets = (kbts_u16 *)(IdDeltas + SegmentCount);
|
||||
kbts_un SegmentIndexOffset = 0;
|
||||
|
||||
KBTS_FOR(SegmentIndex, 0, SegmentCount)
|
||||
if(SegmentCount)
|
||||
{
|
||||
kbts_u16 Start = StartCodes[SegmentIndex];
|
||||
if((Codepoint >= Start) && (Codepoint <= EndCodes[SegmentIndex]))
|
||||
while(SegmentCount > 1)
|
||||
{
|
||||
kbts_s16 Delta = IdDeltas[SegmentIndex];
|
||||
kbts_u16 RangeOffset = IdRangeOffsets[SegmentIndex];
|
||||
|
||||
kbts_u16 GlyphId = (kbts_u16)Delta;
|
||||
if(RangeOffset)
|
||||
{
|
||||
GlyphId += *(&IdRangeOffsets[SegmentIndex] + (Codepoint - Start) + RangeOffset / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
GlyphId += (kbts_u16)(Codepoint);
|
||||
}
|
||||
Result.Id = GlyphId;
|
||||
|
||||
break;
|
||||
kbts_un HalfCount = SegmentCount / 2;
|
||||
SegmentIndexOffset = (EndCodes[SegmentIndexOffset + HalfCount - 1] < Codepoint) ? (SegmentIndexOffset + HalfCount) : SegmentIndexOffset;
|
||||
SegmentCount -= HalfCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
kbts_u16 Start = StartCodes[SegmentIndexOffset];
|
||||
if((Codepoint >= Start) && (Codepoint <= EndCodes[SegmentIndexOffset]))
|
||||
{
|
||||
kbts_s16 Delta = IdDeltas[SegmentIndexOffset];
|
||||
kbts_u16 RangeOffset = IdRangeOffsets[SegmentIndexOffset];
|
||||
|
||||
kbts_u16 GlyphId = (kbts_u16)Delta;
|
||||
if(RangeOffset)
|
||||
{
|
||||
GlyphId += *(&IdRangeOffsets[SegmentIndexOffset] + (Codepoint - Start) + RangeOffset / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
GlyphId += (kbts_u16)(Codepoint);
|
||||
}
|
||||
Result.Id = GlyphId;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 6:
|
||||
{
|
||||
@ -16088,8 +16166,7 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint
|
||||
{
|
||||
Result.Id = GlyphIds[Offset];
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
|
||||
case 12:
|
||||
{
|
||||
@ -16097,22 +16174,25 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint
|
||||
kbts_sequential_map_group *Groups = KBTS_POINTER_AFTER(kbts_sequential_map_group, Cmap12);
|
||||
|
||||
kbts_un GlyphId = 0;
|
||||
KBTS_FOR(GroupIndex, 0, Cmap12->GroupCount)
|
||||
kbts_un GroupCount = Cmap12->GroupCount;
|
||||
if(GroupCount)
|
||||
{
|
||||
kbts_sequential_map_group *Group = &Groups[GroupIndex];
|
||||
|
||||
if((Codepoint >= Group->StartCharacterCode) && (Codepoint <= Group->EndCharacterCode))
|
||||
while(GroupCount > 1)
|
||||
{
|
||||
kbts_un Offset = Codepoint - Group->StartCharacterCode;
|
||||
GlyphId = Group->StartGlyphId + Offset;
|
||||
|
||||
break;
|
||||
kbts_un HalfCount = GroupCount / 2;
|
||||
Groups = (Groups[HalfCount - 1].EndCharacterCode < Codepoint) ? (Groups + HalfCount) : Groups;
|
||||
GroupCount -= HalfCount;
|
||||
}
|
||||
}
|
||||
|
||||
if((Codepoint >= Groups->StartCharacterCode) && (Codepoint <= Groups->EndCharacterCode))
|
||||
{
|
||||
kbts_un Offset = Codepoint - Groups->StartCharacterCode;
|
||||
GlyphId = Groups->StartGlyphId + Offset;
|
||||
}
|
||||
|
||||
Result.Id = (kbts_u16)GlyphId;
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23878,4 +23958,4 @@ KBTS_EXPORT int kbts_ScriptIsComplex(kbts_script Script)
|
||||
}
|
||||
|
||||
#endif
|
||||
#undef KBTS_X_FEATURES
|
||||
#undef KBTS_X_FEATURES
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//
|
||||
// This file was auto-generated using the following command:
|
||||
//
|
||||
// jai generate.jai - -compile -debug
|
||||
// jai generate.jai
|
||||
//
|
||||
|
||||
|
||||
@ -15,7 +15,8 @@ KBTS_MAX_SIMULTANEOUS_FEATURES :: 16;
|
||||
KBTS_BREAK_REORDER_BUFFER_FLUSH_THRESHOLD :: 4;
|
||||
KBTS_BREAK_REORDER_BUFFER_SIZE :: KBTS_BREAK_REORDER_BUFFER_FLUSH_THRESHOLD * 2;
|
||||
|
||||
kbts_joining_feature :: enum u8 {
|
||||
kbts_joining_feature :: u8;
|
||||
kbts_joining_feature_enum :: enum s32 {
|
||||
NONE :: 0;
|
||||
|
||||
ISOL :: 1;
|
||||
@ -41,7 +42,8 @@ kbts_joining_feature :: enum u8 {
|
||||
KBTS_JOINING_FEATURE_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_reph_position :: enum u8 {
|
||||
kbts_reph_position :: u8;
|
||||
kbts_reph_position_enum :: enum s32 {
|
||||
AFTER_POST :: 0;
|
||||
BEFORE_POST :: 1;
|
||||
BEFORE_SUBJOINED :: 2;
|
||||
@ -59,7 +61,8 @@ kbts_reph_position :: enum u8 {
|
||||
KBTS_REPH_POSITION_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_reph_encoding :: enum u8 {
|
||||
kbts_reph_encoding :: u8;
|
||||
kbts_reph_encoding_enum :: enum s32 {
|
||||
IMPLICIT :: 0;
|
||||
EXPLICIT :: 1;
|
||||
LOGICAL_REPHA :: 2;
|
||||
@ -75,7 +78,8 @@ kbts_reph_encoding :: enum u8 {
|
||||
KBTS_REPH_ENCODING_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_syllabic_position :: enum u8 {
|
||||
kbts_syllabic_position :: u8;
|
||||
kbts_syllabic_position_enum :: enum s32 {
|
||||
NONE :: 0;
|
||||
|
||||
RA_TO_BECOME_REPH :: 1;
|
||||
@ -1533,7 +1537,8 @@ kbts_break_flags :: enum u32 {
|
||||
KBTS_BREAK_FLAG_ANY :: ANY;
|
||||
}
|
||||
|
||||
kbts_op_kind :: enum u8 {
|
||||
kbts_op_kind :: u8;
|
||||
kbts_op_kind_enum :: enum s32 {
|
||||
END :: 0;
|
||||
|
||||
PRE_NORMALIZE_DOTTED_CIRCLES :: 1;
|
||||
@ -1569,7 +1574,8 @@ kbts_op_kind :: enum u8 {
|
||||
KBTS_OP_KIND_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_glyph_flags :: enum u32 {
|
||||
kbts_glyph_flags :: u32;
|
||||
kbts_glyph_flags_enum :: enum s32 {
|
||||
ISOL :: 1;
|
||||
FINA :: 2;
|
||||
FIN2 :: 4;
|
||||
@ -1659,7 +1665,8 @@ kbts_japanese_line_break_style :: enum u8 {
|
||||
KBTS_JAPANESE_LINE_BREAK_STYLE_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_orientation :: enum u32 {
|
||||
kbts_orientation :: u32;
|
||||
kbts_orientation_enum :: enum s32 {
|
||||
HORIZONTAL :: 0;
|
||||
VERTICAL :: 1;
|
||||
|
||||
@ -1685,7 +1692,8 @@ kbts_direction :: enum u32 {
|
||||
KBTS_DIRECTION_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_unicode_joining_type :: enum u8 {
|
||||
kbts_unicode_joining_type :: u8;
|
||||
kbts_unicode_joining_type_enum :: enum s32 {
|
||||
NONE :: 0;
|
||||
LEFT :: 1;
|
||||
DUAL :: 2;
|
||||
@ -1722,7 +1730,8 @@ kbts_unicode_flag_enum :: enum s32 {
|
||||
KBTS_UNICODE_FLAG_NON_SPACING_MARK :: NON_SPACING_MARK;
|
||||
}
|
||||
|
||||
kbts_unicode_bidirectional_class :: enum u8 {
|
||||
kbts_unicode_bidirectional_class :: u8;
|
||||
kbts_unicode_bidirectional_class_enum :: enum s32 {
|
||||
NI :: 0;
|
||||
L :: 1;
|
||||
R :: 2;
|
||||
@ -1748,7 +1757,8 @@ kbts_unicode_bidirectional_class :: enum u8 {
|
||||
KBTS_UNICODE_BIDIRECTIONAL_CLASS_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_line_break_class :: enum u8 {
|
||||
kbts_line_break_class :: u8;
|
||||
kbts_line_break_class_enum :: enum s32 {
|
||||
Onea :: 0;
|
||||
Oea :: 1;
|
||||
Ope :: 2;
|
||||
@ -1896,7 +1906,8 @@ kbts_line_break_class :: enum u8 {
|
||||
KBTS_LINE_BREAK_CLASS_EOT :: EOT;
|
||||
}
|
||||
|
||||
kbts_word_break_class :: enum u8 {
|
||||
kbts_word_break_class :: u8;
|
||||
kbts_word_break_class_enum :: enum s32 {
|
||||
Onep :: 0;
|
||||
Oep :: 1;
|
||||
CR :: 2;
|
||||
@ -1946,7 +1957,8 @@ kbts_word_break_class :: enum u8 {
|
||||
KBTS_WORD_BREAK_CLASS_SOT :: SOT;
|
||||
}
|
||||
|
||||
kbts_shaper :: enum u32 {
|
||||
kbts_shaper :: u32;
|
||||
kbts_shaper_enum :: enum s32 {
|
||||
DEFAULT :: 0;
|
||||
ARABIC :: 1;
|
||||
HANGUL :: 2;
|
||||
@ -1972,7 +1984,8 @@ kbts_shaper :: enum u32 {
|
||||
KBTS_SHAPER_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_script_tag :: enum u32 {
|
||||
kbts_script_tag :: u32;
|
||||
kbts_script_tag_enum :: enum s32 {
|
||||
DONT_KNOW :: 538976288;
|
||||
ADLAM :: 1835820129;
|
||||
AHOM :: 1836017761;
|
||||
@ -2662,7 +2675,8 @@ kbts_script :: enum u32 {
|
||||
KBTS_SCRIPT_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_feature_tag :: enum u32 {
|
||||
kbts_feature_tag :: u32;
|
||||
kbts_feature_tag_enum :: enum s32 {
|
||||
UNREGISTERED :: 0;
|
||||
isol :: 1819243369;
|
||||
fina :: 1634625894;
|
||||
@ -2693,7 +2707,7 @@ kbts_feature_tag :: enum u32 {
|
||||
blwm :: 1836543074;
|
||||
blws :: 1937206370;
|
||||
calt :: 1953259875;
|
||||
case_ :: 1702060387;
|
||||
_case :: 1702060387;
|
||||
ccmp :: 1886217059;
|
||||
chws :: 1937205347;
|
||||
cjct :: 1952672355;
|
||||
@ -2939,7 +2953,7 @@ kbts_feature_tag :: enum u32 {
|
||||
KBTS_FEATURE_TAG_blwm :: blwm;
|
||||
KBTS_FEATURE_TAG_blws :: blws;
|
||||
KBTS_FEATURE_TAG_calt :: calt;
|
||||
KBTS_FEATURE_TAG_case :: case_;
|
||||
KBTS_FEATURE_TAG_case :: _case;
|
||||
KBTS_FEATURE_TAG_ccmp :: ccmp;
|
||||
KBTS_FEATURE_TAG_chws :: chws;
|
||||
KBTS_FEATURE_TAG_cjct :: cjct;
|
||||
@ -3156,7 +3170,8 @@ kbts_feature_tag :: enum u32 {
|
||||
KBTS_FEATURE_TAG_zero :: zero;
|
||||
}
|
||||
|
||||
kbts_feature_id :: enum u32 {
|
||||
kbts_feature_id :: u32;
|
||||
kbts_feature_id_enum :: enum s32 {
|
||||
UNREGISTERED :: 0;
|
||||
isol :: 1;
|
||||
fina :: 2;
|
||||
@ -3187,7 +3202,7 @@ kbts_feature_id :: enum u32 {
|
||||
blwm :: 27;
|
||||
blws :: 28;
|
||||
calt :: 29;
|
||||
case_ :: 30;
|
||||
_case :: 30;
|
||||
ccmp :: 31;
|
||||
chws :: 32;
|
||||
cjct :: 33;
|
||||
@ -3434,7 +3449,7 @@ kbts_feature_id :: enum u32 {
|
||||
KBTS_FEATURE_ID_blwm :: blwm;
|
||||
KBTS_FEATURE_ID_blws :: blws;
|
||||
KBTS_FEATURE_ID_calt :: calt;
|
||||
KBTS_FEATURE_ID_case :: case_;
|
||||
KBTS_FEATURE_ID_case :: _case;
|
||||
KBTS_FEATURE_ID_ccmp :: ccmp;
|
||||
KBTS_FEATURE_ID_chws :: chws;
|
||||
KBTS_FEATURE_ID_cjct :: cjct;
|
||||
@ -3652,7 +3667,8 @@ kbts_feature_id :: enum u32 {
|
||||
KBTS_FEATURE_ID_COUNT :: COUNT;
|
||||
}
|
||||
|
||||
kbts_shaping_table :: enum u8 {
|
||||
kbts_shaping_table :: u8;
|
||||
kbts_shaping_table_enum :: enum s32 {
|
||||
GSUB :: 0;
|
||||
GPOS :: 1;
|
||||
COUNT :: 2;
|
||||
@ -3932,7 +3948,8 @@ kbts_bracket :: struct {
|
||||
Script: u8;
|
||||
}
|
||||
|
||||
kbts_break_state_flags :: enum u32 {
|
||||
kbts_break_state_flags :: u32;
|
||||
kbts_break_state_flags_enum :: enum s32 {
|
||||
STARTED :: 1;
|
||||
END :: 2;
|
||||
RAN_OUT_OF_REORDER_BUFFER_SPACE :: 4;
|
||||
|
||||
6
src/.build/.added_strings_w2.jai
Normal file
6
src/.build/.added_strings_w2.jai
Normal file
@ -0,0 +1,6 @@
|
||||
// Workspace: Target Program
|
||||
|
||||
//
|
||||
// String added via add_build_string() from C:/Users/vfs/Dev/Jails/bin/metaprogram/jails_diagnostics.jai:19.
|
||||
//
|
||||
JAILS_DIAGNOSTICS_BUILD :: true;
|
||||
@ -1,6 +1,6 @@
|
||||
#import "Basic";
|
||||
#import "GL";
|
||||
#import "freetype-2.12.1";
|
||||
#import "freetype";
|
||||
#import "Hash_Table";
|
||||
#import "Math";
|
||||
#import "File";
|
||||
@ -26,9 +26,9 @@ using SDL3 :: #import "SDL3";
|
||||
window_width :: 1280;
|
||||
window_height :: 720;
|
||||
|
||||
window : *SDL_Window;
|
||||
window : *SDL_Window;
|
||||
gl_context : SDL_GLContext;
|
||||
running : bool = true;
|
||||
running : bool = true;
|
||||
|
||||
dt : float;
|
||||
frame : u64;
|
||||
|
||||
@ -278,6 +278,8 @@
|
||||
|
||||
STB_TEXTEDIT_STRING :: TextInput;
|
||||
|
||||
STB_TEXTEDIT_GETWIDTH_NEWLINE :: -1.0;
|
||||
|
||||
STB_TEXTEDIT_STRINGLEN :: (obj: *STB_TEXTEDIT_STRING) -> s32 {
|
||||
return xx obj.text.count;
|
||||
}
|
||||
@ -306,6 +308,7 @@ STB_TEXTEDIT_INSERTCHARS :: (obj: *STB_TEXTEDIT_STRING, i: s32, c: *STB_TEXTEDIT
|
||||
|
||||
len := obj.text.count;
|
||||
|
||||
// if the string is empty add an initial new line.
|
||||
if n + len > obj.text.count {
|
||||
array_resize(*obj.text, n + len);
|
||||
}
|
||||
@ -324,12 +327,23 @@ STB_TEXTEDIT_INSERTCHARS :: (obj: *STB_TEXTEDIT_STRING, i: s32, c: *STB_TEXTEDIT
|
||||
|
||||
STB_TEXTEDIT_LAYOUTROW :: (row: *StbTexteditRow, obj: *STB_TEXTEDIT_STRING, n: s32) {
|
||||
idx : s32;
|
||||
if n + idx < obj.text.count && row.x1 < obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding) {
|
||||
|
||||
size, max_descent, read := next_line_size(obj.font, obj.text, n + idx, obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding));
|
||||
row.x1 = size.x;
|
||||
row.baseline_y_delta = size.y;
|
||||
row.ymin = max_descent;
|
||||
row.ymax = size.y - row.ymin;
|
||||
row.num_chars = read;
|
||||
|
||||
return;
|
||||
|
||||
|
||||
if n + idx < obj.text.count && row.x1 <= obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding) {
|
||||
size, max_descent, read := next_line_size(obj.font, obj.text, n + idx, obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding));
|
||||
|
||||
if row.x1 + size.x > obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding) {
|
||||
|
||||
while word := n + idx < obj.text.count && row.x1 < obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding) {
|
||||
while word := n + idx < obj.text.count && row.x1 <= obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding) {
|
||||
size, max_descent, read := next_word_size(obj.font, obj.text, n + idx, obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding));
|
||||
|
||||
if row.x1 + size.x > obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding) {
|
||||
@ -337,7 +351,6 @@ STB_TEXTEDIT_LAYOUTROW :: (row: *StbTexteditRow, obj: *STB_TEXTEDIT_STRING, n: s
|
||||
size, max_descent, read := next_grapheme_size(obj.font, obj.text, n + idx, obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding));
|
||||
|
||||
if row.x1 + size.x > obj.max_size.x - (2 * obj.margin + 2 * obj.border_size + 2 * obj.padding) {
|
||||
//break word;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -357,14 +370,13 @@ STB_TEXTEDIT_LAYOUTROW :: (row: *StbTexteditRow, obj: *STB_TEXTEDIT_STRING, n: s
|
||||
row.num_chars += read;
|
||||
idx += read;
|
||||
}
|
||||
} else {
|
||||
row.x1 = size.x;
|
||||
row.baseline_y_delta = size.y;
|
||||
row.ymin = max_descent;
|
||||
row.ymax = size.y - row.ymin;
|
||||
row.num_chars = read;
|
||||
}
|
||||
|
||||
row.x1 += size.x;
|
||||
row.baseline_y_delta = max(row.baseline_y_delta, size.y);
|
||||
row.ymin = max(row.ymin, max_descent);
|
||||
row.ymax = max(row.ymax, size.y - row.ymin);
|
||||
row.num_chars += read;
|
||||
idx += read;
|
||||
}
|
||||
|
||||
row.x0 = 0.0;
|
||||
@ -373,37 +385,54 @@ STB_TEXTEDIT_LAYOUTROW :: (row: *StbTexteditRow, obj: *STB_TEXTEDIT_STRING, n: s
|
||||
STB_TEXTEDIT_GETWIDTH :: (obj: *STB_TEXTEDIT_STRING, n: s32, i: s32) -> float {
|
||||
textp : *u32 = obj.text.data;
|
||||
Codepoints : *u32 = textp + n;
|
||||
CodepointCount : u64 = xx (obj.text.count - (Codepoints - textp));
|
||||
CodepointCount : u64 = xx (obj.text.count - n);
|
||||
|
||||
Cursor : kbts_cursor;
|
||||
Direction : kbts_direction = .NONE;
|
||||
Script : kbts_script = .DONT_KNOW;
|
||||
RunStart : u64 = 0;
|
||||
|
||||
BreakState : kbts_break_state;
|
||||
kbts_BeginBreak(*BreakState, .NONE, .NORMAL);
|
||||
|
||||
width : float;
|
||||
|
||||
CodepointIndex : u64;
|
||||
while CodepointIndex <= xx i {
|
||||
CodepointIndex : s32 = 0;
|
||||
while CodepointIndex < xx CodepointCount {
|
||||
kbts_BreakAddCodepoint(*BreakState, Codepoints[CodepointIndex], 1, xx ((CodepointIndex + 1) == xx CodepointCount));
|
||||
Break : kbts_break;
|
||||
while kbts_Break(*BreakState, *Break) {
|
||||
|
||||
if Break.Flags & .DIRECTION {
|
||||
Direction = Break.Direction;
|
||||
if(!Cursor.Direction) Cursor = kbts_Cursor(BreakState.MainDirection);
|
||||
}
|
||||
|
||||
if Break.Flags & .SCRIPT {
|
||||
Script = Break.Script;
|
||||
}
|
||||
// if (Break.Position > RunStart) && (Break.Flags & (KBTS_BREAK_FLAG_DIRECTION | KBTS_BREAK_FLAG_SCRIPT | KBTS_BREAK_FLAG_LINE_HARD)) {
|
||||
// RunLength : int = Break.Position - RunStart;
|
||||
// ShapeText(*Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);
|
||||
// RunStart = Break.Position;
|
||||
// }
|
||||
|
||||
if (Break.Position > RunStart) && (Break.Flags & .GRAPHEME) {
|
||||
RunLength : u64 = Break.Position - RunStart;
|
||||
size, max_descent := ShapeText(obj.font, *Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);
|
||||
|
||||
width = size.x;
|
||||
log("Grapheme: % Position: % Run Length: %, width: %", Codepoints[Break.Position - 1], Break.Position, RunLength, width);
|
||||
if Break.Position == xx (i + 1) {
|
||||
if Codepoints[Break.Position - 1] == STB_TEXTEDIT_NEWLINE {
|
||||
width = STB_TEXTEDIT_GETWIDTH_NEWLINE;
|
||||
return width;
|
||||
} else {
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
RunStart = Break.Position;
|
||||
}
|
||||
|
||||
if Break.Flags & .DIRECTION {
|
||||
Direction = Break.Direction;
|
||||
|
||||
if !Cursor.Direction Cursor = kbts_Cursor(BreakState.MainDirection);
|
||||
}
|
||||
|
||||
if Break.Flags & .SCRIPT {
|
||||
Script = Break.Script;
|
||||
}
|
||||
}
|
||||
|
||||
@ -411,6 +440,95 @@ STB_TEXTEDIT_GETWIDTH :: (obj: *STB_TEXTEDIT_STRING, n: s32, i: s32) -> float {
|
||||
}
|
||||
|
||||
return width;
|
||||
|
||||
// Cursor : kbts_cursor;
|
||||
// Direction : kbts_direction = .NONE;
|
||||
// Script : kbts_script = .DONT_KNOW;
|
||||
// RunStart : u64 = 0;
|
||||
// BreakState : kbts_break_state;
|
||||
// kbts_BeginBreak(*BreakState, .NONE, .NORMAL);
|
||||
|
||||
// width : float;
|
||||
|
||||
// CodepointIndex : u64;
|
||||
// while CodepointIndex < CodepointCount {
|
||||
// kbts_BreakAddCodepoint(*BreakState, Codepoints[CodepointIndex], 1, xx ((CodepointIndex + 1) == xx CodepointCount));
|
||||
// Break : kbts_break;
|
||||
// while kbts_Break(*BreakState, *Break) {
|
||||
|
||||
// if Break.Flags & .DIRECTION {
|
||||
// Direction = Break.Direction;
|
||||
// if(!Cursor.Direction) Cursor = kbts_Cursor(BreakState.MainDirection);
|
||||
// }
|
||||
|
||||
// if Break.Flags & .SCRIPT {
|
||||
// Script = Break.Script;
|
||||
// }
|
||||
|
||||
// if Break.Flags & .GRAPHEME {
|
||||
// RunLength : u64 = Break.Position - RunStart;
|
||||
// if RunLength == 0 continue;
|
||||
// size, max_descent := ShapeText(obj.font, *Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);
|
||||
// width = size.x;
|
||||
// if Break.Position == xx max(i, 1) {
|
||||
// if Codepoints[Break.Position] == STB_TEXTEDIT_NEWLINE {
|
||||
// width = STB_TEXTEDIT_GETWIDTH_NEWLINE;
|
||||
// log("Grapheme: % Position: % Run Length: %, width: %", Codepoints[Break.Position], Break.Position, RunLength, width);
|
||||
// return width;
|
||||
// } else {
|
||||
// log("Grapheme: % Position: % Run Length: %, width: %", Codepoints[Break.Position], Break.Position, RunLength, width);
|
||||
// return width;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// RunStart = Break.Position;
|
||||
// }
|
||||
|
||||
// if Break.Flags & .WORD {
|
||||
// RunLength : u64 = Break.Position - RunStart;
|
||||
// log("Word: Position: % Run Length: %", Break.Position, RunLength);
|
||||
// }
|
||||
|
||||
// if Break.Flags & .LINE_SOFT {
|
||||
// RunLength : u64 = Break.Position - RunStart;
|
||||
// log("Line Soft: Position: % Run Length: %", Break.Position, RunLength);
|
||||
// }
|
||||
|
||||
// if Break.Flags & .LINE_HARD {
|
||||
// RunLength : u64 = Break.Position - RunStart;
|
||||
// log("Line Hard: Position: % Run Length: %", Break.Position, RunLength);
|
||||
// }
|
||||
|
||||
// // if (Break.Position >= RunStart) && (Break.Flags & .ANY) {
|
||||
// // RunLength : u64 = Break.Position - RunStart;
|
||||
// // if RunLength == 0 {
|
||||
// // size, max_descent := ShapeText(obj.font, *Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);
|
||||
|
||||
// // if Codepoints[Break.Position] == STB_TEXTEDIT_NEWLINE {
|
||||
// // width = STB_TEXTEDIT_GETWIDTH_NEWLINE;
|
||||
// // return width;
|
||||
// // } else {
|
||||
// // width = size.x;
|
||||
// // }
|
||||
// // } else if RunLength > 0 {
|
||||
// // size, max_descent := ShapeText(obj.font, *Cursor, Codepoints + RunStart, RunLength, BreakState.MainDirection, Direction, Script);
|
||||
|
||||
// // if Codepoints[Break.Position] == STB_TEXTEDIT_NEWLINE {
|
||||
// // width = STB_TEXTEDIT_GETWIDTH_NEWLINE;
|
||||
// // return width;
|
||||
// // } else {
|
||||
// // width = size.x;
|
||||
// // return width;
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
|
||||
// CodepointIndex += 1;
|
||||
// }
|
||||
|
||||
// return width;
|
||||
}
|
||||
|
||||
STB_TEXTEDIT_K_LEFT :: 0x200000; // keyboard input to move cursor left
|
||||
@ -558,6 +676,7 @@ stb_text_locate_coord :: (str: *STB_TEXTEDIT_STRING, x: float, y: float) -> s32
|
||||
|
||||
// search rows to find one that straddles 'y'
|
||||
while i < n {
|
||||
r = .{};
|
||||
STB_TEXTEDIT_LAYOUTROW(*r, str, i);
|
||||
if r.num_chars <= 0
|
||||
return n;
|
||||
@ -628,7 +747,7 @@ stb_textedit_drag :: (str: *STB_TEXTEDIT_STRING, state: *STB_TexteditState, x: f
|
||||
// In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
|
||||
// goes off the top or bottom of the text
|
||||
if state.single_line {
|
||||
r :StbTexteditRow ;
|
||||
r : StbTexteditRow ;
|
||||
STB_TEXTEDIT_LAYOUTROW(*r, str, 0);
|
||||
y = r.ymin;
|
||||
}
|
||||
@ -669,10 +788,11 @@ stb_textedit_find_charpos :: (find: *StbFindState, str: *STB_TEXTEDIT_STRING, n:
|
||||
i : s32 = 0;
|
||||
first : s32;
|
||||
|
||||
if n == z {
|
||||
if (str.text[str.text.count - 1] == STB_TEXTEDIT_NEWLINE && n == z - 1) || (n == z) {
|
||||
// if it's at the end, then find the last line -- simpler than trying to
|
||||
// explicitly handle this case in the regular code
|
||||
if single_line {
|
||||
r = .{};
|
||||
STB_TEXTEDIT_LAYOUTROW(*r, str, 0);
|
||||
find.y = 0;
|
||||
find.first_char = 0;
|
||||
@ -684,13 +804,27 @@ stb_textedit_find_charpos :: (find: *StbFindState, str: *STB_TEXTEDIT_STRING, n:
|
||||
find.x = 0;
|
||||
find.height = 1;
|
||||
while (i < z) {
|
||||
r = .{};
|
||||
STB_TEXTEDIT_LAYOUTROW(*r, str, i);
|
||||
if i + r.num_chars == z {
|
||||
break;
|
||||
}
|
||||
|
||||
prev_start = i;
|
||||
i += r.num_chars;
|
||||
}
|
||||
find.first_char = i;
|
||||
find.length = 0;
|
||||
find.length = r.num_chars;
|
||||
find.prev_first = prev_start;
|
||||
|
||||
// now scan to find xpos
|
||||
find.x = r.x0;
|
||||
i = 0;
|
||||
while first + i < n {
|
||||
find.x += STB_TEXTEDIT_GETWIDTH(str, first, i);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -699,6 +833,7 @@ stb_textedit_find_charpos :: (find: *StbFindState, str: *STB_TEXTEDIT_STRING, n:
|
||||
find.y = 0;
|
||||
|
||||
while true {
|
||||
r = .{};
|
||||
STB_TEXTEDIT_LAYOUTROW(*r, str, i);
|
||||
if n < i + r.num_chars
|
||||
break;
|
||||
@ -731,13 +866,25 @@ STB_TEXT_HAS_SELECTION :: (s: *STB_TexteditState) -> bool #expand {
|
||||
stb_textedit_clamp :: (str: *STB_TEXTEDIT_STRING, state: *STB_TexteditState) {
|
||||
n : s32 = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if STB_TEXT_HAS_SELECTION(state) {
|
||||
if (state.select_start > n) state.select_start = n;
|
||||
if (state.select_end > n) state.select_end = n;
|
||||
if str.text[str.text.count - 1] == STB_TEXTEDIT_NEWLINE && state.select_start > n - 1 {
|
||||
state.select_start = n - 1;
|
||||
} else if state.select_start > n
|
||||
state.select_start = n;
|
||||
|
||||
if str.text[str.text.count - 1] == STB_TEXTEDIT_NEWLINE && state.select_end > n - 1 {
|
||||
state.select_end = n - 1;
|
||||
} else if state.select_end > n
|
||||
state.select_end = n;
|
||||
|
||||
// if clamping forced them to be equal, move the cursor to match
|
||||
if (state.select_start == state.select_end)
|
||||
state.cursor = state.select_start;
|
||||
}
|
||||
if (state.cursor > n) state.cursor = n;
|
||||
if str.text[str.text.count - 1] == STB_TEXTEDIT_NEWLINE && state.cursor > n - 1 {
|
||||
state.cursor = n - 1;
|
||||
} else if state.cursor > n {
|
||||
state.cursor = n;
|
||||
}
|
||||
}
|
||||
|
||||
// delete characters while updating undo
|
||||
@ -880,8 +1027,12 @@ while retry := true {
|
||||
ch : STB_TEXTEDIT_CHARTYPE = cast(STB_TEXTEDIT_CHARTYPE, c);
|
||||
|
||||
// can't add newline in single-line mode
|
||||
if c == #char "\n" && state.single_line
|
||||
if c == #char "\n" && state.single_line {
|
||||
break;
|
||||
} else if str.text.count == 0 {
|
||||
new_line : u32 = #char "\n";
|
||||
STB_TEXTEDIT_INSERTCHARS(str, state.cursor, *new_line, 1);
|
||||
}
|
||||
|
||||
if state.insert_mode && !STB_TEXT_HAS_SELECTION(state) && state.cursor < STB_TEXTEDIT_STRINGLEN(str) {
|
||||
stb_text_makeundo_replace(str, state, state.cursor, 1, 1);
|
||||
@ -1024,6 +1175,7 @@ while retry := true {
|
||||
|
||||
// now find character position down a row
|
||||
state.cursor = start;
|
||||
row = .{};
|
||||
STB_TEXTEDIT_LAYOUTROW(*row, str, state.cursor);
|
||||
x = row.x0;
|
||||
for i : 0..row.num_chars - 1 {
|
||||
@ -1090,6 +1242,7 @@ while retry := true {
|
||||
|
||||
// now find character position up a row
|
||||
state.cursor = find.prev_first;
|
||||
row = .{};
|
||||
STB_TEXTEDIT_LAYOUTROW(*row, str, state.cursor);
|
||||
x = row.x0;
|
||||
for i : 0..row.num_chars - 1 {
|
||||
|
||||
189
src/text.jai
189
src/text.jai
@ -36,8 +36,6 @@ Font :: struct {
|
||||
|
||||
glyphs : Table(u32, Glyph);
|
||||
|
||||
//hb : *hb_font_t;
|
||||
|
||||
kb : kbts_font;
|
||||
|
||||
texture : Texture;
|
||||
@ -61,7 +59,6 @@ init_font :: (using font: *Font, filename: string, size: s32) {
|
||||
return;
|
||||
}
|
||||
|
||||
//FT_Set_Pixel_Sizes(face, 0, size);
|
||||
error = FT_Set_Char_Size(face, 0, size * 64, 0, 96);
|
||||
if error {
|
||||
log("%", to_string(FT_Error_String(error)));
|
||||
@ -113,34 +110,6 @@ init_font :: (using font: *Font, filename: string, size: s32) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// for 0..face.num_glyphs {
|
||||
// //for 65..90 {
|
||||
// //index := FT_Get_Char_Index(face, xx it);
|
||||
// error = FT_Load_Glyph(face, cast(u32) it, FT_LOAD_RENDER);
|
||||
// if error
|
||||
// logd("%", to_string(FT_Error_String(error)));
|
||||
|
||||
// array_add(*rects, stbrp_rect.{cast(s32) face.glyph.glyph_index, xx (face.glyph.bitmap.width + 2),
|
||||
// xx (face.glyph.bitmap.rows + 2), 0, 0, 0});
|
||||
|
||||
// glyph : Glyph;
|
||||
// glyph.utf32 = cast(u32) it;
|
||||
// glyph.index = face.glyph.glyph_index;
|
||||
// glyph.bearing_x = cast,trunc(s16) face.glyph.bitmap_left;
|
||||
// glyph.bearing_y = cast,trunc(s16) face.glyph.bitmap_top;
|
||||
// glyph.width = xx face.glyph.bitmap.width;
|
||||
// glyph.height = xx face.glyph.bitmap.rows;
|
||||
// glyph.rwidth = xx face.glyph.metrics.width >> 6;
|
||||
// glyph.rheight = xx face.glyph.metrics.height >> 6;
|
||||
// glyph.descent = xx (cast(float) face.glyph.bitmap.rows - cast(float) face.glyph.bitmap_top);
|
||||
// glyph.ascent = xx (cast(float) face.glyph.bitmap.rows - glyph.descent);
|
||||
// glyph.advance = cast(s16) face.glyph.advance.x >> 6;
|
||||
// glyph.bitmap = alloc(glyph.height * glyph.width);
|
||||
// memcpy(glyph.bitmap, face.glyph.bitmap.buffer, glyph.height * glyph.width);
|
||||
|
||||
// table_set(*font.glyphs, glyph.index, glyph);
|
||||
// }
|
||||
|
||||
stbrp_pack_rects(*stbrpcontext, rects.data, cast(s32) rects.count);
|
||||
|
||||
|
||||
@ -181,33 +150,9 @@ init_font :: (using font: *Font, filename: string, size: s32) {
|
||||
}
|
||||
|
||||
flip(atlas);
|
||||
|
||||
// for rect : rects {
|
||||
// glyph : *Glyph = table_find_pointer(*font.glyphs, cast(u32) rect.id);
|
||||
// if (glyph.bitmap) {
|
||||
// glyph.x = cast,trunc(s16) rect.x;
|
||||
// glyph.y = ATLAS_SIZE - 1 - cast,trunc(s16) rect.y - xx glyph.height;
|
||||
// glyph.st0 = .{cast(float, glyph.x) / ATLAS_SIZE, cast(float, glyph.y) / ATLAS_SIZE};
|
||||
// glyph.st1 = glyph.st0 + .{cast(float, glyph.width) / ATLAS_SIZE, cast(float, glyph.height) / ATLAS_SIZE};
|
||||
// }
|
||||
// }
|
||||
|
||||
texture = make_texture_from_data(atlas, ATLAS_SIZE, ATLAS_SIZE);
|
||||
|
||||
// blob : *hb_blob_t = hb_blob_create_or_fail(font_data.data, xx font_data.count, .HB_MEMORY_MODE_DUPLICATE, null, null);
|
||||
// if blob == null {
|
||||
// loge("Could not create the HarfBuzz blob.");
|
||||
// }
|
||||
|
||||
// hb_face : *hb_face_t = hb_face_create_or_fail(blob, 0);
|
||||
// if hb_face == null {
|
||||
// loge("Could not create the HarfBuzz face.");
|
||||
// }
|
||||
|
||||
// hb = hb_font_create(hb_face);
|
||||
// hb_font_set_ppem(hb, xx pixel_size, xx pixel_size);
|
||||
// hb_font_set_scale(hb, cast(s32) pixel_size * 64, cast(s32) pixel_size * 64);
|
||||
|
||||
ScratchSize : u64 = kbts_ReadFontHeader(*font.kb, font_data.data, xx font_data.count);
|
||||
PermanentMemorySize : u64 = kbts_ReadFontData(*font.kb, alloc(xx ScratchSize), ScratchSize);
|
||||
kbts_PostReadFontInitialize(*font.kb, alloc(xx PermanentMemorySize), PermanentMemorySize);
|
||||
@ -271,15 +216,9 @@ render_text :: (font: *Font, text: []u32, pos: Vector2, size: Vector2, window_sp
|
||||
X, Y : s32;
|
||||
kbts_PositionGlyph(*Cursor, kglyph, *X, *Y);
|
||||
|
||||
// x_offset : hb_position_t = glyph_pos[i].x_offset;
|
||||
// y_offset : hb_position_t = glyph_pos[i].y_offset;
|
||||
// x_advance : hb_position_t = glyph_pos[i].x_advance;
|
||||
// y_advance : hb_position_t = glyph_pos[i].y_advance;
|
||||
|
||||
glyph : *Glyph = table_find_pointer(*font.glyphs, kglyph.Id);
|
||||
|
||||
if !glyph {
|
||||
//log("[Error] Panic! Didn't find the glyph!");
|
||||
GlyphIndex += 1;
|
||||
continue;
|
||||
}
|
||||
@ -316,80 +255,11 @@ render_text :: (font: *Font, text: []u32, pos: Vector2, size: Vector2, window_sp
|
||||
array_add(*vertices, .{.{v0.x, v1.y}, .{t0.x, t1.y}, colour});
|
||||
array_add(*vertices, .{v1, t1, colour});
|
||||
|
||||
//render_pos += Vector2.{x_advance / 64.0, y_advance / 64.0};
|
||||
|
||||
GlyphIndex += 1;
|
||||
}
|
||||
|
||||
free(Glyphs);
|
||||
|
||||
//size, max_descent := ShapeText(font, *Cursor, text_utf32.data, xx text_utf32.count, Direction, Direction, Script);
|
||||
|
||||
// buf : *hb_buffer_t = hb_buffer_create();
|
||||
// hb_buffer_add_utf8(buf, text.data, xx text.count, 0, -1);
|
||||
|
||||
// hb_buffer_set_direction(buf, hb_direction_t.LTR);
|
||||
// hb_buffer_set_script(buf, hb_script_t.HB_SCRIPT_LATIN);
|
||||
// hb_buffer_set_language(buf, hb_language_from_string("en", -1));
|
||||
|
||||
// features : [1]hb_feature_t;
|
||||
// features[0].tag = HB_TAG(#char "c", #char "a", #char "l", #char "t");
|
||||
// features[0].value = 1;
|
||||
// features[0].start = HB_FEATURE_GLOBAL_START;
|
||||
// features[0].end = HB_FEATURE_GLOBAL_END;
|
||||
|
||||
// hb_shape(font.hb, buf, features.data, features.count);
|
||||
|
||||
// glyph_count : u32;
|
||||
// glyph_info : *hb_glyph_info_t = hb_buffer_get_glyph_infos(buf, *glyph_count);
|
||||
// glyph_pos : *hb_glyph_position_t = hb_buffer_get_glyph_positions(buf, *glyph_count);
|
||||
|
||||
|
||||
|
||||
// draw_size, max_ascent, max_descent := calculate_string_draw_size(font, text);
|
||||
|
||||
// for i : 0..glyph_count - 1 {
|
||||
// glyphid : hb_codepoint_t = glyph_info[i].codepoint;
|
||||
// x_offset : hb_position_t = glyph_pos[i].x_offset;
|
||||
// y_offset : hb_position_t = glyph_pos[i].y_offset;
|
||||
// x_advance : hb_position_t = glyph_pos[i].x_advance;
|
||||
// y_advance : hb_position_t = glyph_pos[i].y_advance;
|
||||
|
||||
// glyph : *Glyph = table_find_pointer(*font.glyphs, glyphid);
|
||||
|
||||
// v0 : Vector2;
|
||||
// v1 : Vector2;
|
||||
// if count_descent {
|
||||
// v0 = render_pos + .{cast(float) x_offset + glyph.bearing_x,
|
||||
// cast(float) y_offset - glyph.bearing_y + draw_size.y - max_descent};
|
||||
// } else {
|
||||
// v0 = render_pos + .{cast(float) x_offset + glyph.bearing_x,
|
||||
// cast(float) y_offset - (xx glyph.height - glyph.bearing_y)/* - glyph.height + draw_size.y*/};
|
||||
// }
|
||||
|
||||
// v1 = v0 + Vector2.{cast(float) glyph.width, cast(float) glyph.height};
|
||||
|
||||
// // #if Y_IS_UP {
|
||||
// // t0 := Vector2.{cast(float, glyph.x) / cast(float, ATLAS_SIZE), cast(float, glyph.y) / cast(float, ATLAS_SIZE)};
|
||||
// // t1 := t0 + Vector2.{cast(float, glyph.width) / cast(float, ATLAS_SIZE), -cast(float, glyph.height) / cast(float, ATLAS_SIZE)};
|
||||
// t0 := glyph.st0;
|
||||
// t1 := glyph.st1;
|
||||
// // } else {
|
||||
// // t0 := Vector2.{cast(float, glyph.x / ATLAS_SIZE), cast(float, glyph.y / ATLAS_SIZE)};
|
||||
// // t1 := t0 + .{cast(float, glyph.width / ATLAS_SIZE), cast(float, glyph.height / ATLAS_SIZE)};
|
||||
// // }
|
||||
|
||||
// array_add(*vertices, .{v0, t0, colour});
|
||||
// array_add(*vertices, .{.{v0.x, v1.y}, .{t0.x, t1.y}, colour});
|
||||
// array_add(*vertices, .{.{v1.x, v0.y}, .{t1.x, t0.y}, colour});
|
||||
|
||||
// array_add(*vertices, .{.{v1.x, v0.y}, .{t1.x, t0.y}, colour});
|
||||
// array_add(*vertices, .{.{v0.x, v1.y}, .{t0.x, t1.y}, colour});
|
||||
// array_add(*vertices, .{v1, t1, colour});
|
||||
|
||||
// render_pos += Vector2.{x_advance / 64.0, y_advance / 64.0};
|
||||
// }
|
||||
// hb_buffer_destroy(buf);
|
||||
|
||||
view := identity_of(Matrix4);
|
||||
proj := window_proj;
|
||||
@ -400,58 +270,6 @@ render_text :: (font: *Font, text: []u32, pos: Vector2, size: Vector2, window_sp
|
||||
// restore_opengl_state(*opengl_state);
|
||||
}
|
||||
|
||||
// string_size :: (font: *Font, Cursor: *kbts_cursor, text: string, MainDirection: kbts_direction, Direction: kbts_direction, Script: kbts_script) -> Vector2 {
|
||||
// text_utf32 : [..]u32;
|
||||
// defer array_free(text_utf32);
|
||||
|
||||
// StringAt : u64;
|
||||
// while StringAt < xx text.count {
|
||||
// Decode : kbts_decode = kbts_DecodeUtf8(text.data, xx text.count - StringAt);
|
||||
// StringAt += Decode.SourceCharactersConsumed;
|
||||
// if Decode.Valid {
|
||||
// array_add(*text_utf32, Decode.Codepoint);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Glyphs : *kbts_glyph = cast(*kbts_glyph, alloc(size_of(kbts_glyph) * text_utf32.count));
|
||||
|
||||
// CodepointIndex: u64;
|
||||
// while CodepointIndex < xx text_utf32.count {
|
||||
// Glyphs[CodepointIndex] = kbts_CodepointToGlyph(*font.kb, text_utf32[CodepointIndex]);
|
||||
|
||||
// CodepointIndex += 1;
|
||||
// }
|
||||
|
||||
// State : *kbts_shape_state = kbts_CreateShapeState(*font.kb);
|
||||
// Config : kbts_shape_config = kbts_ShapeConfig(*font.kb, Script, xx kbts_language_enum.KBTS_LANGUAGE_DONT_KNOW);
|
||||
|
||||
// size : Vector2;
|
||||
|
||||
// GlyphCount : u32 = xx text_utf32.count;
|
||||
// GlyphCapacity : u32 = GlyphCount;
|
||||
// while kbts_Shape(State, *Config, MainDirection, Direction, Glyphs, *GlyphCount, GlyphCapacity) {
|
||||
// Glyphs = cast(*kbts_glyph, realloc(Glyphs, size_of(kbts_glyph) * State.RequiredGlyphCapacity, size_of(kbts_glyph) * GlyphCapacity));
|
||||
// GlyphCapacity = State.RequiredGlyphCapacity;
|
||||
// }
|
||||
|
||||
// GlyphIndex : u64;
|
||||
// while GlyphIndex < GlyphCount {
|
||||
// Glyph : *kbts_glyph = *Glyphs[GlyphIndex];
|
||||
|
||||
// X : s32;
|
||||
// Y : s32;
|
||||
// kbts_PositionGlyph(Cursor, Glyph, *X, *Y);
|
||||
|
||||
// //RenderGlyph(Glyph, X, Y);
|
||||
|
||||
// GlyphIndex += 1;
|
||||
// }
|
||||
|
||||
// free(Glyphs);
|
||||
|
||||
// return size;
|
||||
// }
|
||||
|
||||
ShapeText :: (font: *Font, Cursor: *kbts_cursor, Codepoints: *u32, CodepointCount: u64, MainDirection: kbts_direction, Direction: kbts_direction, Script: kbts_script) -> Vector2, float {
|
||||
kglyphs : *kbts_glyph = cast(*kbts_glyph, alloc(xx (size_of(kbts_glyph) * CodepointCount)));
|
||||
|
||||
@ -488,12 +306,8 @@ ShapeText :: (font: *Font, Cursor: *kbts_cursor, Codepoints: *u32, CodepointCoun
|
||||
size.y = max(size.y, xx glyph.height);
|
||||
size.x += xx (FT_MulFix(kglyph.AdvanceX, font.face.size.metrics.x_scale) >> 6);
|
||||
max_descent = max(max_descent, xx glyph.descent);
|
||||
//line.max_descent = max(line.max_descent, cast(float) glyph.descent);
|
||||
//line.max_ascent = max(line.max_ascent, cast(float) glyph.ascent);
|
||||
}
|
||||
|
||||
//RenderGlyph(Glyph, X, Y);
|
||||
|
||||
GlyphIndex += 1;
|
||||
}
|
||||
|
||||
@ -555,9 +369,6 @@ next_word_size :: (font : *Font, text : []u32, n : s32, max_width : float = 0.0)
|
||||
BreakState : kbts_break_state;
|
||||
kbts_BeginBreak(*BreakState, .NONE, .NORMAL);
|
||||
|
||||
// size : Vector2;
|
||||
// max_descent : float;
|
||||
|
||||
CodepointIndex : u64;
|
||||
while CodepointIndex < xx CodepointCount {
|
||||
kbts_BreakAddCodepoint(*BreakState, Codepoints[CodepointIndex], 1, xx ((CodepointIndex + 1) == xx CodepointCount));
|
||||
|
||||
131
src/ui.jai
131
src/ui.jai
@ -78,7 +78,6 @@ UIContext :: struct {
|
||||
sizing_y : UISizing;
|
||||
offaxis_layout : UIOffAxisLayout;
|
||||
|
||||
// font : *Simp.Dynamic_Font;
|
||||
font : *Font;
|
||||
|
||||
margin : s32 = 2;
|
||||
@ -159,7 +158,6 @@ Label :: struct {
|
||||
text : string;
|
||||
text_size : Vector2;
|
||||
max_descent : float;
|
||||
//lines : [..]Line;
|
||||
}
|
||||
|
||||
Icon :: struct {
|
||||
@ -313,7 +311,7 @@ ui_render :: () {
|
||||
|
||||
ui_begin :: (s: string) {
|
||||
text, key := ui_decompose_and_generate_id(null, s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
root : *Rect = xx ptr;
|
||||
if !success {
|
||||
new_root := New(Rect);
|
||||
@ -382,7 +380,7 @@ global_max_width : float = 100.0;
|
||||
|
||||
ui_label :: (s: string = "", colour := Vector4.{1.0, 1.0, 1.0, 1.0}) {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
label : *Label = xx ptr;
|
||||
if !success {
|
||||
new_label := New(Label);
|
||||
@ -534,7 +532,7 @@ ui_draw_label :: (using label: *Label) {
|
||||
|
||||
ui_icon :: (s: string, texture: *Texture, size: Vector2) {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
icon : *Icon = xx ptr;
|
||||
if !success {
|
||||
new_icon := New(Icon);
|
||||
@ -580,7 +578,7 @@ ui_draw_icon :: (using icon: *Icon) {
|
||||
|
||||
ui_font_icon :: (font: *Font, s: string, size: Vector2, colour: Vector4 = .{1, 1, 1, 1}) {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
icon : *FontIcon = xx ptr;
|
||||
if !success {
|
||||
new_icon := New(FontIcon);
|
||||
@ -613,10 +611,6 @@ ui_font_icon :: (font: *Font, s: string, size: Vector2, colour: Vector4 = .{1, 1
|
||||
ui_draw_font_icon :: (using font_icon: *FontIcon) {
|
||||
midline : Vector2 = pos + Vector2.{cast(float, margin + border_size + padding), xx (size.y / 2.0)};
|
||||
|
||||
// label_size := Vector2.{xx Simp.prepare_icon(font, name), xx font.character_height};
|
||||
|
||||
// Simp.draw_prepared_text(font, xx midline.x, xx (midline.y - label_size.y / 2.0), colour);
|
||||
|
||||
// Backup GL state
|
||||
// opengl_state : OpenGLState;
|
||||
// save_opengl_state(*opengl_state);
|
||||
@ -633,7 +627,7 @@ ui_draw_font_icon :: (using font_icon: *FontIcon) {
|
||||
|
||||
ui_button :: (s: string = "", icon_texture: *Texture = null, font_colour := Vector4.{1, 1, 1, 1}, draw_frame := true) -> bool {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
button : *Button = xx ptr;
|
||||
if !success {
|
||||
new_button := New(Button);
|
||||
@ -773,7 +767,7 @@ ui_draw_button :: (using button: *Button) {
|
||||
|
||||
ui_scrollbar :: (s: string = "", icon_texture: *Texture = null, font_colour := Vector4.{1, 1, 1, 1}, draw_frame := true) -> bool {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
scrollbar : *ScrollBar = xx ptr;
|
||||
if !success {
|
||||
new_scrollbar := New(ScrollBar);
|
||||
@ -884,7 +878,7 @@ ui_draw_scroll_bar :: (using scroll_bar: *ScrollBar) {
|
||||
|
||||
ui_text_input :: (s: string, font_colour := Vector4.{1, 1, 1, 1}) {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
text_input : *TextInput = xx ptr;
|
||||
if !success {
|
||||
new_text_input := New(TextInput);
|
||||
@ -906,32 +900,22 @@ ui_text_input :: (s: string, font_colour := Vector4.{1, 1, 1, 1}) {
|
||||
ui_append_to_parent(text_input);
|
||||
|
||||
text_size : Vector2;
|
||||
//text_size.y = xx text_input.font.face.size.metrics.height >> 6;
|
||||
//str := builder_to_string(*text_input.input_buffer, 0, false);
|
||||
// if str.count > 0 {
|
||||
// defer free(str);
|
||||
// textsize = Vector2.{xx Simp.prepare_text(text_input.font, str), xx text_input.font.character_height};
|
||||
// }
|
||||
max_descent : float;
|
||||
// idx : s32;
|
||||
// while idx < text_input.text.count {
|
||||
// word_size, word_descent, read := next_word_size(text_input.font, text_input.text, idx, 0.0);
|
||||
|
||||
// text_size.x += word_size.x;
|
||||
// text_size.y = max(text_size.y, word_size.y);
|
||||
// max_descent = max(max_descent, word_descent);
|
||||
|
||||
// idx += read;
|
||||
// }
|
||||
|
||||
i : s32;
|
||||
while i < text_input.text.count {
|
||||
row : StbTexteditRow;
|
||||
STB_TEXTEDIT_LAYOUTROW(*row, text_input, i);
|
||||
|
||||
is_new_line : bool = text_input.text[i + row.num_chars - 1] == #char "\n";
|
||||
|
||||
text_size.x = max(text_size.x, row.x1);
|
||||
text_size.y += text_input.font.face.size.metrics.height >> 6;
|
||||
|
||||
// if is_new_line && text_input.text.count <= i + row.num_chars {
|
||||
// text_size.y += text_input.font.face.size.metrics.height >> 6;
|
||||
// }
|
||||
|
||||
i += xx row.num_chars;
|
||||
}
|
||||
|
||||
@ -1005,23 +989,28 @@ ui_draw_text_input :: (using text_input: *TextInput) {
|
||||
row : StbTexteditRow;
|
||||
STB_TEXTEDIT_LAYOUTROW(*row, text_input, i);
|
||||
|
||||
is_new_line : bool = text_input.text[i + row.num_chars - 1] == #char "\n";
|
||||
|
||||
select_rectangle : Rectangle;
|
||||
|
||||
if i + xx row.num_chars >= textedit_state.cursor {
|
||||
log("Cursor: %", textedit_state.cursor);
|
||||
|
||||
if i + row.num_chars >= textedit_state.cursor && !(is_new_line && textedit_state.cursor == i + row.num_chars) {
|
||||
|
||||
j : s32;
|
||||
while grapheme := i + j < textedit_state.cursor {
|
||||
size, max_descent, read := next_grapheme_size(font, text, i + j, 0.0);
|
||||
size, max_descent, read := next_grapheme_size(font, text, i + j, 0.0);
|
||||
|
||||
cursor_pos.x += size.x;
|
||||
j += read;
|
||||
cursor_pos.x += size.x;
|
||||
|
||||
j += read;
|
||||
}
|
||||
} else {
|
||||
cursor_pos.y -= font.face.size.metrics.height >> 6;
|
||||
}
|
||||
|
||||
render_text(font, .{row.num_chars, text_input.text.data + i},
|
||||
.{draw_pos.x, draw_pos.y + row.ymin}, text_input.size,
|
||||
colour = font_colour);
|
||||
.{draw_pos.x, draw_pos.y + row.ymin}, text_input.size,colour = font_colour);
|
||||
|
||||
if textedit_state.select_start < textedit_state.select_end {
|
||||
|
||||
@ -1087,6 +1076,72 @@ ui_draw_text_input :: (using text_input: *TextInput) {
|
||||
}
|
||||
}
|
||||
|
||||
render_filled_rectangle(select_pos + v2(margin) + v2(border_size) + v2(padding), select_size, .{0.8, 0.2, 0.55, 0.4});
|
||||
}
|
||||
} else if textedit_state.select_start > textedit_state.select_end {
|
||||
|
||||
if textedit_state.select_start > i && textedit_state.select_end < i + row.num_chars {
|
||||
|
||||
select_size : Vector2 = .{0.0, xx font.face.size.metrics.height >> 6};
|
||||
|
||||
if textedit_state.select_end < i && textedit_state.select_start > i + row.num_chars {
|
||||
select_pos.x = 0.0;
|
||||
select_size.x = row.x1 - row.x0;
|
||||
}
|
||||
|
||||
if textedit_state.select_end >= i && textedit_state.select_start > i + row.num_chars {
|
||||
j : s32;
|
||||
while grapheme := i + j < textedit_state.select_end && i + j < i + row.num_chars {
|
||||
size, max_descent, read, segment_type := next_segment_size(font, text, i + j, 0.0, .GRAPHEME | .LINE_HARD);
|
||||
|
||||
if segment_type & .GRAPHEME {
|
||||
select_pos.x += size.x;
|
||||
j += read;
|
||||
} else {
|
||||
select_pos.y -= font.face.size.metrics.height >> 6;
|
||||
}
|
||||
}
|
||||
|
||||
select_size.x = row.x1 - select_pos.x;
|
||||
}
|
||||
|
||||
if textedit_state.select_end < i && textedit_state.select_start <= i + row.num_chars {
|
||||
select_pos.x = 0.0;
|
||||
|
||||
j : s32;
|
||||
while grapheme := i + j < textedit_state.select_start && i + j < i + row.num_chars {
|
||||
size, max_descent, read, segment_type := next_segment_size(font, text, i + j, 0.0, .GRAPHEME | .LINE_HARD);
|
||||
|
||||
if segment_type & .GRAPHEME {
|
||||
select_size.x += size.x;
|
||||
j += read;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if textedit_state.select_end >= i && textedit_state.select_start <= i + row.num_chars {
|
||||
j : s32;
|
||||
|
||||
while grapheme := i + j < textedit_state.select_end && i + j < i + row.num_chars {
|
||||
size, max_descent, read, segment_type := next_segment_size(font, text, i + j, 0.0, .GRAPHEME | .LINE_HARD);
|
||||
|
||||
if segment_type & .GRAPHEME {
|
||||
select_pos.x += size.x;
|
||||
j += read;
|
||||
}
|
||||
}
|
||||
|
||||
while grapheme := i + j < textedit_state.select_start && i + j < i + row.num_chars {
|
||||
size, max_descent, read, segment_type := next_segment_size(font, text, i + j, 0.0, .GRAPHEME | .LINE_HARD);
|
||||
|
||||
if segment_type & .GRAPHEME {
|
||||
select_size.x += size.x;
|
||||
j += read;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render_filled_rectangle(select_pos + v2(margin) + v2(border_size) + v2(padding), select_size, .{0.8, 0.2, 0.55, 0.4});
|
||||
}
|
||||
}
|
||||
@ -1117,7 +1172,7 @@ ui_draw_text_input :: (using text_input: *TextInput) {
|
||||
|
||||
ui_begin_container :: (s: string, gradient := false, colour_left := Vector4.{}, colour_right := Vector4.{}, max_x: s32 = -1, max_y: s32 = -1, flags: ContainerFlags = 0) -> bool {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
container : *Container = xx ptr;
|
||||
if !success {
|
||||
new_container := New(Container);
|
||||
@ -1219,7 +1274,7 @@ ui_draw_container :: (using container: *Container) {
|
||||
|
||||
ui_spacing :: (s: string) {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
spacing : *Spacing = xx ptr;
|
||||
if !success {
|
||||
new_spacing := New(Spacing);
|
||||
@ -1251,7 +1306,7 @@ ui_spacing :: (s: string) {
|
||||
|
||||
ui_progress_bar :: (s: string = "", progress: float, size: Vector2, background := Vector4.{1, 1, 1, 1}, foreground := Vector4.{1, 1, 1, 1}) {
|
||||
text, key := ui_decompose_and_generate_id(peek(ui_context.stack), s);
|
||||
success, ptr := table_find_new(*ui_context.rects, key);
|
||||
success, ptr := table_find(*ui_context.rects, key);
|
||||
bar : *ProgressBar = xx ptr;
|
||||
if !success {
|
||||
new_bar := New(ProgressBar);
|
||||
|
||||
45
ui.rad
45
ui.rad
@ -1,45 +0,0 @@
|
||||
// raddbg 0.9.21 project file
|
||||
|
||||
recent_file: path: "src/stb_textedit.jai"
|
||||
recent_file: path: "src/ui.jai"
|
||||
recent_file: path: "../../../../jai/modules/runtime_support.jai"
|
||||
recent_file: path: "src/main.jai"
|
||||
recent_file: path: "src/text.jai"
|
||||
recent_file: path: "../../../../jai/modules/basic/array.jai"
|
||||
recent_file: path: "../../../../jai/modules/default_allocator/module.jai"
|
||||
recent_file: path: "../../../../jai/modules/math/module.jai"
|
||||
recent_file: path: "modules/kb_text_shape/kb_text_shape.h"
|
||||
recent_file: path: "modules/SDL3/src/SDL-release-3.2.16/src/events/sdl_keyboard.c"
|
||||
recent_file: path: "modules/SDL3/src/SDL-release-3.2.16/src/video/windows/SDL_windowsevents.c"
|
||||
recent_file: path: "src/math/math.jai"
|
||||
recent_file: path: "../../../../jai/modules/basic/module.jai"
|
||||
target:
|
||||
{
|
||||
executable: "bin/mexplore-debug.exe"
|
||||
working_directory: bin
|
||||
enabled: 1
|
||||
}
|
||||
breakpoint:
|
||||
{
|
||||
source_location: "src/ui.jai:1032:1"
|
||||
hit_count: 0
|
||||
enabled: 0
|
||||
}
|
||||
breakpoint:
|
||||
{
|
||||
source_location: "src/stb_textedit.jai:995:1"
|
||||
hit_count: 0
|
||||
enabled: 0
|
||||
}
|
||||
breakpoint:
|
||||
{
|
||||
source_location: "src/stb_textedit.jai:1016:1"
|
||||
hit_count: 0
|
||||
enabled: 0
|
||||
}
|
||||
breakpoint:
|
||||
{
|
||||
source_location: "src/stb_textedit.jai:1030:1"
|
||||
enabled: 0
|
||||
hit_count: 0
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user