Compare commits

...

4 Commits

12 changed files with 583 additions and 63 deletions

8
debug.plist Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>

View File

@ -3,7 +3,7 @@ alias r := run
build: transpile_shaders_metal
mkdir -p bin
cc -Ivendor/ -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor
cc -Ivendor/ -O0 -g -Wall -Wextra -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC src/*.c -o bin/an_editor
# cc -Ivendor/ -g -Wall -Wextra src/*.c -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor
# cc bin/*.o -o bin/an_editor -lEGL -lGLESv2 -lGL -lm -lX11 -lXi -lXcursor

View File

@ -126,7 +126,7 @@ fragment float4 ui_rect_fs(UiRectFragment in [[stage_in]])
float distance = rect_sdf(pixel_pos, in.position, in.size, in.border_size.x);
if (distance <= 0.0) {
return float4(1,1,1,0.5);
return float4(0.2,0.2,0.2,1.0);
} else {
return float4(0);
}

229
src/buffer.h Normal file
View File

@ -0,0 +1,229 @@
#ifndef ED_BUFFER_INCLUDED
#define ED_BUFFER_INCLUDED
#include "ed_array.h"
#include "string.h"
#include "ui.h"
#include <stdbool.h>
typedef struct {
size_t link_index;
size_t content_index;
} ed_buffer_offset;
typedef struct {
uint64_t column;
uint64_t line;
ed_buffer_offset offset;
} ed_buffer_cursor;
typedef enum {
ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL,
ED_BUFFER_CONTENT_LINK_TYPE_ADDED,
} ed_buffer_content_link_t;
typedef struct {
ed_buffer_content_link_t type;
size_t start;
size_t end;
size_t len;
} ed_buffer_content_link;
arrayTemplate(ed_buffer_content_link);
typedef struct {
array(uint8_t) original_content;
array(uint8_t) added_content;
array(ed_buffer_content_link) linked_content;
} ed_buffer_data;
typedef struct {
ed_buffer_data data;
ed_buffer_cursor cursor;
} ed_buffer;
#ifdef ED_BUFFER_IMPLEMENTATION
static ed_buffer_data _ed_buffer_init_buffer_data(slice(uint8_t) data) {
array(ed_buffer_content_link) linked_content = newArray(ed_buffer_content_link, 16);
pushArray(ed_buffer_content_link, &linked_content, ((ed_buffer_content_link) {
.type = ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL,
.start = 0,
.end = data.len,
.len = data.len,
}));
return (ed_buffer_data) {
.original_content = newArrayFromSlice(uint8_t, data),
.added_content = newArray(uint8_t, 512),
.linked_content = linked_content,
};
}
uint8_t ed_buffer_get_char_at(ed_buffer *buffer, ed_buffer_cursor cursor) {
ed_buffer_content_link link = buffer->data.linked_content.data[cursor.offset.link_index];
uint8_t character;
if (link.type == ED_BUFFER_CONTENT_LINK_TYPE_ORIGINAL) {
character = buffer->data.original_content.data[link.start+cursor.offset.content_index];
} else if (link.type == ED_BUFFER_CONTENT_LINK_TYPE_ADDED) {
character = buffer->data.added_content.data[link.start+cursor.offset.content_index];
} else {
character = 0;
}
return character;
}
ed_buffer ed_buffer_from_file(string path) {
char text[] =
"bool ht_set(ed_ht *ht, string key, void *value) {\n"
" ht_hash_t hash = ht_hash(key, ht->capacity);\n"
"\n"
" for (size_t i=hash; i<ht->capacity; ++i) {\n"
" if (ht->key_slots[i].key.data == NULL || string_eq(ht->key_slots[i].key, key)) {\n"
" if (ht->key_slots[i].key.data != NULL) {\n"
" free(ht->key_slots[i].key.data);\n"
" }\n"
" ht->key_slots[i].key = string_copy(key);\n"
"\n"
" memcpy(ht->value_slots+i*ht->value_size, value, ht->value_size);\n"
" return true;\n"
" }\n"
" }\n"
" return false;\n"
"}\n";
slice(uint8_t) data = {
.data = text,
.len = sizeof(text),
};
ed_buffer buffer = {
.data = _ed_buffer_init_buffer_data(data),
.cursor = {
.column = 0,
.line = 0,
.offset = {
.link_index = 0,
.content_index = 0,
},
}
};
return buffer;
}
bool ed_buffer_content_iter(ed_buffer *buffer, ed_buffer_cursor *cursor) {
if (cursor->offset.link_index >= buffer->data.linked_content.size
|| cursor->offset.content_index >= (buffer->data.linked_content.data[cursor->offset.link_index].len))
{
return false;
}
uint8_t character = ed_buffer_get_char_at(buffer, *cursor);
ed_buffer_content_link link = buffer->data.linked_content.data[cursor->offset.link_index];
if (cursor->offset.content_index < link.len-1) {
cursor->offset.content_index += 1;
} else if (cursor->offset.link_index < buffer->data.linked_content.size-1) {
cursor->offset.content_index = 0;
cursor->offset.link_index += 1;
} else {
return false;
}
if (character == '\n') {
cursor->column = 0;
cursor->line += 1;
} else {
cursor->column += 1;
}
return true;
}
void ed_buffer_insert_text(ed_buffer *buffer, string text, ed_buffer_cursor cursor) {
if (text.len == 0) {
return;
}
size_t content_start = buffer->data.added_content.size;
pushArrayMulti(uint8_t, &buffer->data.added_content, text.data, text.len);
if (cursor.offset.content_index == 0) {
insertArrayAt(ed_buffer_content_link,
&buffer->data.linked_content,
cursor.offset.link_index,
((ed_buffer_content_link) {
.type = ED_BUFFER_CONTENT_LINK_TYPE_ADDED,
.start = content_start,
.end = content_start+text.len,
.len = text.len,
})
);
} else {
ed_buffer_content_link end_link = buffer->data.linked_content.data[cursor.offset.link_index];
end_link.start += cursor.offset.content_index;
end_link.len -= cursor.offset.content_index;
buffer->data.linked_content.data[cursor.offset.link_index].end = end_link.start;
buffer->data.linked_content.data[cursor.offset.link_index].len = buffer->data.linked_content.data[cursor.offset.link_index].end - buffer->data.linked_content.data[cursor.offset.link_index].start;
// insert new content
insertArrayAt(ed_buffer_content_link,
&buffer->data.linked_content,
cursor.offset.link_index+1,
((ed_buffer_content_link) {
.type = ED_BUFFER_CONTENT_LINK_TYPE_ADDED,
.start = content_start,
.end = content_start+text.len,
.len = text.len,
})
);
// insert cut slice
insertArrayAt(ed_buffer_content_link,
&buffer->data.linked_content,
cursor.offset.link_index+2,
end_link
);
}
}
void ed_buffer_render(ui_context *cx, ed_buffer *buffer) {
ui_element(cx, _String("Buffer"), UI_AXIS_VERTICAL, ui_make_size(ui_fill, ui_fill), UI_FLAG_DRAW_BACKGROUND);
ui_push_parent(cx);
{
ed_buffer_cursor cursor = buffer->cursor;
do {
uint8_t line_buffer[512];
size_t line_buffer_index = 0;
do {
uint8_t character = ed_buffer_get_char_at(buffer, cursor);
if (character == '\n' || line_buffer_index >= 512) {
break;
}
line_buffer[line_buffer_index] = character;
line_buffer_index += 1;
} while(ed_buffer_content_iter(buffer, &cursor));
uint8_t *str = malloc(line_buffer_index * sizeof(uint8_t));
memcpy(str, line_buffer, line_buffer_index * sizeof(uint8_t));
ui_element(cx,
(string){ .data = str,
.len = line_buffer_index,
.owned = true
},
UI_AXIS_HORIZONTAL,
ui_make_size(ui_fit_text, ui_fit_text),
UI_FLAG_DRAW_TEXT|UI_FLAG_DRAW_BACKGROUND
);
} while(ed_buffer_content_iter(buffer, &cursor));
}
ui_pop_parent(cx);
}
#endif
#endif

View File

@ -2,5 +2,7 @@
-DED_HT_IMPLEMENTATION
-DED_STRING_IMPLEMENTATION
-DED_GFX_IMPLEMENTATION
-DED_BUFFER_IMPLEMENTATION
-DED_FILE_IO_IMPLEMENTATION
-I../vendor/
-ObjC

View File

@ -1,14 +1,16 @@
#ifndef ED_ARRAY_INCLUDED
#define ED_ARRAY_INCLUDED
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <memory.h>
#define array(T) struct T ## Array
#define array(T) struct T ## _Array
#define arrayTemplate(T) array(T) {\
size_t size, capacity;\
T *data;\
};\
array(T) T ## ArrayConstruct(size_t size) {\
array(T) T ## _ArrayConstruct(size_t size) {\
array(T) this;\
this.size = 0;\
this.capacity = size;\
@ -18,16 +20,61 @@ array(T) T ## ArrayConstruct(size_t size) {\
}\
return this;\
};\
void T ## PushArray(array(T) *arr, T value) {\
void T ## _PushArray(array(T) *arr, T value) {\
if (arr->size+1 <= arr->capacity) {\
arr->data[arr->size] = value;\
arr->size += 1;\
} else {\
fprintf(stderr, "failed to push to u8 array, size+num > capacity\n");\
}\
};\
void T ## _PushArrayMulti(array(T) *arr, T *values, size_t len) {\
for (size_t i = 0; i < len; ++i) {\
T ## _PushArray(arr, values[i]);\
}\
};\
void T ## _InsertArrayAt(array(T) *arr, size_t loc, T value) {\
if (arr->size == arr->capacity) {\
arr->capacity *= 2;\
void *new_data = realloc(arr->data, arr->capacity);\
if (new_data == NULL) {\
fprintf(stderr, "out of memory when reallocating array\n");\
}\
arr->data = new_data;\
}\
memcpy(&arr->data[loc+1], &arr->data[loc], arr->size * sizeof(T));\
arr->data[loc] = value;\
arr->size += 1;\
};
#define newArray(T, size) T ## ArrayConstruct(size)
#define pushArray(T, arr, value) T ## PushArray(arr, (value))
#define slice(T) struct T ## _Slice
#define sliceTemplate(T) slice(T) {\
size_t len;\
T *data;\
};\
array(T) T ## _FromSlice(slice(T) s) {\
array(T) arr = T ## _ArrayConstruct(s.len);\
memcpy(arr.data, s.data, sizeof(T) * s.len);\
arr.size = s.len;\
return arr;\
}\
slice(T) T ## _SliceConstruct(void *data, size_t len) {\
slice(T) this;\
this.len = len;\
this.data = data;\
return this;\
}
#define newArray(T, size) T ## _ArrayConstruct(size)
#define newArrayFromSlice(T, s) T ## _FromSlice(s)
#define pushArray(T, arr, value) T ## _PushArray(arr, (value))
#define pushArrayMulti(T, arr, values, len) T ## _PushArrayMulti(arr, values, len)
#define insertArrayAt(T, arr, loc, value) T ## _InsertArrayAt(arr, loc, (value))
#define newSlice(T, data, len) T ## _SliceConstruct(data, len)
arrayTemplate(uint8_t);
sliceTemplate(uint8_t);
#endif

138
src/file_io.h Normal file
View File

@ -0,0 +1,138 @@
#ifndef ED_FILE_IO_INCLUDED
#define ED_FILE_IO_INCLUDED
#include <stdint.h>
#include <stdbool.h>
#include "string.h"
uint64_t get_file_size(string file_path);
bool load_file(string file_path, size_t size, void *buffer);
#ifdef ED_FILE_IO_IMPLEMENTATION
#if defined(__unix__)
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
unsigned long get_file_size(const char *filePath) {
FILE *file = fopen(filePath, "r");
assert(file && "get_file_size: failed to open file");
fseek(file, 0, SEEK_END);
long fsize = ftell(file);
fseek(file, 0, SEEK_SET);
fclose(file);
return fsize;
}
bool load_file(const char *filePath, unsigned long size, void *buffer) {
FILE *file = fopen(filePath, "r");
assert(file && "load_file: failed to open file");
fseek(file, 0, SEEK_END);
long fsize = ftell(file);
fseek(file, 0, SEEK_SET);
fread(buffer, fsize, 1, file);
fclose(file);
return true;
}
#elif defined(_WIN32) || defined(WIN32)
#include <windows.h>
#define assertm(exp, msg) assert(((void)msg, exp))
VOID CALLBACK FileIOCompletionRoutine(
__in DWORD dwErrorCode,
__in DWORD dwNumberOfBytesTransfered,
__in LPOVERLAPPED lpOverlapped )
{
printf("Error code: %li", dwErrorCode);
printf("Number of bytes: %li", dwNumberOfBytesTransfered);
}
unsigned long get_file_size(const char *filePath) {
HANDLE hFile = CreateFile(filePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
LARGE_INTEGER size;
assert(GetFileSizeEx(hFile, &size) && "get_file_size: failed to get file size");
return size.LowPart;
}
bool load_file(const char *filePath, unsigned long size, void *buffer) {
HANDLE hFile = CreateFile(filePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
OVERLAPPED ol = {0};
unsigned long bytesRead = 0;
if (!ReadFile(hFile, buffer, size, &bytesRead, NULL)) {
unsigned long error = GetLastError();
unsigned long msg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, buffer, size, NULL);
printf("failed to load file: %s\n", (char *)buffer);
assert(false && "failed to read file");
}
assert(bytesRead == size && "load_file: didn't one-shot read the whole file");
return true;
}
#elif defined(__APPLE__)
#include <Foundation/Foundation.h>
uint64_t get_file_size(string file_path) {
NSFileManager* file_manager = [NSFileManager defaultManager];
NSString *path = [file_manager stringWithFileSystemRepresentation:(const char *)file_path.data length:file_path.len];
NSError *error = NULL;
NSDictionary<NSFileAttributeKey, id> *attributes = [file_manager attributesOfItemAtPath:path error:&error];
if (error) {
NSLog(@"error getting file attributes: %@\n", error.description);
return 0;
}
NSNumber *file_size = [attributes objectForKey:NSFileSize];
return file_size.unsignedLongLongValue;
}
bool load_file(string file_path, size_t size, void *buffer) {
NSFileManager* file_manager = [NSFileManager defaultManager];
NSString *path = [file_manager stringWithFileSystemRepresentation:(const char *)file_path.data length:file_path.len];
NSData *data = [file_manager contentsAtPath:path];
if (data == NULL) {
fprintf(stderr, "failed to load file: %.*s\n", (int)file_path.len, file_path.data);
return false;
}
if (data.length > size) {
fprintf(stderr, "buffer not large enough for file: %.*s\n", (int)file_path.len, file_path.data);
}
memcpy(buffer, data.bytes, data.length);
return true;
}
#endif
#endif
#endif

View File

@ -58,6 +58,9 @@ typedef struct {
uint32_t frame_width;
uint32_t frame_height;
_gfx_frame_func frame_func;
size_t num_glyphs;
size_t num_ui_rects;
} gfx_context_t;
static gfx_context_t _gfx_context;
@ -88,15 +91,13 @@ static void _metal_gfx_send_events(_metal_gfx_context *cx);
}
- (void)windowDidResize:(NSNotification *)notification {
NSLog(@"did resize\n");
_gfx_context.frame_width = _gfx_context.backend.window.contentView.frame.size.width;
_gfx_context.frame_height = _gfx_context.backend.window.contentView.frame.size.height;
CGFloat scale = _gfx_context.backend.metal_layer.contentsScale;
[_gfx_context.backend.metal_layer setDrawableSize:CGSizeMake(_gfx_context.frame_width * scale, _gfx_context.frame_height * scale)];
_metal_gfx_present(&_gfx_context.backend);
_gfx_context.backend.view.needsDisplay = true;
}
@end
@ -259,7 +260,6 @@ void _metal_gfx_send_events(_metal_gfx_context *cx) {
untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
[cx->application sendEvent:event];
[cx->application updateWindows];
}
void _metal_gfx_present(_metal_gfx_context *cx) {
@ -276,24 +276,28 @@ void _metal_gfx_present(_metal_gfx_context *cx) {
id<MTLRenderCommandEncoder> encoder = [command_buffer renderCommandEncoderWithDescriptor:render_pass_desc];
// UI Text
[encoder setRenderPipelineState:cx->pipelines.data[0]];
// FIXME: allow these to be described by the user instead of hardcoded
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
[encoder setVertexBuffer:cx->buffers.data[2] offset:0 atIndex:1]; // glyph data
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
[encoder setFragmentTexture:cx->textures.data[0] atIndex:0];
// TODO: get instance count properly
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:36];
if (_gfx_context.num_ui_rects > 0) {
// UI Rects
[encoder setRenderPipelineState:cx->pipelines.data[1]];
// FIXME: allow these to be described by the user instead of hardcoded
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
[encoder setVertexBuffer:cx->buffers.data[4] offset:0 atIndex:1]; // ui rects
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
// TODO: get instance count properly
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:_gfx_context.num_ui_rects];
}
// UI Rects
[encoder setRenderPipelineState:cx->pipelines.data[1]];
// FIXME: allow these to be described by the user instead of hardcoded
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
[encoder setVertexBuffer:cx->buffers.data[4] offset:0 atIndex:1]; // ui rects
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
// TODO: get instance count properly
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:3];
if (_gfx_context.num_glyphs > 0) {
// UI Text
[encoder setRenderPipelineState:cx->pipelines.data[0]];
// FIXME: allow these to be described by the user instead of hardcoded
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
[encoder setVertexBuffer:cx->buffers.data[2] offset:0 atIndex:1]; // glyph data
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
[encoder setFragmentTexture:cx->textures.data[0] atIndex:0];
// TODO: get instance count properly
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:_gfx_context.num_glyphs];
}
[encoder endEncoding];
[command_buffer presentDrawable:drawable];
@ -355,6 +359,8 @@ void * gfx_init_context(_gfx_frame_func frame_func, uint32_t width, uint32_t hei
_gfx_context.frame_func = frame_func;
_gfx_context.frame_width = width;
_gfx_context.frame_height = height;
_gfx_context.num_glyphs = 0;
_gfx_context.num_ui_rects = 0;
return &_gfx_context;
}

View File

@ -61,7 +61,10 @@ bool ht_set(ed_ht *ht, string key, void *value) {
for (size_t i=hash; i<ht->capacity; ++i) {
if (ht->key_slots[i].key.data == NULL || string_eq(ht->key_slots[i].key, key)) {
ht->key_slots[i].key = key;
if (ht->key_slots[i].key.data != NULL) {
free(ht->key_slots[i].key.data);
}
ht->key_slots[i].key = string_copy(key);
memcpy(ht->value_slots+i*ht->value_size, value, ht->value_size);
return true;

View File

@ -11,11 +11,15 @@
#define ED_STRING_IMPLEMENTATION
#define ED_HT_IMPLEMENTATION
#define ED_UI_IMPLEMENTATION
#define ED_BUFFER_IMPLEMENTATION
#define ED_FILE_IO_IMPLEMENTATION
#include "string.h"
#include "ht.h"
#include "ui.h"
#include "ed_array.h"
#include "gfx.h"
#include "buffer.h"
#include "file_io.h"
// static Arena default_arena = {0};
// static Arena temporary_arena = {0};
@ -56,6 +60,8 @@ static struct {
ui_context ui_cx;
gfx_context_t *gfx_cx;
ed_buffer test_buffer;
} state;
void queue_text(string text, float position[2]) {
@ -66,18 +72,18 @@ void queue_text(string text, float position[2]) {
glyph.position[0] = x+position[0];
glyph.position[1] = position[1];
x += glyph.size[0]/2;
x += glyph.size[0]/2+4;
pushArray(GpuGlyph, &state.gpu_glyphs, glyph);
}
}
}
void queue_ui_rect(uint32_t position[2], uint32_t size[2], uint32_t border_size) {
void queue_ui_rect(float position[2], float size[2], float border_size) {
GpuUiRect rect = (GpuUiRect) {
.position = { (float)position[0], (float)position[1] },
.size = { (float)size[0], (float)size[1] },
.border_size = { (float)border_size, (float)border_size },
.position = { position[0], position[1] },
.size = { size[0], size[1] },
.border_size = { border_size, border_size },
};
pushArray(GpuUiRect, &state.gpu_ui_rects, rect);
@ -101,22 +107,16 @@ void ed_init(_gfx_frame_func frame_func) {
gfx_push_vertex_buffer(state.gfx_cx, indices, sizeof(indices));
state.gpu_ui_rects = newArray(GpuUiRect, 2000);
state.gpu_glyphs = newArray(GpuGlyph, 1024);
state.gpu_glyphs = newArray(GpuGlyph, 2048);
state.glyph_cache = newArray(GpuGlyph, 97);
gfx_allocate_vertex_buffer(state.gfx_cx, state.gpu_glyphs.capacity * sizeof(GpuGlyph));
gfx_allocate_vertex_buffer(state.gfx_cx, sizeof(GpuUniformParams));
gfx_allocate_vertex_buffer(state.gfx_cx, state.gpu_ui_rects.capacity * sizeof(GpuUiRect));
uint8_t ttf_buffer[1<<20];
// TODO: grab default font from the system
FILE *ttf_file = fopen("./bin/JetBrainsMono-Medium.ttf", "rb");
if (!ttf_file) {
fprintf(stderr, "failed to load font\n");
exit(1);
}
assert(fread(ttf_buffer, 1, 1<<20, ttf_file));
fclose(ttf_file);
uint8_t ttf_buffer[1<<20];
assert("failed to load font" && load_file(_String("./bin/JetBrainsMono-Medium.ttf"), 1<<20, ttf_buffer));
stbtt_fontinfo font;
stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0));
@ -177,37 +177,48 @@ void ed_init(_gfx_frame_func frame_func) {
gfx_push_texture_buffer(state.gfx_cx, font_bitmap_size, font_bitmap_size, font_bitmap, font_bitmap_size*font_bitmap_size * sizeof(uint8_t));
state.test_buffer = ed_buffer_from_file(_String(""));
ed_buffer_insert_text(&state.test_buffer, _String("_hehe i'm inserting_"), (ed_buffer_cursor) { .offset = (ed_buffer_offset) { .link_index = 0, .content_index = 5 } });
}
void render_ui_text(string text, float position[2]) {
queue_text(text, position);
}
void render_ui_rect(float position[2], float size[2]) {
queue_ui_rect(position, size, 0);
}
void ed_frame() {
state.ui_cx.frame_elements.data[0].size.computed_size[0] = state.gfx_cx->frame_width;
state.ui_cx.frame_elements.data[0].size.computed_size[1] = state.gfx_cx->frame_height;
ui_element(&state.ui_cx, _String("Number 1"));
ui_element(&state.ui_cx, _String("ui element 2"));
ui_element(&state.ui_cx, _String("ui element 3"));
ui_element(&state.ui_cx, _String("Number 1"), UI_AXIS_VERTICAL, ui_make_size(ui_children_sum, ui_fill), 0);
ui_push_parent(&state.ui_cx);
{
ui_element(&state.ui_cx, _String("ui element 2"), UI_AXIS_HORIZONTAL, ui_make_size(ui_fit_text, ui_fill), UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_TEXT);
ui_element(&state.ui_cx, _String("ui element 2"), UI_AXIS_HORIZONTAL, ui_make_size(ui_fit_text, ui_fit_text), UI_FLAG_DRAW_BACKGROUND|UI_FLAG_DRAW_TEXT);
}
ui_pop_parent(&state.ui_cx);
// Test buffer
ed_buffer_render(&state.ui_cx, &state.test_buffer);
ui_compute_layout(&state.ui_cx, 0);
state.gpu_glyphs.size = 0;
state.gpu_ui_rects.size = 0;
for (size_t i = 1; i < state.ui_cx.frame_elements.size; ++i) {
string text = state.ui_cx.frame_elements.data[i].key;
ui_element_frame_data *elm = &state.ui_cx.frame_elements.data[i];
queue_text(text, (float[]){ (float)elm->size.computed_pos[0], (float)elm->size.computed_pos[1] });
fprintf(stderr, "size[0]: %d, size[1]: %d\n", elm->size.computed_size[0], elm->size.computed_size[1]);
queue_ui_rect(elm->size.computed_pos, elm->size.computed_size, 16);
}
ui_render(&state.ui_cx, render_ui_text, render_ui_rect);
ui_update_cache(&state.ui_cx, 0);
ui_prune(&state.ui_cx);
state.gfx_cx->num_glyphs = state.gpu_glyphs.size;
if (state.gpu_glyphs.size > 0) {
gfx_update_buffer(state.gfx_cx, 2, state.gpu_glyphs.data, state.gpu_glyphs.size * sizeof(GpuGlyph));
fprintf(stderr, "updated glyph buffer: %zu\n", state.gpu_glyphs.size);
}
state.gfx_cx->num_ui_rects = state.gpu_ui_rects.size;
if (state.gpu_ui_rects.size > 0) {
gfx_update_buffer(state.gfx_cx, 4, state.gpu_ui_rects.data, state.gpu_ui_rects.size * sizeof(GpuUiRect));
}

View File

@ -1,13 +1,17 @@
#ifndef ED_STRING_INCLUDED
#define ED_STRING_INCLUDED
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define _String(text) ((string) { .data = (uint8_t*) text, .len = sizeof(text) })
#define _String(text) ((string) { .data = (uint8_t*) text, .len = sizeof(text), .owned = false })
typedef struct {
uint8_t *data;
size_t len;
bool owned;
} string;
@ -22,5 +26,17 @@ bool string_eq(string a, string b) {
return true;
}
string string_copy(string a) {
string new_string;
new_string.data = malloc(a.len * sizeof(uint8_t));
new_string.len = a.len;
new_string.owned = true;
memcpy(new_string.data, a.data, new_string.len * sizeof(uint8_t));
return new_string;
}
#endif
#endif

View File

@ -52,6 +52,13 @@ typedef struct {
};
} ui_semantic_size;
#define ui_make_size(horizontal, vertical) ((ui_semantic_size[2]) { horizontal, vertical })
#define ui_fit_text ((ui_semantic_size) { .type = UI_SEMANTIC_SIZE_FIT_TEXT })
#define ui_fill ((ui_semantic_size) { .type = UI_SEMANTIC_SIZE_FILL })
#define ui_children_sum ((ui_semantic_size) { .type = UI_SEMANTIC_SIZE_CHILDREN_SUM })
#define ui_exact(value) ((ui_semantic_size) { .type = UI_SEMANTIC_SIZE_EXACT, .integer = value })
typedef struct {
ui_axis axis;
ui_semantic_size semantic_size[2];
@ -142,7 +149,19 @@ ui_context ui_init_context() {
};
}
size_t ui_element(ui_context *cx, string label) {
void ui_push_parent(ui_context *cx) {
if (cx->frame_elements.size > 0) {
cx->current_parent = cx->frame_elements.size-1;
}
}
void ui_pop_parent(ui_context *cx) {
if (_parent(cx->current_parent) < SIZE_MAX) {
cx->current_parent = _parent(cx->current_parent);
}
}
size_t ui_element(ui_context *cx, string label, ui_axis axis, ui_semantic_size size[2], ui_flags flags) {
ui_element_frame_data frame_data = (ui_element_frame_data) {
.index = cx->frame_elements.size,
// TODO: don't just set this to label, because then elements
@ -154,8 +173,10 @@ size_t ui_element(ui_context *cx, string label) {
.next = -1,
.prev = cx->frame_elements.data[cx->current_parent].last,
.parent = cx->current_parent,
.size.semantic_size[0].type = UI_SEMANTIC_SIZE_FILL,
.size.semantic_size[1].type = UI_SEMANTIC_SIZE_FIT_TEXT,
.size.axis = axis,
.size.semantic_size[0] = size[0],
.size.semantic_size[1] = size[1],
.flags = flags,
};
// Get cached element data
@ -163,13 +184,18 @@ size_t ui_element(ui_context *cx, string label) {
if (cache_data) {
cache_data->last_instantiated_index = cx->frame_index;
frame_data.size = cache_data->size;
frame_data.size.computed_pos[0] = cache_data->size.computed_pos[0];
frame_data.size.computed_pos[1] = cache_data->size.computed_pos[1];
frame_data.size.computed_size[0] = cache_data->size.computed_size[0];
frame_data.size.computed_size[1] = cache_data->size.computed_size[1];
} else {
assert("couldn't insert into ui element cache" && ht_set(&cx->cached_elements, label, &(ui_element_cache_data) {
bool did_insert = ht_set(&cx->cached_elements, label, &(ui_element_cache_data) {
.label = label,
.size = { 0 },
.last_instantiated_index = cx->frame_index,
}));
});
assert("couldn't insert into ui element cache" && did_insert);
}
pushArray(ui_element_frame_data, &cx->frame_elements, frame_data);
@ -386,6 +412,32 @@ void ui_update_cache(ui_context *cx, size_t element_index) {
}
}
typedef void (*_ui_render_text_func)(string text, float position[2]);
typedef void (*_ui_render_rect_func)(float position[2], float size[2]);
void ui_render(ui_context *cx, _ui_render_text_func text_func, _ui_render_rect_func rect_func) {
for (size_t i = 1; i < cx->frame_elements.size; ++i) {
string text = cx->frame_elements.data[i].key;
ui_element_frame_data *elm = &cx->frame_elements.data[i];
if (_flags(i, UI_FLAG_DRAW_TEXT)) {
text_func(text, (float[]){ (float)elm->size.computed_pos[0], (float)elm->size.computed_pos[1] });
}
if (_flags(i, UI_FLAG_DRAW_BACKGROUND)) {
rect_func(
(float[]){
(float)elm->size.computed_pos[0],
(float)elm->size.computed_pos[1]
},
(float[]){
(float)elm->size.computed_size[0],
(float)elm->size.computed_size[1]
}
);
}
}
}
void ui_prune(ui_context *cx) {
for (size_t i = 0; i < cx->cached_elements.capacity; ++i) {
if (cx->cached_elements.key_slots[i].key.data != NULL) {
@ -401,6 +453,14 @@ void ui_prune(ui_context *cx) {
}
}
size_t child_index = _elm(0)->first;
do {
__auto_type elm = _elm(child_index);
if (elm->label.owned) {
free(elm->label.data);
}
} while ((child_index = _next(child_index)) < SIZE_MAX);
cx->frame_index += 1;
cx->frame_elements.size = 1;
cx->frame_elements.data[0].first = SIZE_MAX;