Victory. Text editor working well. Docs being written.

This commit is contained in:
Vicente Ferrari Smith 2025-10-21 23:49:59 +02:00
parent 406a8a3a71
commit f18795ff79
21 changed files with 640 additions and 427 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -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
View 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.

View File

@ -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"],
];

View File

@ -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;

View File

@ -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

View File

@ -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;

View 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;

View File

@ -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;

View File

@ -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 {

View File

@ -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));

View File

@ -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
View File

@ -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
}