mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-10 19:08:39 +00:00
Started with ui theme, ui layout and general ui rendering impl. Not working.
This commit is contained in:
parent
38f9178831
commit
208bff208f
|
|
@ -372,8 +372,6 @@ void thrd_ams_remove_asset(AssetManagementSystem* ams, const char* name, Asset*
|
|||
|
||||
Asset* ams_reserve_asset(AssetManagementSystem* ams, byte type, const char* name, uint32 size, uint32 overhead = 0)
|
||||
{
|
||||
ASSERT_SIMPLE(strlen(name) < HASH_MAP_MAX_KEY_LENGTH - 1);
|
||||
|
||||
AssetComponent* ac = &ams->asset_components[type];
|
||||
uint16 elements = ams_calculate_chunks(ac, size, overhead);
|
||||
|
||||
|
|
@ -430,8 +428,6 @@ Asset* thrd_ams_reserve_asset(AssetManagementSystem* ams, byte type, const char*
|
|||
|
||||
DEBUG_MEMORY_RESERVE((uintptr_t) asset_data, asset.ram_size, 180);
|
||||
|
||||
ASSERT_SIMPLE(strlen(name) < HASH_MAP_MAX_KEY_LENGTH - 1);
|
||||
|
||||
return (Asset *) hashmap_insert(&ams->hash_map, name, (byte *) &asset)->value;
|
||||
}
|
||||
|
||||
|
|
@ -464,6 +460,11 @@ void thrd_ams_update(AssetManagementSystem* ams, uint64 time, uint64 dt)
|
|||
++ams->asset_components[asset->component_id].asset_count;
|
||||
|
||||
if ((asset->state & ASSET_STATE_RAM_GC) || (asset->state & ASSET_STATE_VRAM_GC)) {
|
||||
// @todo Currently we cannot really delete based on last access since we are not updating the last_access reliably
|
||||
// This is usually the case for global/static assets such as font, ui_asset = ui vertices, ...
|
||||
// The reason for this is that we store a reference to those assets in a global struct
|
||||
// One solution could be to manually update the last_access at the end of every frame (shouldn't be THAT many assets)?
|
||||
// Maybe we can even implement a scene specific post_scene() function that does this for scene specific assets e.g. post_scene_scene1();
|
||||
if ((asset->state & ASSET_STATE_RAM_GC)
|
||||
&& (asset->state & ASSET_STATE_VRAM_GC)
|
||||
&& time - asset->last_access <= dt
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@
|
|||
* this AppCmdBuffer interacts with the individual systems and manually call those
|
||||
*/
|
||||
#include "AppCmdBuffer.h"
|
||||
#include "../camera/Camera.h"
|
||||
#include "../ui/UILayout.h"
|
||||
#include "../ui/UITheme.h"
|
||||
#include "../system/FileUtils.cpp"
|
||||
|
||||
// @todo Move the different functions to their own respective files (e.g. CmdAsset.cpp, CmdLayout.cpp)
|
||||
|
||||
inline
|
||||
void cmd_buffer_create(AppCmdBuffer* cb, BufferMemory* buf, int32 commands_count)
|
||||
|
|
@ -40,10 +46,32 @@ void cmd_asset_load_enqueue(AppCmdBuffer* cb, Command* cmd)
|
|||
queue_enqueue_wait_atomic(cb->assets_to_load, (byte *) cmd->data);
|
||||
}
|
||||
|
||||
// This doesn't load the file directly but tells (most likely) a worker thread to load a file
|
||||
static inline
|
||||
void cmd_file_load_enqueue(AppCmdBuffer* cb, Command* cmd)
|
||||
{
|
||||
// cmd->data structure:
|
||||
// start with a pointer to a callback function
|
||||
// file path
|
||||
queue_enqueue_wait_atomic(cb->files_to_load, (byte *) cmd->data);
|
||||
}
|
||||
|
||||
static inline
|
||||
void cmd_file_load(AppCmdBuffer* cb, Command* cmd)
|
||||
{
|
||||
FileBody file;
|
||||
file_read((const char *) cmd->data + sizeof(CommandFunction), &file, cb->thrd_mem_vol);
|
||||
|
||||
// WARNING: This is not the normal cmd.callback
|
||||
// This is a special callback part of the cmd data;
|
||||
CommandFunction callback = *((CommandFunction *) cmd->data);
|
||||
callback(&file);
|
||||
}
|
||||
|
||||
static inline
|
||||
void* cmd_func_run(AppCmdBuffer* cb, Command* cmd)
|
||||
{
|
||||
CommandFunc func = *((CommandFunc *) cmd->data);
|
||||
CommandFunction func = *((CommandFunction *) cmd->data);
|
||||
return func(cmd);
|
||||
}
|
||||
|
||||
|
|
@ -187,10 +215,10 @@ void thrd_cmd_insert(AppCmdBuffer* cb, CommandType type, const char* data)
|
|||
thrd_cmd_insert(cb, &cmd);
|
||||
}
|
||||
|
||||
inline void thrd_cmd_func_insert(AppCmdBuffer* cb, CommandType type, CommandFunc* func) {
|
||||
inline void thrd_cmd_func_insert(AppCmdBuffer* cb, CommandType type, CommandFunction* func) {
|
||||
Command cmd;
|
||||
cmd.type = CMD_FUNC_RUN;
|
||||
*((CommandFunc *) cmd.data) = *func;
|
||||
*((CommandFunction *) cmd.data) = *func;
|
||||
|
||||
thrd_cmd_insert(cb, &cmd);
|
||||
}
|
||||
|
|
@ -211,10 +239,10 @@ inline void thrd_cmd_audio_play(AppCmdBuffer* cb, const char* data) {
|
|||
thrd_cmd_insert(cb, &cmd);
|
||||
}
|
||||
|
||||
inline void thrd_cmd_func_run(AppCmdBuffer* cb, CommandFunc* func) {
|
||||
inline void thrd_cmd_func_run(AppCmdBuffer* cb, CommandFunction* func) {
|
||||
Command cmd;
|
||||
cmd.type = CMD_FUNC_RUN;
|
||||
*((CommandFunc *) cmd.data) = *func;
|
||||
*((CommandFunction *) cmd.data) = *func;
|
||||
|
||||
thrd_cmd_insert(cb, &cmd);
|
||||
}
|
||||
|
|
@ -251,13 +279,13 @@ inline void thrd_cmd_font_load(AppCmdBuffer* cb, const char* data) {
|
|||
thrd_cmd_insert(cb, &cmd);
|
||||
}
|
||||
|
||||
inline Asset* cmd_asset_load(AppCmdBuffer* cb, int32 asset_id)
|
||||
inline Asset* cmd_asset_load_sync(AppCmdBuffer* cb, int32 asset_id)
|
||||
{
|
||||
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||
return asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||
}
|
||||
|
||||
inline Asset* cmd_asset_load(AppCmdBuffer* cb, const char* asset_id_str)
|
||||
inline Asset* cmd_asset_load_sync(AppCmdBuffer* cb, const char* asset_id_str)
|
||||
{
|
||||
int32 asset_id = (int32) str_to_int(asset_id_str);
|
||||
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||
|
|
@ -309,11 +337,11 @@ inline Asset* cmd_audio_play(AppCmdBuffer* cb, const char* name) {
|
|||
return asset;
|
||||
}
|
||||
|
||||
inline void* cmd_func_run(AppCmdBuffer* cb, CommandFunc func) {
|
||||
inline void* cmd_func_run(AppCmdBuffer* cb, CommandFunction func) {
|
||||
return func(NULL);
|
||||
}
|
||||
|
||||
inline Asset* cmd_texture_load(AppCmdBuffer* cb, int32 asset_id) {
|
||||
inline Asset* cmd_texture_load_sync(AppCmdBuffer* cb, int32 asset_id) {
|
||||
// Check if asset already loaded
|
||||
char id_str[9];
|
||||
int_to_hex(asset_id, id_str);
|
||||
|
|
@ -339,7 +367,7 @@ inline Asset* cmd_texture_load(AppCmdBuffer* cb, int32 asset_id) {
|
|||
return asset;
|
||||
}
|
||||
|
||||
inline Asset* cmd_texture_load(AppCmdBuffer* cb, const char* name) {
|
||||
inline Asset* cmd_texture_load_sync(AppCmdBuffer* cb, const char* name) {
|
||||
// Check if asset already loaded
|
||||
Asset* asset = thrd_ams_get_asset_wait(cb->ams, name);
|
||||
|
||||
|
|
@ -363,7 +391,7 @@ inline Asset* cmd_texture_load(AppCmdBuffer* cb, const char* name) {
|
|||
return asset;
|
||||
}
|
||||
|
||||
inline Asset* cmd_font_load(AppCmdBuffer* cb, int32 asset_id) {
|
||||
inline Asset* cmd_font_load_sync(AppCmdBuffer* cb, int32 asset_id) {
|
||||
// Check if asset already loaded
|
||||
char id_str[9];
|
||||
int_to_hex(asset_id, id_str);
|
||||
|
|
@ -387,7 +415,7 @@ inline Asset* cmd_font_load(AppCmdBuffer* cb, int32 asset_id) {
|
|||
return asset;
|
||||
}
|
||||
|
||||
inline Asset* cmd_font_load(AppCmdBuffer* cb, const char* name) {
|
||||
inline Asset* cmd_font_load_sync(AppCmdBuffer* cb, const char* name) {
|
||||
// Check if asset already loaded
|
||||
Asset* asset = thrd_ams_get_asset_wait(cb->ams, name);
|
||||
|
||||
|
|
@ -409,10 +437,135 @@ inline Asset* cmd_font_load(AppCmdBuffer* cb, const char* name) {
|
|||
return asset;
|
||||
}
|
||||
|
||||
inline
|
||||
UILayout* cmd_layout_load_sync(
|
||||
AppCmdBuffer* cb,
|
||||
UILayout* layout, const char* layout_path
|
||||
) {
|
||||
FileBody layout_file;
|
||||
file_read(layout_path, &layout_file, cb->mem_vol);
|
||||
layout_from_data(layout_file.content, layout);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
inline
|
||||
UIThemeStyle* cmd_theme_load_sync(
|
||||
AppCmdBuffer* cb,
|
||||
UIThemeStyle* theme, const char* theme_path
|
||||
) {
|
||||
FileBody theme_file;
|
||||
file_read(theme_path, &theme_file, cb->mem_vol);
|
||||
theme_from_data(theme_file.content, theme);
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
inline
|
||||
void cmd_layout_populate_sync(
|
||||
AppCmdBuffer* cb,
|
||||
UILayout* layout, UIThemeStyle* theme,
|
||||
const Camera* camera
|
||||
) {
|
||||
layout_from_theme(layout, theme, camera);
|
||||
}
|
||||
|
||||
inline
|
||||
UILayout* cmd_ui_load_sync(
|
||||
AppCmdBuffer* cb,
|
||||
UILayout* layout, const char* layout_path,
|
||||
UIThemeStyle* general_theme,
|
||||
UIThemeStyle* theme, const char* theme_path,
|
||||
const Camera* camera
|
||||
) {
|
||||
cmd_layout_load_sync(cb, layout, layout_path);
|
||||
cmd_layout_populate_sync(cb, layout, general_theme, camera);
|
||||
|
||||
cmd_theme_load_sync(cb, theme, theme_path);
|
||||
cmd_layout_populate_sync(cb, layout, theme, camera);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
static inline
|
||||
UILayout* cmd_ui_load(AppCmdBuffer* cb, Command* cmd)
|
||||
{
|
||||
byte* pos = cmd->data;
|
||||
|
||||
UILayout* layout = (UILayout *) pos;
|
||||
pos += sizeof(uintptr_t);
|
||||
|
||||
char* layout_path = (char *) pos;
|
||||
str_move_to((char **) &pos, '\0'); ++pos;
|
||||
|
||||
UIThemeStyle* general_theme = (UIThemeStyle *) pos;
|
||||
pos += sizeof(uintptr_t);
|
||||
|
||||
UIThemeStyle* theme = (UIThemeStyle *) pos;
|
||||
pos += sizeof(uintptr_t);
|
||||
|
||||
char* theme_path = (char *) pos;
|
||||
str_move_to((char **) &pos, '\0'); ++pos;
|
||||
|
||||
Camera* camera = (Camera *) pos;
|
||||
|
||||
return cmd_ui_load_sync(
|
||||
cb,
|
||||
layout, layout_path,
|
||||
general_theme,
|
||||
theme, theme_path,
|
||||
camera
|
||||
);
|
||||
}
|
||||
|
||||
inline
|
||||
void thrd_cmd_ui_load(
|
||||
AppCmdBuffer* cb,
|
||||
UILayout* layout, const char* layout_path,
|
||||
UIThemeStyle* general_theme,
|
||||
UIThemeStyle* theme, const char* theme_path,
|
||||
const Camera* camera,
|
||||
CommandFunction callback
|
||||
) {
|
||||
Command cmd;
|
||||
cmd.type = CMD_UI_LOAD;
|
||||
cmd.callback = callback;
|
||||
byte* pos = cmd.data;
|
||||
|
||||
// Layout pointer
|
||||
*((uintptr_t *) pos) = (uintptr_t) layout;
|
||||
pos += sizeof(uintptr_t);
|
||||
|
||||
// Layout path
|
||||
pos += str_copy_until((char *) pos, layout_path, '\0');
|
||||
*pos = '\0'; ++pos;
|
||||
|
||||
// General theme pointer
|
||||
*((uintptr_t *) pos) = (uintptr_t) general_theme;
|
||||
pos += sizeof(uintptr_t);
|
||||
|
||||
// Theme pointer
|
||||
*((uintptr_t *) pos) = (uintptr_t) theme;
|
||||
pos += sizeof(uintptr_t);
|
||||
|
||||
// Theme path
|
||||
pos += str_copy_until((char *) pos, theme_path, '\0');
|
||||
*pos = '\0'; ++pos;
|
||||
|
||||
// Camera pointer
|
||||
*((uintptr_t *) pos) = (uintptr_t) camera;
|
||||
|
||||
thrd_cmd_insert(cb, &cmd);
|
||||
}
|
||||
|
||||
// @question In some cases we don't remove an element if it couldn't get completed
|
||||
// Would it make more sense to remove it and add a new follow up command automatically in such cases?
|
||||
// e.g. couldn't play audio since it isn't loaded -> queue for asset load -> queue for internal play
|
||||
// I gues this only makes sense if we would switch to a queue
|
||||
// I guess this only makes sense if we would switch to a queue
|
||||
// @question Some of the functions create another async call
|
||||
// E.g. do something that requires an asset, if asset not available queue for asset load.
|
||||
// Do we really want to do that or do we instead want to load the asset right then and there
|
||||
// If we do it right then and DON'T defer it, this would also solve the first question
|
||||
void cmd_iterate(AppCmdBuffer* cb)
|
||||
{
|
||||
int32 last_element = 0;
|
||||
|
|
@ -431,7 +584,9 @@ void cmd_iterate(AppCmdBuffer* cb)
|
|||
case CMD_ASSET_LOAD: {
|
||||
cmd_asset_load(cb, cmd);
|
||||
} break;
|
||||
case CMD_FILE_LOAD: {} break;
|
||||
case CMD_FILE_LOAD: {
|
||||
cmd_file_load(cb, cmd);
|
||||
} break;
|
||||
case CMD_TEXTURE_LOAD: {
|
||||
remove = cmd_texture_load_async(cb, cmd) != NULL;
|
||||
} break;
|
||||
|
|
@ -456,6 +611,9 @@ void cmd_iterate(AppCmdBuffer* cb)
|
|||
case CMD_SHADER_LOAD: {
|
||||
remove = cmd_shader_load(cb, cmd) != NULL;
|
||||
} break;
|
||||
case CMD_UI_LOAD: {
|
||||
remove = cmd_ui_load(cb, cmd) != NULL;
|
||||
} break;
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
@ -466,10 +624,14 @@ void cmd_iterate(AppCmdBuffer* cb)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (cmd->callback) {
|
||||
cmd->callback(cmd);
|
||||
}
|
||||
|
||||
chunk_free_element(&cb->commands, free_index, bit_index);
|
||||
|
||||
// @performance This adds some unnecessary overhead.
|
||||
// It would be much better, if we could define cb->last_element as the limit in the for loop
|
||||
// It would be better, if we could define cb->last_element as the limit in the for loop
|
||||
if (chunk_id == cb->last_element) {
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "../asset/AssetManagementSystem.h"
|
||||
#include "../object/Texture.h"
|
||||
#include "../memory/Queue.h"
|
||||
#include "../system/FileUtils.cpp"
|
||||
#include "Command.h"
|
||||
|
||||
struct AppCmdBuffer {
|
||||
|
|
@ -40,6 +41,7 @@ struct AppCmdBuffer {
|
|||
AssetManagementSystem* ams;
|
||||
AssetArchive* asset_archives;
|
||||
Queue* assets_to_load;
|
||||
Queue* files_to_load;
|
||||
AudioMixer* mixer;
|
||||
GpuApiType gpu_api;
|
||||
};
|
||||
|
|
@ -48,13 +50,13 @@ struct AppCmdBuffer {
|
|||
#include "../gpuapi/opengl/AppCmdBuffer.h"
|
||||
#elif VULKAN
|
||||
inline void* cmd_shader_load(AppCmdBuffer* cb, Command* cmd) { return NULL; }
|
||||
inline void* cmd_shader_load(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||
inline void* cmd_shader_load_sync(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||
#elif DIRECTX
|
||||
inline void* cmd_shader_load(AppCmdBuffer* cb, Command* cmd) { return NULL; }
|
||||
inline void* cmd_shader_load(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||
inline void* cmd_shader_load_sync(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||
#else
|
||||
inline void* cmd_shader_load(AppCmdBuffer* cb, Command* cmd) { return NULL; }
|
||||
inline void* cmd_shader_load(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||
inline void* cmd_shader_load_sync(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
// @question Consider to rename internal enum values to contain the word INTERNAL
|
||||
enum CommandType {
|
||||
CMD_FUNC_RUN,
|
||||
CMD_ASSET_ENQUEUE,
|
||||
|
|
@ -23,13 +24,15 @@ enum CommandType {
|
|||
CMD_AUDIO_PLAY,
|
||||
CMD_AUDIO_ENQUEUE, // Only for internal use
|
||||
CMD_SHADER_LOAD,
|
||||
CMD_UI_LOAD,
|
||||
};
|
||||
|
||||
typedef void* (*CommandFunction)(void* data);
|
||||
|
||||
struct Command {
|
||||
CommandType type;
|
||||
byte data[28]; // @todo to be adjusted
|
||||
CommandFunction callback;
|
||||
byte data[256]; // @todo to be adjusted
|
||||
};
|
||||
|
||||
typedef void* (*CommandFunc)(Command*);
|
||||
|
||||
#endif
|
||||
12
font/Font.h
12
font/Font.h
|
|
@ -116,21 +116,21 @@ void font_from_file_txt(
|
|||
++pos;
|
||||
}
|
||||
|
||||
if (strcmp(block_name, "texture") == 0) {
|
||||
if (str_compare(block_name, "texture") == 0) {
|
||||
while (*pos != '\n') {
|
||||
*texture_pos++ = *pos++;
|
||||
}
|
||||
|
||||
*texture_pos++ = '\0';
|
||||
} else if (strcmp(block_name, "font_size") == 0) {
|
||||
} else if (str_compare(block_name, "font_size") == 0) {
|
||||
font->size = strtof(pos, &pos);
|
||||
} else if (strcmp(block_name, "line_height") == 0) {
|
||||
} else if (str_compare(block_name, "line_height") == 0) {
|
||||
font->line_height = strtof(pos, &pos);
|
||||
} else if (strcmp(block_name, "image_width") == 0) {
|
||||
} else if (str_compare(block_name, "image_width") == 0) {
|
||||
image_width = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(block_name, "image_height") == 0) {
|
||||
} else if (str_compare(block_name, "image_height") == 0) {
|
||||
image_height = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(block_name, "glyph_count") == 0) {
|
||||
} else if (str_compare(block_name, "glyph_count") == 0) {
|
||||
// glyph_count has to be the last general element
|
||||
font->glyph_count = strtoul(pos, &pos, 10);
|
||||
start = false;
|
||||
|
|
|
|||
|
|
@ -442,223 +442,6 @@ v2_f32 vertex_text_create(
|
|||
return {rendered_width, rendered_height};
|
||||
}
|
||||
|
||||
// @todo implement shadow (offset + angle + diffuse) or should this be a shader only thing? if so this would be a problem for us since we are handling text in the same shader as simple shapes
|
||||
// we might want to implement distance field font atlas
|
||||
f32 ui_text_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
UITheme* theme, UIElement* element
|
||||
) {
|
||||
if (element->vertex_count > 0) {
|
||||
memcpy(vertices + *index, element->vertices, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
|
||||
return vertices[element->vertex_count - 1].position.x;
|
||||
}
|
||||
|
||||
// @performance see comment for setup_theme()
|
||||
|
||||
// Load element data
|
||||
HashEntryVoidP* element_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->primary_scene->hash_map, element->name, element->id);
|
||||
UIAttributeGroup* element_group = (UIAttributeGroup *) element_entry->value;
|
||||
|
||||
// Load general style
|
||||
UIAttribute* style = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_STYLE);
|
||||
HashEntryVoidP* style_entry = NULL;
|
||||
UIAttributeGroup* style_group = NULL;
|
||||
|
||||
if (style) {
|
||||
style_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->primary_scene->hash_map, style->value_str);
|
||||
style_group = (UIAttributeGroup *) style_entry->value;
|
||||
}
|
||||
|
||||
UIAttribute* x;
|
||||
UIAttribute* y;
|
||||
|
||||
// Load parent data (for position data)
|
||||
UIAttribute* parent = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_PARENT);
|
||||
if (parent) {
|
||||
HashEntryVoidP* parent_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->primary_scene->hash_map, parent->value_str);
|
||||
UIAttributeGroup* parent_group = (UIAttributeGroup *) parent_entry->value;
|
||||
|
||||
x = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
|
||||
// @question Do we have more values which can be inherited from the parent?
|
||||
// We don't want to inherit implicit stuff like size, background etc. These things should be defined explicitly
|
||||
} else {
|
||||
x = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
}
|
||||
|
||||
UIAttribute* width = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH);
|
||||
UIAttribute* height = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT);
|
||||
UIAttribute* align_h = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_H);
|
||||
UIAttribute* align_v = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_V);
|
||||
UIAttribute* text = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_CONTENT);
|
||||
UIAttribute* size = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_SIZE);
|
||||
UIAttribute* color_index = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_COLOR);
|
||||
|
||||
int32 length = utf8_strlen(text->value_str);
|
||||
bool is_ascii = strlen(text->value_str) == length;
|
||||
f32 scale = size->value_float / theme->font.size;
|
||||
|
||||
// If we do a different alignment we need to pre-calculate the width and height
|
||||
if (align_h != NULL || align_v != NULL) {
|
||||
f32 tmp_width = (f32) width->value_int;
|
||||
f32 tmp_height = (f32) height->value_int;
|
||||
|
||||
if (align_h != NULL && align_v != NULL) {
|
||||
text_calculate_dimensions(&tmp_width, &tmp_height, &theme->font, text->value_str, is_ascii, scale, length);
|
||||
} else if (align_h != NULL) {
|
||||
tmp_width = text_calculate_dimensions_width(&theme->font, text->value_str, is_ascii, scale, length);
|
||||
} else {
|
||||
tmp_height = text_calculate_dimensions_height(&theme->font, text->value_str, scale, length);
|
||||
}
|
||||
|
||||
if (align_h->value_int == UI_ALIGN_H_RIGHT) {
|
||||
x -= width->value_int;
|
||||
} else if (align_h->value_int == UI_ALIGN_H_CENTER) {
|
||||
x -= width->value_int / 2;
|
||||
}
|
||||
|
||||
if (align_v->value_int == UI_ALIGN_V_TOP) {
|
||||
y -= height->value_int;
|
||||
} else if (align_v->value_int == UI_ALIGN_V_CENTER) {
|
||||
y -= height->value_int / 2;
|
||||
}
|
||||
}
|
||||
|
||||
int32 start = *index;
|
||||
f32 offset_x = (f32) x->value_int;
|
||||
f32 offset_y = (f32) y->value_int;
|
||||
|
||||
int32 first_char = is_ascii ? text->value_str[0] : utf8_get_char_at(text->value_str, 0);
|
||||
for (int32 i = (first_char == '\n' ? 1 : 0); i < length; ++i) {
|
||||
int32 character = is_ascii ? text->value_str[i] : utf8_get_char_at(text->value_str, i);
|
||||
|
||||
if (character == '\n') {
|
||||
offset_y += theme->font.line_height * scale;
|
||||
offset_x = (f32) x->value_int;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Glyph* glyph = font_glyph_find(&theme->font, character);
|
||||
if (!glyph) {
|
||||
continue;
|
||||
}
|
||||
|
||||
f32 offset_y2 = offset_y + glyph->metrics.offset_y * scale;
|
||||
offset_x += glyph->metrics.offset_x * scale;
|
||||
|
||||
// @performance Consider to handle whitespaces just by offsetting
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
offset_x, offset_y2, glyph->metrics.width * scale, glyph->metrics.height * scale, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
color_index->value_float, glyph->coords.x1, glyph->coords.y1, glyph->coords.x2, glyph->coords.y2
|
||||
);
|
||||
|
||||
offset_x += (glyph->metrics.width + glyph->metrics.advance_x) * scale;
|
||||
}
|
||||
|
||||
element->vertex_count = *index - start;
|
||||
memcpy(element->vertices, vertices + start, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
|
||||
// @todo See todo of vertex_text function
|
||||
// @performance use elements->vertices and also cache result in there
|
||||
|
||||
return offset_x;
|
||||
}
|
||||
|
||||
void ui_button_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
UITheme* theme, UIElement* element
|
||||
)
|
||||
{
|
||||
// @todo handle different states and ongoing animations
|
||||
// We cannot return early in such cases
|
||||
if (element->vertex_count > 0) {
|
||||
memcpy(vertices + *index, element->vertices, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// @performance see comment for setup_theme()
|
||||
|
||||
// Load element data
|
||||
HashEntryVoidP* element_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->primary_scene->hash_map, element->name, element->id);
|
||||
UIAttributeGroup* element_group = (UIAttributeGroup *) element_entry->value;
|
||||
|
||||
// Load general style
|
||||
UIAttribute* style = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_STYLE);
|
||||
HashEntryVoidP* style_entry = NULL;
|
||||
UIAttributeGroup* style_group = NULL;
|
||||
|
||||
if (style) {
|
||||
style_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->primary_scene->hash_map, style->value_str);
|
||||
style_group = (UIAttributeGroup *) style_entry->value;
|
||||
}
|
||||
|
||||
UIAttribute* x;
|
||||
UIAttribute* y;
|
||||
|
||||
// Load parent data (for position data)
|
||||
UIAttribute* parent = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_PARENT);
|
||||
if (parent) {
|
||||
HashEntryVoidP* parent_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->primary_scene->hash_map, parent->value_str);
|
||||
UIAttributeGroup* parent_group = (UIAttributeGroup *) parent_entry->value;
|
||||
|
||||
x = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
|
||||
// @question Do we have more values which can be inherited from the parent?
|
||||
// We don't want to inherit implicit stuff like size, background etc. These things should be defined explicitly
|
||||
} else {
|
||||
x = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
}
|
||||
|
||||
UIAttribute* width = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH);
|
||||
UIAttribute* height = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT);
|
||||
UIAttribute* align_h = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_H);
|
||||
UIAttribute* align_v = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_V);
|
||||
UIAttribute* text = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_CONTENT);
|
||||
UIAttribute* size = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_SIZE);
|
||||
UIAttribute* color_index = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_COLOR);
|
||||
|
||||
// @todo Above we only handle the default values, what about state dependent values like hover, active?
|
||||
// Simply check the state here and load the child_entries based on the state
|
||||
// However, for that we need the current state of the button... should this be in a separate button object,
|
||||
// that also holds position information for hover checks etc. Or should that state be stored in the theme data?
|
||||
// Right now we could make these checks right here anyway but in the future we don't want to update the rendering data
|
||||
// every frame if we don't have to. We don't want immediate mode! We only want to update the UI if there is a change.
|
||||
// If a change (or state change like hover) triggers a complete update of all elements or just a sub region update
|
||||
// remains TBD
|
||||
|
||||
int32 start = *index;
|
||||
|
||||
vertex_rect_border_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float, y->value_float, width->value_float, height->value_float, 1, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
12, 0.0f, 0.0f
|
||||
);
|
||||
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float + 1, y->value_float + 1, width->value_float - 2, height->value_float - 2, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
14, 0.0f, 0.0f
|
||||
);
|
||||
|
||||
zindex = nextafterf(zindex, INFINITY);
|
||||
|
||||
vertex_text_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float, y->value_float, width->value_float, height->value_float, align_h->value_int, align_v->value_int,
|
||||
&theme->font, text->value_str, size->value_float, color_index->value_float
|
||||
);
|
||||
|
||||
element->vertex_count = *index - start;
|
||||
memcpy(element->vertices, vertices + start, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_world_space(f32* world_space, const f32* local_space, const f32* model_mat)
|
||||
{
|
||||
|
|
|
|||
219
gpuapi/UIUtils.h
219
gpuapi/UIUtils.h
|
|
@ -30,4 +30,223 @@ void ui_input_create(Vertex3DTextureColorIndex* __restrict vertices, uint32* __r
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
// @todo implement shadow (offset + angle + diffuse) or should this be a shader only thing? if so this would be a problem for us since we are handling text in the same shader as simple shapes
|
||||
// we might want to implement distance field font atlas
|
||||
f32 ui_text_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
UIThemeStyle* theme, UIElement* element
|
||||
) {
|
||||
if (element->vertex_count > 0) {
|
||||
memcpy(vertices + *index, element->vertices, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
|
||||
return vertices[element->vertex_count - 1].position.x;
|
||||
}
|
||||
|
||||
// @performance see comment for setup_theme()
|
||||
|
||||
// Load element data
|
||||
HashEntryVoidP* element_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, element->name, element->id);
|
||||
UIAttributeGroup* element_group = (UIAttributeGroup *) element_entry->value;
|
||||
|
||||
// Load general style
|
||||
UIAttribute* style = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_STYLE);
|
||||
HashEntryVoidP* style_entry = NULL;
|
||||
UIAttributeGroup* style_group = NULL;
|
||||
|
||||
if (style) {
|
||||
style_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, style->value_str);
|
||||
style_group = (UIAttributeGroup *) style_entry->value;
|
||||
}
|
||||
|
||||
UIAttribute* x;
|
||||
UIAttribute* y;
|
||||
|
||||
// Load parent data (for position data)
|
||||
UIAttribute* parent = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_PARENT);
|
||||
if (parent) {
|
||||
HashEntryVoidP* parent_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, parent->value_str);
|
||||
UIAttributeGroup* parent_group = (UIAttributeGroup *) parent_entry->value;
|
||||
|
||||
x = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
|
||||
// @question Do we have more values which can be inherited from the parent?
|
||||
// We don't want to inherit implicit stuff like size, background etc. These things should be defined explicitly
|
||||
} else {
|
||||
x = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
}
|
||||
|
||||
UIAttribute* width = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH);
|
||||
UIAttribute* height = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT);
|
||||
UIAttribute* align_h = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_H);
|
||||
UIAttribute* align_v = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_V);
|
||||
UIAttribute* text = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_CONTENT);
|
||||
UIAttribute* size = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_SIZE);
|
||||
UIAttribute* color_index = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_COLOR);
|
||||
|
||||
int32 length = utf8_strlen(text->value_str);
|
||||
bool is_ascii = strlen(text->value_str) == length;
|
||||
f32 scale = size->value_float / theme->font->size;
|
||||
|
||||
// If we do a different alignment we need to pre-calculate the width and height
|
||||
if (align_h != NULL || align_v != NULL) {
|
||||
f32 tmp_width = (f32) width->value_int;
|
||||
f32 tmp_height = (f32) height->value_int;
|
||||
|
||||
if (align_h != NULL && align_v != NULL) {
|
||||
text_calculate_dimensions(&tmp_width, &tmp_height, theme->font, text->value_str, is_ascii, scale, length);
|
||||
} else if (align_h != NULL) {
|
||||
tmp_width = text_calculate_dimensions_width(theme->font, text->value_str, is_ascii, scale, length);
|
||||
} else {
|
||||
tmp_height = text_calculate_dimensions_height(theme->font, text->value_str, scale, length);
|
||||
}
|
||||
|
||||
if (align_h->value_int == UI_ALIGN_H_RIGHT) {
|
||||
x -= width->value_int;
|
||||
} else if (align_h->value_int == UI_ALIGN_H_CENTER) {
|
||||
x -= width->value_int / 2;
|
||||
}
|
||||
|
||||
if (align_v->value_int == UI_ALIGN_V_TOP) {
|
||||
y -= height->value_int;
|
||||
} else if (align_v->value_int == UI_ALIGN_V_CENTER) {
|
||||
y -= height->value_int / 2;
|
||||
}
|
||||
}
|
||||
|
||||
int32 start = *index;
|
||||
f32 offset_x = (f32) x->value_int;
|
||||
f32 offset_y = (f32) y->value_int;
|
||||
|
||||
int32 first_char = is_ascii ? text->value_str[0] : utf8_get_char_at(text->value_str, 0);
|
||||
for (int32 i = (first_char == '\n' ? 1 : 0); i < length; ++i) {
|
||||
int32 character = is_ascii ? text->value_str[i] : utf8_get_char_at(text->value_str, i);
|
||||
|
||||
if (character == '\n') {
|
||||
offset_y += theme->font->line_height * scale;
|
||||
offset_x = (f32) x->value_int;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Glyph* glyph = font_glyph_find(theme->font, character);
|
||||
if (!glyph) {
|
||||
continue;
|
||||
}
|
||||
|
||||
f32 offset_y2 = offset_y + glyph->metrics.offset_y * scale;
|
||||
offset_x += glyph->metrics.offset_x * scale;
|
||||
|
||||
// @performance Consider to handle whitespaces just by offsetting
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
offset_x, offset_y2, glyph->metrics.width * scale, glyph->metrics.height * scale, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
color_index->value_float, glyph->coords.x1, glyph->coords.y1, glyph->coords.x2, glyph->coords.y2
|
||||
);
|
||||
|
||||
offset_x += (glyph->metrics.width + glyph->metrics.advance_x) * scale;
|
||||
}
|
||||
|
||||
element->vertex_count = *index - start;
|
||||
memcpy(element->vertices, vertices + start, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
|
||||
// @todo See todo of vertex_text function
|
||||
// @performance use elements->vertices and also cache result in there
|
||||
|
||||
return offset_x;
|
||||
}
|
||||
|
||||
void ui_button_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
UIThemeStyle* theme, UIElement* element
|
||||
)
|
||||
{
|
||||
// @todo handle different states and ongoing animations
|
||||
// We cannot return early in such cases
|
||||
if (element->vertex_count > 0) {
|
||||
memcpy(vertices + *index, element->vertices, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// @performance see comment for setup_theme()
|
||||
|
||||
// Load element data
|
||||
HashEntryVoidP* element_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, element->name, element->id);
|
||||
UIAttributeGroup* element_group = (UIAttributeGroup *) element_entry->value;
|
||||
|
||||
// Load general style
|
||||
UIAttribute* style = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_STYLE);
|
||||
HashEntryVoidP* style_entry = NULL;
|
||||
UIAttributeGroup* style_group = NULL;
|
||||
|
||||
if (style) {
|
||||
style_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, style->value_str);
|
||||
style_group = (UIAttributeGroup *) style_entry->value;
|
||||
}
|
||||
|
||||
UIAttribute* x;
|
||||
UIAttribute* y;
|
||||
|
||||
// Load parent data (for position data)
|
||||
UIAttribute* parent = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_PARENT);
|
||||
if (parent) {
|
||||
HashEntryVoidP* parent_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, parent->value_str);
|
||||
UIAttributeGroup* parent_group = (UIAttributeGroup *) parent_entry->value;
|
||||
|
||||
x = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
|
||||
// @question Do we have more values which can be inherited from the parent?
|
||||
// We don't want to inherit implicit stuff like size, background etc. These things should be defined explicitly
|
||||
} else {
|
||||
x = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
}
|
||||
|
||||
UIAttribute* width = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH);
|
||||
UIAttribute* height = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT);
|
||||
UIAttribute* align_h = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_H);
|
||||
UIAttribute* align_v = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_V);
|
||||
UIAttribute* text = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_CONTENT);
|
||||
UIAttribute* size = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_SIZE);
|
||||
UIAttribute* color_index = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_COLOR);
|
||||
|
||||
// @todo Above we only handle the default values, what about state dependent values like hover, active?
|
||||
// Simply check the state here and load the child_entries based on the state
|
||||
// However, for that we need the current state of the button... should this be in a separate button object,
|
||||
// that also holds position information for hover checks etc. Or should that state be stored in the theme data?
|
||||
// Right now we could make these checks right here anyway but in the future we don't want to update the rendering data
|
||||
// every frame if we don't have to. We don't want immediate mode! We only want to update the UI if there is a change.
|
||||
// If a change (or state change like hover) triggers a complete update of all elements or just a sub region update
|
||||
// remains TBD
|
||||
|
||||
int32 start = *index;
|
||||
|
||||
vertex_rect_border_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float, y->value_float, width->value_float, height->value_float, 1, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
12, 0.0f, 0.0f
|
||||
);
|
||||
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float + 1, y->value_float + 1, width->value_float - 2, height->value_float - 2, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
14, 0.0f, 0.0f
|
||||
);
|
||||
|
||||
zindex = nextafterf(zindex, INFINITY);
|
||||
|
||||
vertex_text_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float, y->value_float, width->value_float, height->value_float, align_h->value_int, align_v->value_int,
|
||||
theme->font, text->value_str, size->value_float, color_index->value_float
|
||||
);
|
||||
|
||||
element->vertex_count = *index - start;
|
||||
memcpy(element->vertices, vertices + start, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -20,7 +20,7 @@ void* cmd_shader_load(AppCmdBuffer* cb, Command* cmd) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void* cmd_shader_load(AppCmdBuffer* cb, Shader* shader, int32* shader_ids) {
|
||||
void* cmd_shader_load_sync(AppCmdBuffer* cb, Shader* shader, int32* shader_ids) {
|
||||
char asset_id[9];
|
||||
|
||||
int32 shader_assets[SHADER_TYPE_SIZE];
|
||||
|
|
|
|||
|
|
@ -740,7 +740,7 @@ bool gl_extensions_load()
|
|||
}
|
||||
|
||||
// umm count = end - pos;
|
||||
// OpenGL->SupportsSRGBFramebuffer = strcmp(count, pos, "WGL_EXT_framebuffer_sRGB") == 0 || strcmp(count, pos, "WGL_ARB_framebuffer_sRGB") == 0;
|
||||
// OpenGL->SupportsSRGBFramebuffer = str_compare(count, pos, "WGL_EXT_framebuffer_sRGB") == 0 || str_compare(count, pos, "WGL_ARB_framebuffer_sRGB") == 0;
|
||||
|
||||
pos = end;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@
|
|||
#include <vulkan/vulkan.h>
|
||||
#include <vector>
|
||||
#include "../../stdlib/Types.h"
|
||||
#include "ShaderUtils.h"
|
||||
#include "../../utils/StringUtils.h"
|
||||
#include "../../utils/TestUtils.h"
|
||||
#include "../../log/Log.h"
|
||||
#include "../../memory/RingMemory.h"
|
||||
#include "ShaderUtils.h"
|
||||
|
||||
PACKED_STRUCT;
|
||||
// WARNING: indices values start at one (are offset by +1) because 0 means no value in our implementation
|
||||
|
|
@ -65,7 +66,7 @@ int32 vulkan_check_validation_layer_support(const char** validation_layers, uint
|
|||
bool layerFound = false;
|
||||
|
||||
for (uint32 j = 0; j < layer_count; ++j) {
|
||||
if (strcmp(validation_layers[i], available_layers[j].layerName) == 0) {
|
||||
if (str_compare(validation_layers[i], available_layers[j].layerName) == 0) {
|
||||
layerFound = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -90,7 +91,7 @@ int32 vulkan_check_extension_support(const char** extensions, uint32 extension_c
|
|||
bool layerFound = false;
|
||||
|
||||
for (uint32 j = 0; j < ext_count; ++j) {
|
||||
if (strcmp(extensions[i], available_extensions[j].extensionName) == 0) {
|
||||
if (str_compare(extensions[i], available_extensions[j].extensionName) == 0) {
|
||||
layerFound = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -227,7 +228,7 @@ bool vulkan_device_supports_extensions(VkPhysicalDevice device, const char** dev
|
|||
for (int32 i = 0; i < device_extension_count; ++i) {
|
||||
bool found = false;
|
||||
for (int32 j = 0; j < extension_count; ++j) {
|
||||
if (strcmp(device_extensions[i], available_extensions[j].extensionName) == 0) {
|
||||
if (str_compare(device_extensions[i], available_extensions[j].extensionName) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
288
math/Evaluator.h
Normal file
288
math/Evaluator.h
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_MATH_EVALUATOR_H
|
||||
#define TOS_MATH_EVALUATOR_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../utils/StringUtils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define EVALUATOR_MAX_STACK_SIZE 256
|
||||
|
||||
// Stack for operators
|
||||
struct EvaluatorOperatorStack {
|
||||
char items[EVALUATOR_MAX_STACK_SIZE];
|
||||
int32 top;
|
||||
};
|
||||
|
||||
// Stack for values
|
||||
struct EvaluatorValueStack {
|
||||
f32 items[EVALUATOR_MAX_STACK_SIZE];
|
||||
int32 top;
|
||||
};
|
||||
|
||||
struct EvaluatorVariable {
|
||||
char name[32];
|
||||
f32 value;
|
||||
};
|
||||
|
||||
static inline
|
||||
const char* evaluator_skip_whitespace(const char* str) {
|
||||
while (*str && (*str == ' ' || *str == '\t')) {
|
||||
++str;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// Stack operations
|
||||
void evaluator_push_operator(EvaluatorOperatorStack* stack, char op) {
|
||||
if (stack->top >= EVALUATOR_MAX_STACK_SIZE - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack->items[++stack->top] = op;
|
||||
}
|
||||
|
||||
char evaluator_pop_operator(EvaluatorOperatorStack* stack) {
|
||||
ASSERT_SIMPLE(stack->top >= 0);
|
||||
|
||||
return stack->items[stack->top--];
|
||||
}
|
||||
|
||||
char evaluator_peek_operator(EvaluatorOperatorStack* stack) {
|
||||
if (stack->top < 0) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
return stack->items[stack->top];
|
||||
}
|
||||
|
||||
void evaluator_push_value(EvaluatorValueStack* stack, f32 value) {
|
||||
if (stack->top >= EVALUATOR_MAX_STACK_SIZE - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack->items[++stack->top] = value;
|
||||
}
|
||||
|
||||
f32 evaluator_pop_value(EvaluatorValueStack* stack) {
|
||||
ASSERT_SIMPLE(stack->top >= 0);
|
||||
|
||||
return stack->items[stack->top--];
|
||||
}
|
||||
|
||||
// Precedence of operators
|
||||
int32 evaluator_precedence(char op) {
|
||||
switch (op) {
|
||||
case '+':
|
||||
case '-':
|
||||
return 1;
|
||||
case '*':
|
||||
case '/':
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply an operator to two values
|
||||
f32 evaluator_apply_operator(char op, f32 a, f32 b) {
|
||||
switch (op) {
|
||||
case '+':
|
||||
return a + b;
|
||||
case '-':
|
||||
return a - b;
|
||||
case '*':
|
||||
return a * b;
|
||||
case '/':
|
||||
return a / b;
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f32 evaluator_evaluate_function(const char* name, const char* args);
|
||||
|
||||
// Shunting-yard algorithm to evaluate the expression
|
||||
f32 evaluator_evaluate_expression(const char* expr) {
|
||||
EvaluatorOperatorStack operators = { .top = -1 };
|
||||
EvaluatorValueStack values = { .top = -1 };
|
||||
|
||||
const char* ptr = expr;
|
||||
while (*ptr) {
|
||||
ptr = evaluator_skip_whitespace(ptr);
|
||||
|
||||
if (str_is_num(*ptr) || *ptr == '.') {
|
||||
// Parse number
|
||||
char* end;
|
||||
f32 value = strtof(ptr, &end);
|
||||
evaluator_push_value(&values, value);
|
||||
ptr = end;
|
||||
} else if (str_is_alpha(*ptr)) {
|
||||
// Parse function
|
||||
const char* start = ptr;
|
||||
while (str_is_alphanum(*ptr)) {
|
||||
++ptr;
|
||||
}
|
||||
|
||||
ASSERT_SIMPLE(*ptr == '(');
|
||||
|
||||
++ptr;
|
||||
const char* args_start = ptr;
|
||||
int32 depth = 1;
|
||||
while (*ptr && depth > 0) {
|
||||
if (*ptr == '(') {
|
||||
++depth;
|
||||
} else if (*ptr == ')') {
|
||||
--depth;
|
||||
}
|
||||
|
||||
++ptr;
|
||||
}
|
||||
|
||||
ASSERT_SIMPLE(depth == 0);
|
||||
|
||||
char name[32];
|
||||
memcpy(name, start, ptr - start);
|
||||
name[ptr - start] = '\0';
|
||||
|
||||
char args[128];
|
||||
memcpy(args, args_start, ptr - args_start - 1);
|
||||
args[ptr - args_start - 1] = '\0';
|
||||
|
||||
f32 result = evaluator_evaluate_function(name, args);
|
||||
evaluator_push_value(&values, result);
|
||||
} else if (*ptr == '(') {
|
||||
// Open parenthesis
|
||||
evaluator_push_operator(&operators, *ptr);
|
||||
++ptr;
|
||||
} else if (*ptr == ')') {
|
||||
// Close parenthesis
|
||||
while (evaluator_peek_operator(&operators) != '(') {
|
||||
char op = evaluator_pop_operator(&operators);
|
||||
f32 b = evaluator_pop_value(&values);
|
||||
f32 a = evaluator_pop_value(&values);
|
||||
evaluator_push_value(&values, evaluator_apply_operator(op, a, b));
|
||||
}
|
||||
|
||||
evaluator_pop_operator(&operators); // Remove '('
|
||||
++ptr;
|
||||
} else if (strchr("+-*/", *ptr)) {
|
||||
// Operator
|
||||
char op = *ptr;
|
||||
while (evaluator_precedence(evaluator_peek_operator(&operators)) >= evaluator_precedence(op)) {
|
||||
char top_op = evaluator_pop_operator(&operators);
|
||||
f32 b = evaluator_pop_value(&values);
|
||||
f32 a = evaluator_pop_value(&values);
|
||||
evaluator_push_value(&values, evaluator_apply_operator(top_op, a, b));
|
||||
}
|
||||
|
||||
evaluator_push_operator(&operators, op);
|
||||
++ptr;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
// Process remaining operators
|
||||
while (operators.top >= 0) {
|
||||
char op = evaluator_pop_operator(&operators);
|
||||
f32 b = evaluator_pop_value(&values);
|
||||
f32 a = evaluator_pop_value(&values);
|
||||
evaluator_push_value(&values, evaluator_apply_operator(op, a, b));
|
||||
}
|
||||
|
||||
return evaluator_pop_value(&values);
|
||||
}
|
||||
|
||||
// Evaluate built-in functions
|
||||
f32 evaluator_evaluate_function(const char* name, const char* args) {
|
||||
if (str_compare(name, "min") == 0) {
|
||||
const char* comma = strchr(args, ',');
|
||||
if (!comma) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
char arg1[64], arg2[64];
|
||||
memcpy(arg1, args, comma - args);
|
||||
arg1[comma - args] = '\0';
|
||||
str_copy_short(arg2, comma + 1);
|
||||
|
||||
f32 val1 = evaluator_evaluate_expression(arg1);
|
||||
f32 val2 = evaluator_evaluate_expression(arg2);
|
||||
|
||||
return OMS_MIN(val1, val2);
|
||||
} else if (str_compare(name, "max") == 0) {
|
||||
const char* comma = strchr(args, ',');
|
||||
if (!comma) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
char arg1[64], arg2[64];
|
||||
memcpy(arg1, args, comma - args);
|
||||
arg1[comma - args] = '\0';
|
||||
str_copy_short(arg2, comma + 1);
|
||||
|
||||
f32 val1 = evaluator_evaluate_expression(arg1);
|
||||
f32 val2 = evaluator_evaluate_expression(arg2);
|
||||
|
||||
return OMS_MAX(val1, val2);
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
f32 evaluator_evaluate(char* expr, int32 variable_count = 0, const EvaluatorVariable* variables = NULL) {
|
||||
// Handle variables
|
||||
const char* ptr = expr;
|
||||
int32 available_variables = variable_count;
|
||||
|
||||
while (*ptr && available_variables) {
|
||||
// Skip none-alpha values
|
||||
while (!str_is_alpha(*ptr) && *ptr != '\0') {
|
||||
++ptr;
|
||||
}
|
||||
|
||||
if (*ptr == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Potential variable name
|
||||
for (int32 i = 0; i < variable_count; ++i) {
|
||||
size_t len = strlen(variables[i].name);
|
||||
|
||||
// Check if string is variable (must be followed by a whitespace or end of string)
|
||||
if (str_compare(ptr, variables[i].name, len) == 0 && (ptr[len] == ' ' || ptr[len] == '\0')) {
|
||||
// Remove variable
|
||||
str_remove(expr, ptr - expr, len);
|
||||
|
||||
// Replace variable with value
|
||||
char value[25];
|
||||
int32 value_length = float_to_str(variables[i].value, value, 4);
|
||||
str_insert(expr, ptr - expr, value);
|
||||
|
||||
--available_variables;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Move past string, this should work regardless of whether we found a variable or not
|
||||
while (str_is_alpha(*ptr) && *ptr != '\0') {
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate math formula
|
||||
return evaluator_evaluate_expression(expr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -38,12 +38,12 @@ void module_file_parse(const char* path, Module* module, RingMemory* ring)
|
|||
strncpy_s(value, MAX_LENGTH, space + 1, MAX_LENGTH - 1);
|
||||
value[MAX_LENGTH - 1] = '\0';
|
||||
|
||||
if (strcmp(name, "name") == 0) {
|
||||
if (str_compare(name, "name") == 0) {
|
||||
strncpy_s(module->name, MAX_LENGTH, value, sizeof(module->name) - 1);
|
||||
module->name[strlen(value)] = '\0';
|
||||
} else if (strcmp(name, "version") == 0) {
|
||||
} else if (str_compare(name, "version") == 0) {
|
||||
module->version = (byte) atol(value);
|
||||
} else if (strcmp(name, "type") == 0) {
|
||||
} else if (str_compare(name, "type") == 0) {
|
||||
module->type = (ModuleType) atol(value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,8 +193,9 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
|||
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
// @performance Do we really need strncpy? Either use memcpy or strcpy?! Same goes for all the other cases below
|
||||
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
|
|
@ -219,7 +220,9 @@ void hashmap_insert(HashMap* hm, const char* key, int64 value) {
|
|||
HashEntryInt64* entry = (HashEntryInt64 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
|
|
@ -244,7 +247,9 @@ void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
|
|||
HashEntryUIntPtr* entry = (HashEntryUIntPtr *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
|
|
@ -269,7 +274,9 @@ void hashmap_insert(HashMap* hm, const char* key, void* value) {
|
|||
HashEntryVoidP* entry = (HashEntryVoidP *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
|
|
@ -294,7 +301,9 @@ void hashmap_insert(HashMap* hm, const char* key, f32 value) {
|
|||
HashEntryFloat* entry = (HashEntryFloat *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
|
|
@ -319,10 +328,12 @@ void hashmap_insert(HashMap* hm, const char* key, const char* value) {
|
|||
HashEntryStr* entry = (HashEntryStr *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
strncpy(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->value[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->next = NULL;
|
||||
|
|
@ -348,7 +359,9 @@ HashEntry* hashmap_insert(HashMap* hm, const char* key, byte* value) {
|
|||
|
||||
entry->value = (byte *) entry + sizeof(HashEntry);
|
||||
|
||||
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
memcpy(entry->value, value, hm->buf.chunk_size - sizeof(HashEntry));
|
||||
|
|
@ -378,7 +391,9 @@ HashEntry* hashmap_reserve(HashMap* hm, const char* key) {
|
|||
|
||||
entry->value = (byte *) entry + sizeof(HashEntry);
|
||||
|
||||
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->next = NULL;
|
||||
|
|
@ -422,7 +437,9 @@ HashEntry* hashmap_get_reserve(HashMap* hm, const char* key)
|
|||
|
||||
entry_new->value = (byte *) entry_new + sizeof(HashEntry);
|
||||
|
||||
strncpy(entry_new->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry_new->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry_new->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
if (entry) {
|
||||
|
|
@ -621,7 +638,7 @@ void hashmap_insert(HashMap* hm, int32 key, const char* value) {
|
|||
|
||||
entry->key = key;
|
||||
|
||||
strncpy(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||
str_copy_short(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->value[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->next = NULL;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
#include "../hash/GeneralHash.h"
|
||||
#include "../memory/RingMemory.h"
|
||||
|
||||
#define PERFECT_HASH_MAP_MAX_KEY_LENGTH 32
|
||||
typedef uint64 (*perfect_hash_function)(const char* key, int32 seed);
|
||||
|
||||
const perfect_hash_function PERFECT_HASH_FUNCTIONS[] = {
|
||||
|
|
@ -29,43 +28,43 @@ const perfect_hash_function PERFECT_HASH_FUNCTIONS[] = {
|
|||
|
||||
struct PerfectHashEntryInt32 {
|
||||
int64 element_id;
|
||||
char key[PERFECT_HASH_MAP_MAX_KEY_LENGTH];
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
int32 value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryInt64 {
|
||||
int64 element_id;
|
||||
char key[PERFECT_HASH_MAP_MAX_KEY_LENGTH];
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
int64 value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryUIntPtr {
|
||||
int64 element_id;
|
||||
char key[PERFECT_HASH_MAP_MAX_KEY_LENGTH];
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
uintptr_t value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryVoidP {
|
||||
int64 element_id;
|
||||
char key[PERFECT_HASH_MAP_MAX_KEY_LENGTH];
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
void* value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryFloat {
|
||||
int64 element_id;
|
||||
char key[PERFECT_HASH_MAP_MAX_KEY_LENGTH];
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
f32 value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryStr {
|
||||
int64 element_id;
|
||||
char key[PERFECT_HASH_MAP_MAX_KEY_LENGTH];
|
||||
char value[PERFECT_HASH_MAP_MAX_KEY_LENGTH];
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
char value[HASH_MAP_MAX_KEY_LENGTH];
|
||||
};
|
||||
|
||||
struct PerfectHashEntry {
|
||||
int64 element_id;
|
||||
char key[PERFECT_HASH_MAP_MAX_KEY_LENGTH];
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
byte* value;
|
||||
};
|
||||
|
||||
|
|
@ -244,7 +243,7 @@ void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, const char* val
|
|||
PerfectHashEntryStr* entry = (PerfectHashEntryStr *) (hm->hash_entries + hm->entry_size * index);
|
||||
entry->element_id = index;
|
||||
str_copy_short(entry->key, key);
|
||||
memcpy(entry->value, value, PERFECT_HASH_MAP_MAX_KEY_LENGTH);
|
||||
memcpy(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
|
|||
|
|
@ -153,6 +153,24 @@ struct v4_byte {
|
|||
};
|
||||
};
|
||||
|
||||
struct v4_int16 {
|
||||
union {
|
||||
struct {
|
||||
int16 x, y;
|
||||
|
||||
union {
|
||||
int16 z, width;
|
||||
};
|
||||
|
||||
union {
|
||||
int16 w, height;
|
||||
};
|
||||
};
|
||||
|
||||
int16 v[4];
|
||||
};
|
||||
};
|
||||
|
||||
struct v2_int32 {
|
||||
union {
|
||||
struct {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ struct PoolWorker {
|
|||
void* result;
|
||||
RingMemory ring;
|
||||
ThreadPoolJobFunc func;
|
||||
ThreadPoolJobFunc callback;
|
||||
};
|
||||
|
||||
struct Worker {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
#ifndef TOS_UI_ALIGNMENT_H
|
||||
#define TOS_UI_ALIGNMENT_H
|
||||
|
||||
enum UIAlignH {
|
||||
UI_ALIGN_H_LEFT,
|
||||
UI_ALIGN_H_CENTER,
|
||||
UI_ALIGN_H_RIGHT,
|
||||
};
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
enum UIAlignV {
|
||||
UI_ALIGN_V_BOTTOM,
|
||||
UI_ALIGN_V_CENTER,
|
||||
UI_ALIGN_V_TOP,
|
||||
enum UIAlign : byte {
|
||||
UI_ALIGN_H_LEFT = 1 << 0,
|
||||
UI_ALIGN_H_CENTER = 1 << 1,
|
||||
UI_ALIGN_H_RIGHT = 1 << 2,
|
||||
|
||||
UI_ALIGN_V_BOTTOM = 1 << 3,
|
||||
UI_ALIGN_V_CENTER = 1 << 4,
|
||||
UI_ALIGN_V_TOP = 1 << 5,
|
||||
};
|
||||
|
||||
#endif
|
||||
23
ui/UIAnimation.h
Normal file
23
ui/UIAnimation.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef TOS_UI_ANIMATION_H
|
||||
#define TOS_UI_ANIMATION_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../animation/AnimationEaseType.h"
|
||||
|
||||
#define UI_ANIMATION_ACTIVE_FLAG 0x80000000
|
||||
|
||||
struct UIAnimationState {
|
||||
uint64 start;
|
||||
uint32 duration;
|
||||
// Element specific use (e.g. cursor uses 0/1 for visible/invisible for blinking)
|
||||
// The highest bit indicates if the animation is active or not
|
||||
int32 state;
|
||||
AnimationEaseType anim_type;
|
||||
};
|
||||
|
||||
struct UIAnimation {
|
||||
uint32 duration;
|
||||
AnimationEaseType anim_type;
|
||||
};
|
||||
|
||||
#endif
|
||||
216
ui/UIAttribute.h
216
ui/UIAttribute.h
|
|
@ -1,216 +0,0 @@
|
|||
#ifndef TOS_UI_STYLE_H
|
||||
#define TOS_UI_STYLE_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UIAttribute {
|
||||
// Attributes use ids (=type name) instead of strings
|
||||
int32 attribute_id;
|
||||
|
||||
union {
|
||||
char value_str[32];
|
||||
int32 value_int;
|
||||
f32 value_float;
|
||||
v4_f32 value_v4_f32;
|
||||
};
|
||||
};
|
||||
|
||||
struct UIAttributeGroup {
|
||||
int32 attribute_size;
|
||||
UIAttribute* attributes;
|
||||
};
|
||||
|
||||
enum UIAttributeType {
|
||||
UI_ATTRIBUTE_TYPE_TYPE,
|
||||
UI_ATTRIBUTE_TYPE_STYLE,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_X,
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_Y,
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_CONTENT,
|
||||
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_H,
|
||||
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_V,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_FONT_NAME,
|
||||
UI_ATTRIBUTE_TYPE_FONT_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_FONT_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_FONT_SIZE,
|
||||
UI_ATTRIBUTE_TYPE_FONT_WEIGHT,
|
||||
UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_ALIGN_H,
|
||||
UI_ATTRIBUTE_TYPE_ALIGN_V,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_ZINDEX,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_POSITION_X,
|
||||
UI_ATTRIBUTE_TYPE_POSITION_Y,
|
||||
UI_ATTRIBUTE_TYPE_PARENT,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_BORDER_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_PADDING,
|
||||
UI_ATTRIBUTE_TYPE_PADDING_TOP,
|
||||
UI_ATTRIBUTE_TYPE_PADDING_RIGHT,
|
||||
UI_ATTRIBUTE_TYPE_PADDING_BOTTOM,
|
||||
UI_ATTRIBUTE_TYPE_PADDING_LEFT,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE,
|
||||
|
||||
// @todo This isn't enough, we can have many animations (position, size, colors, ...)
|
||||
// Maybe we need to define an animation child which overwrites the defined values
|
||||
// Maybe it should use the same system as state dependent values like hover, active, ...
|
||||
UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION,
|
||||
UI_ATTRIBUTE_TYPE_TRANSITION_DURATION,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_SIZE,
|
||||
};
|
||||
|
||||
UIAttribute* ui_attribute_from_group(UIAttributeGroup* group, UIAttributeType type)
|
||||
{
|
||||
if (!group->attributes) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32 left = 0;
|
||||
int32 right = type;
|
||||
|
||||
// Binary search since attributes are sorted by attribute_id
|
||||
while (left <= right) {
|
||||
int32 mid = left + (right - left) / 2;
|
||||
|
||||
if (group->attributes[mid].attribute_id == type) {
|
||||
return &group->attributes[mid];
|
||||
} else if (group->attributes[mid].attribute_id < type) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
constexpr const char* ui_attribute_type_to_string(UIAttributeType e)
|
||||
{
|
||||
switch (e) {
|
||||
case UI_ATTRIBUTE_TYPE_TYPE:
|
||||
return "type";
|
||||
case UI_ATTRIBUTE_TYPE_STYLE:
|
||||
return "style";
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_X:
|
||||
return "x";
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_Y:
|
||||
return "y";
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH:
|
||||
return "width";
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT:
|
||||
return "height";
|
||||
case UI_ATTRIBUTE_TYPE_FONT_NAME:
|
||||
return "font_name";
|
||||
case UI_ATTRIBUTE_TYPE_FONT_COLOR:
|
||||
return "font_color";
|
||||
case UI_ATTRIBUTE_TYPE_FONT_SIZE:
|
||||
return "font_size";
|
||||
case UI_ATTRIBUTE_TYPE_FONT_WEIGHT:
|
||||
return "font_weight";
|
||||
case UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT:
|
||||
return "font_line_height";
|
||||
case UI_ATTRIBUTE_TYPE_ALIGN_H:
|
||||
return "align_h";
|
||||
case UI_ATTRIBUTE_TYPE_ALIGN_V:
|
||||
return "align_v";
|
||||
case UI_ATTRIBUTE_TYPE_ZINDEX:
|
||||
return "zindex";
|
||||
case UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR:
|
||||
return "background_color";
|
||||
case UI_ATTRIBUTE_TYPE_BACKGROUND_IMG:
|
||||
return "background_img";
|
||||
case UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY:
|
||||
return "background_img_opacity";
|
||||
case UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V:
|
||||
return "background_img_position_v";
|
||||
case UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H:
|
||||
return "background_img_position_h";
|
||||
case UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE:
|
||||
return "background_img_style";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_COLOR:
|
||||
return "border_color";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_WIDTH:
|
||||
return "border_width";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR:
|
||||
return "border_top_color";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH:
|
||||
return "border_top_width";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR:
|
||||
return "border_right_color";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH:
|
||||
return "border_right_width";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR:
|
||||
return "border_bottom_color";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH:
|
||||
return "border_bottom_width";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR:
|
||||
return "border_left_color";
|
||||
case UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH:
|
||||
return "border_left_width";
|
||||
case UI_ATTRIBUTE_TYPE_PADDING:
|
||||
return "padding";
|
||||
case UI_ATTRIBUTE_TYPE_PADDING_TOP:
|
||||
return "padding_top";
|
||||
case UI_ATTRIBUTE_TYPE_PADDING_RIGHT:
|
||||
return "padding_right";
|
||||
case UI_ATTRIBUTE_TYPE_PADDING_BOTTOM:
|
||||
return "padding_bottom";
|
||||
case UI_ATTRIBUTE_TYPE_PADDING_LEFT:
|
||||
return "padding_left";
|
||||
case UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR:
|
||||
return "shadow_inner_color";
|
||||
case UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE:
|
||||
return "shadow_inner_angle";
|
||||
case UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE:
|
||||
return "shadow_inner_distance";
|
||||
case UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR:
|
||||
return "shadow_outer_color";
|
||||
case UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE:
|
||||
return "shadow_outer_angle";
|
||||
case UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE:
|
||||
return "shadow_outer_distance";
|
||||
case UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION:
|
||||
return "transition_animation";
|
||||
case UI_ATTRIBUTE_TYPE_TRANSITION_DURATION:
|
||||
return "transition_duration";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
13
ui/UIButton.h
Normal file
13
ui/UIButton.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_BUTTON_H
|
||||
#define TOS_UI_BUTTON_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UIButtonState {
|
||||
};
|
||||
|
||||
struct UIButton {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
13
ui/UICursor.h
Normal file
13
ui/UICursor.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_CURSOR_H
|
||||
#define TOS_UI_CURSOR_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UICursorState {
|
||||
};
|
||||
|
||||
struct UICursor {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -2,51 +2,44 @@
|
|||
#define TOS_UI_ELEMENT_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "UIElementType.h"
|
||||
#include "../object/Vertex.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
struct UIElementDimension {
|
||||
int16 x1;
|
||||
int16 y1;
|
||||
int16 x2;
|
||||
int16 y2;
|
||||
enum UIElementType : byte {
|
||||
UI_ELEMENT_TYPE_BUTTON,
|
||||
UI_ELEMENT_TYPE_SELECT,
|
||||
UI_ELEMENT_TYPE_INPUT,
|
||||
UI_ELEMENT_TYPE_TEXTAREA,
|
||||
UI_ELEMENT_TYPE_IMAGE,
|
||||
UI_ELEMENT_TYPE_TEXT,
|
||||
UI_ELEMENT_TYPE_LINK,
|
||||
UI_ELEMENT_TYPE_TABLE,
|
||||
UI_ELEMENT_TYPE_VIEW_WINDOW,
|
||||
UI_ELEMENT_TYPE_VIEW_PANEL,
|
||||
UI_ELEMENT_TYPE_VIEW_TAB,
|
||||
UI_ELEMENT_TYPE_CURSOR,
|
||||
UI_ELEMENT_TYPE_SIZE,
|
||||
};
|
||||
|
||||
#define UI_ELEMENT_STATE_VISIBLE 1
|
||||
#define UI_ELEMENT_STATE_ACTIVE 2
|
||||
#define UI_ELEMENT_STATE_FOCUSED 4
|
||||
#define UI_ELEMENT_STATE_CLICKED 8
|
||||
#define UI_ELEMENT_STATE_ANIMATION 16
|
||||
enum UIElementState : byte {
|
||||
UI_ELEMENT_STATE_ACTIVE = 1 << 0,
|
||||
UI_ELEMENT_STATE_VISIBLE = 1 << 1,
|
||||
UI_ELEMENT_STATE_FOCUSED = 1 << 2,
|
||||
UI_ELEMENT_STATE_CLICKABLE = 1 << 3,
|
||||
UI_ELEMENT_STATE_ANIMATION = 1 << 4,
|
||||
};
|
||||
|
||||
struct UIElement {
|
||||
const char* name;
|
||||
int32 id;
|
||||
// @see UIElementState
|
||||
byte state_flag;
|
||||
UIElementType type;
|
||||
bool is_dynamic;
|
||||
|
||||
int16 window_id;
|
||||
int16 panel_id;
|
||||
UIStyleType style_old;
|
||||
UIStyleType style_new;
|
||||
UIElement* parent;
|
||||
void* state;
|
||||
void* details[UI_STYLE_TYPE_SIZE];
|
||||
|
||||
UIElementDimension dimension;
|
||||
|
||||
int32 state_flag;
|
||||
|
||||
f32 anim_elapsed;
|
||||
|
||||
int16 scroll_x;
|
||||
int16 scroll_y;
|
||||
|
||||
// @todo animation state
|
||||
|
||||
// @todo cache vertex result for default, hover etc.
|
||||
int32 vertex_count;
|
||||
Vertex3DTextureColorIndex* vertices; // WARNING: This is not the official holder of the memory, its in UILayout
|
||||
|
||||
// @todo We could even have a pointer that points into the complete ui array making it possible to simply replace this section
|
||||
// This is something we wanted to do anyway when updating sub regions on the gpu memory
|
||||
uint16 children_count;
|
||||
UIElement** children;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -2,55 +2,116 @@
|
|||
#define TOS_UI_ELEMENT_TYPE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../stdlib/Types.h"
|
||||
#include "UIButton.h"
|
||||
#include "UISelect.h"
|
||||
#include "UIInput.h"
|
||||
#include "UIText.h"
|
||||
#include "UITextarea.h"
|
||||
#include "UIImage.h"
|
||||
#include "UILink.h"
|
||||
#include "UIWindow.h"
|
||||
#include "UITable.h"
|
||||
#include "UIPanel.h"
|
||||
#include "UITab.h"
|
||||
#include "UICursor.h"
|
||||
|
||||
enum UIElementType {
|
||||
UI_ELEMENT_TYPE_BUTTON,
|
||||
UI_ELEMENT_TYPE_SELECT,
|
||||
UI_ELEMENT_TYPE_DROPDOWN,
|
||||
UI_ELEMENT_TYPE_TEXTFIELD,
|
||||
UI_ELEMENT_TYPE_TEXTAREA,
|
||||
UI_ELEMENT_TYPE_IMAGE,
|
||||
UI_ELEMENT_TYPE_TEXT,
|
||||
UI_ELEMENT_TYPE_LINK,
|
||||
UI_ELEMENT_TYPE_TABLE,
|
||||
|
||||
UI_ELEMENT_TYPE_VIEW_WINDOW,
|
||||
UI_ELEMENT_TYPE_VIEW_PANEL,
|
||||
UI_ELEMENT_TYPE_VIEW_TAB,
|
||||
|
||||
UI_ELEMENT_TYPE_SIZE,
|
||||
};
|
||||
|
||||
constexpr const char* ui_element_type_to_string(UIElementType e)
|
||||
constexpr
|
||||
int32 ui_element_type_size(UIElementType e)
|
||||
{
|
||||
switch (e) {
|
||||
case UI_ELEMENT_TYPE_BUTTON:
|
||||
return "button";
|
||||
return sizeof(UIButton);
|
||||
case UI_ELEMENT_TYPE_SELECT:
|
||||
return "select";
|
||||
case UI_ELEMENT_TYPE_DROPDOWN:
|
||||
return "dropdown";
|
||||
case UI_ELEMENT_TYPE_TEXTFIELD:
|
||||
return "textfield";
|
||||
case UI_ELEMENT_TYPE_TEXTAREA:
|
||||
return "textarea";
|
||||
case UI_ELEMENT_TYPE_IMAGE:
|
||||
return "image";
|
||||
return sizeof(UISelect);
|
||||
case UI_ELEMENT_TYPE_INPUT:
|
||||
return sizeof(UIInput);
|
||||
case UI_ELEMENT_TYPE_TEXT:
|
||||
return "text";
|
||||
return sizeof(UIText);
|
||||
case UI_ELEMENT_TYPE_TEXTAREA:
|
||||
return sizeof(UITextarea);
|
||||
case UI_ELEMENT_TYPE_IMAGE:
|
||||
return sizeof(UIImage);
|
||||
case UI_ELEMENT_TYPE_LINK:
|
||||
return "link";
|
||||
return sizeof(UILink);
|
||||
case UI_ELEMENT_TYPE_TABLE:
|
||||
return "table";
|
||||
return sizeof(UITable);
|
||||
case UI_ELEMENT_TYPE_VIEW_WINDOW:
|
||||
return "view_window";
|
||||
return sizeof(UIWindow);
|
||||
case UI_ELEMENT_TYPE_VIEW_PANEL:
|
||||
return "view_panel";
|
||||
return sizeof(UIPanel);
|
||||
case UI_ELEMENT_TYPE_VIEW_TAB:
|
||||
return "view_tab";
|
||||
return sizeof(UITab);
|
||||
case UI_ELEMENT_TYPE_CURSOR:
|
||||
return sizeof(UICursor);
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr
|
||||
int32 ui_element_state_size(UIElementType e)
|
||||
{
|
||||
switch (e) {
|
||||
case UI_ELEMENT_TYPE_BUTTON:
|
||||
return sizeof(UIButtonState);
|
||||
case UI_ELEMENT_TYPE_SELECT:
|
||||
return sizeof(UISelectState);
|
||||
case UI_ELEMENT_TYPE_INPUT:
|
||||
return sizeof(UIInputState);
|
||||
case UI_ELEMENT_TYPE_TEXT:
|
||||
return sizeof(UITextState);
|
||||
case UI_ELEMENT_TYPE_TEXTAREA:
|
||||
return sizeof(UITextareaState);
|
||||
case UI_ELEMENT_TYPE_IMAGE:
|
||||
return sizeof(UIImageState);
|
||||
case UI_ELEMENT_TYPE_LINK:
|
||||
return sizeof(UILinkState);
|
||||
case UI_ELEMENT_TYPE_TABLE:
|
||||
return sizeof(UITableState);
|
||||
case UI_ELEMENT_TYPE_VIEW_WINDOW:
|
||||
return sizeof(UIWindowState);
|
||||
case UI_ELEMENT_TYPE_VIEW_PANEL:
|
||||
return sizeof(UIPanelState);
|
||||
case UI_ELEMENT_TYPE_VIEW_TAB:
|
||||
return sizeof(UITabState);
|
||||
case UI_ELEMENT_TYPE_CURSOR:
|
||||
return sizeof(UICursorState);
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr
|
||||
int32 ui_element_type_to_id(const char* str)
|
||||
{
|
||||
if (str_compare("button", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_BUTTON;
|
||||
} else if (str_compare("select", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_SELECT;
|
||||
} else if (str_compare("input", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_INPUT;
|
||||
} else if (str_compare("textarea", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_TEXTAREA;
|
||||
} else if (str_compare("image", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_IMAGE;
|
||||
} else if (str_compare("text", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_TEXT;
|
||||
} else if (str_compare("link", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_LINK;
|
||||
} else if (str_compare("table", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_TABLE;
|
||||
} else if (str_compare("view_window", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_VIEW_WINDOW;
|
||||
} else if (str_compare("view_panel", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_VIEW_PANEL;
|
||||
} else if (str_compare("view_tab", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_VIEW_TAB;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
13
ui/UIImage.h
Normal file
13
ui/UIImage.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_IMAGE_H
|
||||
#define TOS_UI_IMAGE_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UIImageState {
|
||||
};
|
||||
|
||||
struct UIImage {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
103
ui/UIInput.h
Normal file
103
ui/UIInput.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#ifndef TOS_UI_INPUT_H
|
||||
#define TOS_UI_INPUT_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../camera/Camera.h"
|
||||
#include "attribute/UIAttributeBorder.h"
|
||||
#include "attribute/UIAttributeShadow.h"
|
||||
#include "attribute/UIAttributeFont.h"
|
||||
#include "attribute/UIAttributeBackground.h"
|
||||
#include "attribute/UIAttributeDimension.h"
|
||||
#include "UIAnimation.h"
|
||||
#include "UIStyleType.h"
|
||||
#include "UIElement.h"
|
||||
#include "../math/Evaluator.h"
|
||||
|
||||
enum UIInputType : byte {
|
||||
UI_INPUT_TYPE_TEXT,
|
||||
UI_INPUT_TYPE_NUMERIC,
|
||||
UI_INPUT_TYPE_DATE,
|
||||
UI_INPUT_TYPE_DATE_TIME,
|
||||
UI_INPUT_TYPE_TIME,
|
||||
};
|
||||
|
||||
struct UIInputState {
|
||||
char content[512];
|
||||
char* content_ref;
|
||||
uint16 cursor_pos_x;
|
||||
uint16 cursor_pos_y;
|
||||
UIAnimationState animation;
|
||||
UIInputType type;
|
||||
int32 min_value;
|
||||
int32 max_value;
|
||||
uint16 max_input_length;
|
||||
};
|
||||
|
||||
struct UIInput {
|
||||
UIAttributeDimension dimension;
|
||||
byte opacity; // 1 byte alpha channel
|
||||
byte padding;
|
||||
|
||||
// Animation used to get into this style
|
||||
UIAnimation animation;
|
||||
|
||||
UIAttributeFont font;
|
||||
UIAttributeBackground background;
|
||||
|
||||
UIAttributeBorder border;
|
||||
UIAttributeShadow shadow_outer;
|
||||
UIAttributeShadow shadow_inner;
|
||||
};
|
||||
|
||||
void ui_input_state_populate(const UIAttributeGroup* group, UIInputState* state) {
|
||||
for (int32 i = 0; i < group->attribute_size; ++i) {
|
||||
switch (group->attributes[i].attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_TYPE: {
|
||||
state->type = (UIInputType) group->attributes[i].value_int;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_MIN_VALUE: {
|
||||
state->min_value = group->attributes[i].value_int;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_MAX_VALUE: {
|
||||
state->max_value = group->attributes[i].value_int;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_MAX_INPUT_LENGTH: {
|
||||
state->max_input_length = (uint16) group->attributes[i].value_int;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_input_element_populate(
|
||||
const UIAttributeGroup* group,
|
||||
UIElement* element,
|
||||
UIStyleType style_type,
|
||||
EvaluatorVariable* variables
|
||||
) {
|
||||
if (element->parent) {
|
||||
// @bug How to ensure that the parent is initialized before the child element
|
||||
// Currently the order of the initialization depends on the theme file, NOT the layout file
|
||||
// We could fix it by loading the style based on the layout order but this would result in many misses when looking up styles
|
||||
// The reason for these misses are, that often only 1-2 style_types exist per element
|
||||
UIInput* parent = (UIInput *) element->parent->details[UI_STYLE_TYPE_DEFAULT];
|
||||
variables[2].value = parent->dimension.dimension.x;
|
||||
variables[3].value = parent->dimension.dimension.y;
|
||||
variables[4].value = parent->dimension.dimension.width;
|
||||
variables[5].value = parent->dimension.dimension.height;
|
||||
}
|
||||
|
||||
UIInput* e = (UIInput *) element->details[style_type];
|
||||
// First set all values, which we can set immediately
|
||||
for (int32 i = 0; i < group->attribute_size; ++i) {
|
||||
switch (group->attributes[i].attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_X:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_Y:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT: {
|
||||
ui_theme_assign_dimension(&e->dimension, &group->attributes[i], 6, variables);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
13
ui/UILabel.h
Normal file
13
ui/UILabel.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_LABEL_H
|
||||
#define TOS_UI_LABEL_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UILabelState {
|
||||
};
|
||||
|
||||
struct UILabel {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
672
ui/UILayout.h
672
ui/UILayout.h
|
|
@ -1,10 +1,19 @@
|
|||
#ifndef TOS_UI_LAYOUT_H
|
||||
#define TOS_UI_LAYOUT_H
|
||||
|
||||
#include <string.h>
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../stdlib/HashMap.h"
|
||||
#include "UIElement.h"
|
||||
#include "../asset/Asset.h"
|
||||
#include "../camera/Camera.h"
|
||||
|
||||
#include "../system/FileUtils.cpp"
|
||||
|
||||
#include "UITheme.h"
|
||||
#include "UIElement.h"
|
||||
#include "UIElementType.h"
|
||||
|
||||
#define UI_LAYOUT_VERSION 1
|
||||
|
||||
// Modified for every scene
|
||||
struct UILayout {
|
||||
|
|
@ -18,130 +27,567 @@ struct UILayout {
|
|||
// Contains all UI elements also dynamic ones (e.g. movable windows)
|
||||
uint32* ui_chroma_codes;
|
||||
|
||||
// Contains constant UI elements that usually don't change (e.g. HUD)
|
||||
uint32* ui_chroma_codes_static;
|
||||
|
||||
// Used to directly find element by name
|
||||
// The values are the UIElements
|
||||
// The values are pointers to the UIElements
|
||||
// @todo Should be a perfect hash map
|
||||
HashMap hash_map;
|
||||
|
||||
int32 vertex_size;
|
||||
// UI data
|
||||
// Only the size of the fixed layout, doesn't include the theme specific data
|
||||
uint32 layout_size;
|
||||
|
||||
// Total possible size
|
||||
uint32 data_size;
|
||||
|
||||
// Holds the ui elements
|
||||
// The structure of the data is as follows:
|
||||
// 1. HashMap data (points to 2.a.)
|
||||
// 2. UIElements (default)
|
||||
// a. General UIElement
|
||||
// b. Element specific state
|
||||
// c. Element specific style
|
||||
// 3. Additional UI styles (see c.), dynamically created when the theme is loaded
|
||||
// We effectively create a tree in data where the individual elements can get directly accessed through the hashmap
|
||||
byte* data;
|
||||
|
||||
// Changes on a as needed basis
|
||||
uint32 vertex_size_static;
|
||||
bool static_content_changed;
|
||||
|
||||
// Changes every frame
|
||||
uint32 vertex_size_dynamic;
|
||||
bool dynamic_content_changed;
|
||||
|
||||
// Contains both static and dynamic content
|
||||
// @todo The vertices shouldn't be an Asset, it's more like a ECS, maybe it's not even in RAM and only in VRAM?!
|
||||
// One of the reasons for this being an asset is also that it is easy to log ram/vram usage but that can be fixed
|
||||
Asset* ui_asset;
|
||||
|
||||
// @question Should we maybe also hold the font atlas asset here AND the color palette?
|
||||
// Total count of the ui_asset vertices
|
||||
uint32 vertex_size;
|
||||
|
||||
// Defines the length of the static vertex array
|
||||
int32 vertex_size_static;
|
||||
// @question Should we maybe also hold the font atlas asset here AND the color palette?
|
||||
};
|
||||
|
||||
void count_direct_child_elements(UIElement* element, const char* pos, int32 parent_level)
|
||||
{
|
||||
// Find amount of child elements
|
||||
// We have to perform a lookahead since this determins the since this determines the size of our current element
|
||||
uint16 direct_child_elements = 0;
|
||||
|
||||
int32 level = 0;
|
||||
while (*pos != '\0') {
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (level > parent_level + 4) {
|
||||
// This element is a childrens child and not a direct child
|
||||
continue;
|
||||
} else if (level <= parent_level) {
|
||||
// We are no longer inside of element
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
element->children_count = direct_child_elements;
|
||||
}
|
||||
|
||||
void assign_child_elements(UILayout* layout, UIElement* element, char* pos, int32 parent_level) {
|
||||
int32 current_child_pos = 0;
|
||||
|
||||
char block_name[28];
|
||||
|
||||
int32 level = 0;
|
||||
while (*pos != '\0') {
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (level > parent_level + 4) {
|
||||
// This element is a childrens child and not a direct child
|
||||
continue;
|
||||
} else if (level <= parent_level) {
|
||||
// We are no longer inside of element
|
||||
break;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
element->children[current_child_pos] = (UIElement *) (
|
||||
layout->data
|
||||
+ ((HashEntryInt64 *) hashmap_get_entry(&layout->hash_map, block_name))->value
|
||||
);
|
||||
|
||||
element->children[current_child_pos]->parent = element;
|
||||
|
||||
++current_child_pos;
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING: theme needs to have memory already reserved and assigned to data
|
||||
void layout_from_file_txt(
|
||||
UILayout* layout,
|
||||
const char* path,
|
||||
RingMemory* ring
|
||||
) {
|
||||
FileBody file;
|
||||
file_read(path, &file, ring);
|
||||
ASSERT_SIMPLE(file.size);
|
||||
|
||||
char* pos = (char *) file.content;
|
||||
|
||||
// move past the version string
|
||||
pos += 8;
|
||||
|
||||
// Use version for different handling
|
||||
int32 version = strtol(pos, &pos, 10); ++pos;
|
||||
|
||||
// 1. Iteration: We have to find how many elements are defined in the layout file.
|
||||
// Therefore we have to do an initial iteration
|
||||
int32 temp_element_count = 0;
|
||||
while (*pos != '\0') {
|
||||
// Skip all white spaces
|
||||
str_skip_empty(&pos);
|
||||
|
||||
++temp_element_count;
|
||||
|
||||
// Go to the next line
|
||||
str_move_past(&pos, '\n');
|
||||
}
|
||||
|
||||
// 2. Iteration: Fill HashMap
|
||||
// @performance This is probably horrible since we are not using a perfect hashing function (1 hash -> 1 index)
|
||||
// I wouldn't be surprised if we have a 50% hash overlap (2 hashes -> 1 index)
|
||||
hashmap_create(&layout->hash_map, temp_element_count, sizeof(HashEntryVoidP), layout->data);
|
||||
int64 hm_size = hashmap_size(&layout->hash_map);
|
||||
|
||||
pos = (char *) file.content;
|
||||
|
||||
// move past version string
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
char block_name[28];
|
||||
char block_type[28];
|
||||
|
||||
byte* element_data = layout->data + hm_size;
|
||||
|
||||
int32 level;
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\n') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
level = 0;
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (*pos == '\n' || *pos == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":"); ++pos;
|
||||
str_copy_move_until(&pos, block_type, " \r\n");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
UIElement* element = (UIElement *) element_data;
|
||||
element->type = (UIElementType) ui_element_type_to_id(block_type);
|
||||
element->children = (UIElement **) (element_data + sizeof(UIElement));
|
||||
count_direct_child_elements(element, pos, level);
|
||||
|
||||
// Every UIElement can have child elements, we need to store the pointers to those elements
|
||||
element_data += sizeof(UIElement) + sizeof(UIElement*) * element->children_count;
|
||||
|
||||
// We also put the state data after this element
|
||||
element->state = element_data;
|
||||
element_data += ui_element_state_size(element->type);
|
||||
|
||||
// We also put the default element data after this element
|
||||
// Depending on the theme we will have also additional styles (e.g. :active, :hidden, ...)
|
||||
element->details[0] = element_data;
|
||||
|
||||
// @performance We should probably make sure the data is nicely aligned here
|
||||
element_data += ui_element_type_size(element->type);
|
||||
|
||||
// Insert new element
|
||||
hashmap_insert(&layout->hash_map, block_name, element_data - layout->data);
|
||||
}
|
||||
|
||||
// 3. Iteration: Create child references
|
||||
pos = (char *) file.content;
|
||||
|
||||
// move past version string
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\n') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
level = 0;
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (*pos == '\n' || *pos == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
UIElement* element = (UIElement *) (layout->data + ((HashEntryInt64 *) hashmap_get_entry(&layout->hash_map, block_name))->value);
|
||||
assign_child_elements(layout, element, pos, level);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
void ui_layout_serialize_element(HashEntryInt64* entry, byte* data, byte** pos, const byte* start)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of layout->data
|
||||
UIElement* element = (UIElement *) (data + entry->value);
|
||||
|
||||
**pos = element->state_flag;
|
||||
*pos += sizeof(element->state_flag);
|
||||
|
||||
**pos = element->type;
|
||||
*pos += sizeof(element->type);
|
||||
|
||||
**pos = element->style_old;
|
||||
*pos += sizeof(element->style_old);
|
||||
|
||||
**pos = element->style_new;
|
||||
*pos += sizeof(element->style_new);
|
||||
|
||||
// Parent
|
||||
if (element->parent) {
|
||||
*((uint64 *) *pos) = SWAP_ENDIAN_LITTLE((uint64) ((uintptr_t) element->parent - (uintptr_t) data));
|
||||
} else {
|
||||
memset(*pos, 0, sizeof(uint64));
|
||||
}
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
|
||||
// State
|
||||
if (element->state) {
|
||||
*((uint64 *) *pos) = SWAP_ENDIAN_LITTLE((uint64) ((uintptr_t) element->state - (uintptr_t) data));
|
||||
} else {
|
||||
memset(*pos, 0, sizeof(uint64));
|
||||
}
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
|
||||
// Details
|
||||
for (int32 j = 0; j < UI_STYLE_TYPE_SIZE; ++j) {
|
||||
if (element->details[j]) {
|
||||
*((uint64 *) *pos) = SWAP_ENDIAN_LITTLE((uint64) ((uintptr_t) element->details[j] - (uintptr_t) data));
|
||||
} else {
|
||||
memset(*pos, 0, sizeof(uint64));
|
||||
}
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
}
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(element->children_count);
|
||||
*pos += sizeof(element->children_count);
|
||||
|
||||
for (int32 j = 0; j < element->children_count; ++j) {
|
||||
*((uint64 *) *pos) = SWAP_ENDIAN_LITTLE((uint64) ((uintptr_t) element->children[j] - (uintptr_t) data));
|
||||
*pos += sizeof(uint64);
|
||||
}
|
||||
|
||||
// @performance, shouldn't it just be memset for both elements?
|
||||
// state element data e.g. UIInputState
|
||||
memcpy(*pos, element->state, ui_element_state_size(element->type));
|
||||
*pos += ui_element_state_size(element->type);
|
||||
|
||||
// detailed element data e.g. UIInput
|
||||
memcpy(*pos, element->details[0], ui_element_type_size(element->type));
|
||||
*pos += ui_element_type_size(element->type);
|
||||
}
|
||||
|
||||
int32 layout_to_data(
|
||||
const UILayout* layout,
|
||||
byte* data
|
||||
) {
|
||||
byte* pos = data;
|
||||
byte* max_pos = data;
|
||||
|
||||
// version
|
||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(UI_LAYOUT_VERSION);
|
||||
pos += sizeof(int32);
|
||||
|
||||
// hashmap
|
||||
byte* start = pos;
|
||||
pos += hashmap_dump(&layout->hash_map, pos);
|
||||
|
||||
// UIElement data
|
||||
for (uint32 i = 0; i < layout->hash_map.buf.count; ++i) {
|
||||
if (!layout->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) layout->hash_map.table[i];
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_layout_serialize_element(entry, layout->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
|
||||
// save all the next elements
|
||||
while (entry->next) {
|
||||
pos = start + entry->value;
|
||||
ui_layout_serialize_element(entry, layout->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
return (int32) (max_pos - data);
|
||||
}
|
||||
|
||||
static inline
|
||||
void ui_layout_parse_element(HashEntryInt64* entry, byte* data, const byte** pos)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of layout->data
|
||||
|
||||
// Change offset to pointer
|
||||
entry->value = (uintptr_t) data + entry->value;
|
||||
|
||||
UIElement* element = (UIElement *) entry->value;
|
||||
|
||||
element->state_flag = **pos;
|
||||
*pos += sizeof(element->state_flag);
|
||||
|
||||
element->type = (UIElementType) **pos;
|
||||
*pos += sizeof(element->type);
|
||||
|
||||
element->style_old = (UIStyleType) **pos;
|
||||
*pos += sizeof(element->style_old);
|
||||
|
||||
element->style_new = (UIStyleType) **pos;
|
||||
*pos += sizeof(element->style_new);
|
||||
|
||||
// Parent
|
||||
uint64 temp = SWAP_ENDIAN_LITTLE(*((uint64 *) *pos));
|
||||
element->parent = temp
|
||||
? (UIElement *) (data + temp)
|
||||
: NULL;
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
|
||||
// State
|
||||
temp = SWAP_ENDIAN_LITTLE(*((uint64 *) *pos));
|
||||
element->state = temp
|
||||
? data + temp
|
||||
: NULL;
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
|
||||
// Details
|
||||
for (int32 j = 0; j < UI_STYLE_TYPE_SIZE; ++j) {
|
||||
temp = SWAP_ENDIAN_LITTLE(*((uint64 *) *pos));
|
||||
element->details[j] = temp
|
||||
? data + temp
|
||||
: NULL;
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
}
|
||||
|
||||
element->children_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(element->children_count);
|
||||
|
||||
for (int32 j = 0; j < element->children_count; ++j) {
|
||||
temp = SWAP_ENDIAN_LITTLE(*((uint64 *) *pos));
|
||||
element->children[j] = temp
|
||||
? (UIElement *) (data + temp)
|
||||
: NULL; // this should never happen since the children_count only gets incremented if it has a child
|
||||
|
||||
if (element->children[j]) {
|
||||
element->children[j]->parent = element;
|
||||
}
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
}
|
||||
|
||||
// @performance, shouldn't it just be memset for both elements?
|
||||
// state element data e.g. UIInput
|
||||
memcpy(element->state, *pos, ui_element_state_size(element->type));
|
||||
*pos += ui_element_state_size(element->type);
|
||||
|
||||
// detailed element data e.g. UIInput
|
||||
memcpy(element->details[0], *pos, ui_element_type_size(element->type));
|
||||
*pos += ui_element_type_size(element->type);
|
||||
}
|
||||
|
||||
// The size of layout->data should be the file size + a bunch of additional data for additional theme dependent "UIElements->details".
|
||||
// Yes, this means we have a little too much data but not by a lot
|
||||
int32 layout_from_data(
|
||||
const byte* data,
|
||||
UILayout* layout
|
||||
) {
|
||||
const byte* pos = data;
|
||||
const byte* max_pos = pos;
|
||||
|
||||
int32 version = *((int32 *) pos);
|
||||
pos += sizeof(version);
|
||||
|
||||
// Prepare hashmap (incl. reserve memory) by initializing it the same way we originally did
|
||||
// Of course we still need to populate the data using hashmap_load()
|
||||
// The value is a int64 (because this is the value of the chunk buffer size but the hashmap only allows int32)
|
||||
hashmap_create(&layout->hash_map, (int32) SWAP_ENDIAN_LITTLE(*((uint64 *) pos)), sizeof(HashEntryInt64), layout->data);
|
||||
|
||||
const byte* start = data;
|
||||
pos += hashmap_load(&layout->hash_map, pos);
|
||||
|
||||
// layout data
|
||||
for (int32 i = 0; i < layout->hash_map.buf.count; ++i) {
|
||||
if (!layout->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) layout->hash_map.table[i];
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_layout_parse_element(entry, layout->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
|
||||
// save all the next elements
|
||||
while (entry->next) {
|
||||
pos = start + entry->value;
|
||||
ui_layout_parse_element(entry, layout->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
layout->layout_size = (uint32) (max_pos - data);
|
||||
|
||||
return (int32) layout->layout_size;
|
||||
}
|
||||
|
||||
// @performance Implement a way to only load a specific element and all its children
|
||||
// This way we can re-load specific elements on change and we could also greatly reduce the setup time by ignoring ui elements that are rarely visible
|
||||
|
||||
void layout_from_theme(
|
||||
UILayout* layout,
|
||||
const UIThemeStyle* theme,
|
||||
const Camera* camera
|
||||
) {
|
||||
EvaluatorVariable variables[] = {
|
||||
{ "vw", (f32) camera->viewport_width },
|
||||
{ "vh", (f32) camera->viewport_height },
|
||||
{ "px", 0.0 }, // Placeholder for parent values
|
||||
{ "py", 0.0 }, // Placeholder for parent values
|
||||
{ "pw", 0.0 }, // Placeholder for parent values
|
||||
{ "ph", 0.0 }, // Placeholder for parent values
|
||||
};
|
||||
|
||||
// Current position where we can the different sub elements (e.g. :hover, :active, ...)
|
||||
byte* dynamic_pos = layout->data + layout->layout_size;
|
||||
|
||||
// We first need to handle the default element -> iterate all elements but only handle the default style
|
||||
// @todo do here
|
||||
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||
if (!theme->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryUIntPtr* style_entry = (HashEntryUIntPtr *) theme->hash_map.table[i];
|
||||
|
||||
// We don't handle special styles here, only the default one
|
||||
if (strchr(style_entry->key, ':')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntry* entry = hashmap_get_entry(&layout->hash_map, style_entry->key);
|
||||
if (!entry) {
|
||||
// Couldn't even find the base element
|
||||
continue;
|
||||
}
|
||||
|
||||
// Populate default element
|
||||
UIElement* element = (UIElement *) entry->value;
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) style_entry->value;
|
||||
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_state_populate(group, (UIInputState *) element->state);
|
||||
ui_input_element_populate(group, element, UI_STYLE_TYPE_DEFAULT, variables);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// We iterate every style
|
||||
// 1. Fill default element if it is default style
|
||||
// 2. Create and fill new element if it isn't default style (e.g. :hover)
|
||||
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||
if (!theme->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryUIntPtr* style_entry = (HashEntryUIntPtr *) theme->hash_map.table[i];
|
||||
|
||||
// We only handle special styles here, not the default one
|
||||
char* special = strchr(style_entry->key, ':');
|
||||
if (!special) {
|
||||
// The default element was already handled outside this loop
|
||||
continue;
|
||||
}
|
||||
|
||||
UIStyleType style_type = (UIStyleType) ui_style_type_to_id(special);
|
||||
|
||||
char pure_name[HASH_MAP_MAX_KEY_LENGTH];
|
||||
str_copy_until(style_entry->key, pure_name, ':');
|
||||
|
||||
HashEntry* entry = hashmap_get_entry(&layout->hash_map, pure_name);
|
||||
if (!entry) {
|
||||
// Couldn't even find the base element
|
||||
continue;
|
||||
}
|
||||
|
||||
UIElement* element = (UIElement *) entry->value;
|
||||
|
||||
// Doesn't exist (usually the first load, but exists when we resize our window)
|
||||
if (!element->details[style_type]) {
|
||||
element->details[style_type] = dynamic_pos;
|
||||
dynamic_pos += ui_element_type_size(element->type);
|
||||
}
|
||||
|
||||
// The style inherits from the default element
|
||||
memcpy(element->details[style_type], element->details[0], ui_element_type_size(element->type));
|
||||
|
||||
// Populate element details
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) style_entry->value;
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_element_populate(group, element, style_type, variables);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 layout_element_from_location(UILayout* layout, uint16 x, uint16 y)
|
||||
{
|
||||
return layout->ui_chroma_codes[layout->width * y / 4 + x / 4];
|
||||
}
|
||||
|
||||
// This function should only get called if the location of a UI Element changes
|
||||
// @performance How to handle moving elements (= dragging a window). We don't want to update this while dragging!
|
||||
void layout_chroma_codes_update(UILayout* layout)
|
||||
{
|
||||
// Reset all
|
||||
memcpy(layout->ui_chroma_codes, layout->ui_chroma_codes_static, layout->width * layout->height * sizeof(uint32));
|
||||
|
||||
// @question Are the dimension values below even absolute? They may be in relation to the parent?!
|
||||
for (int32 i = 0; i < layout->hash_map.buf.count; ++i) {
|
||||
if (!layout->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntry* entry = (HashEntry *) layout->hash_map.table[i];
|
||||
UIElement* element = (UIElement *) entry->value;
|
||||
|
||||
if (element->is_dynamic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 y_start = element->dimension.y1 / 4;
|
||||
int32 y_end = element->dimension.y2 / 4;
|
||||
int32 x_start = element->dimension.x1 / 4;
|
||||
int32 x_end = element->dimension.x2 / 4;
|
||||
|
||||
for (int32 y = y_start; y < y_end; ++y) {
|
||||
int32 y_offset = layout->width * y;
|
||||
for (int32 x = x_start; x < x_end; ++x) {
|
||||
layout->ui_chroma_codes[y_offset + x] = (uint32) element->id;
|
||||
}
|
||||
}
|
||||
|
||||
// Now handle all next elements
|
||||
while (entry->next) {
|
||||
entry = entry->next;
|
||||
|
||||
element = (UIElement *) entry->value;
|
||||
|
||||
y_start = element->dimension.y1 / 4;
|
||||
y_end = element->dimension.y2 / 4;
|
||||
x_start = element->dimension.x1 / 4;
|
||||
x_end = element->dimension.x2 / 4;
|
||||
|
||||
for (int32 y = y_start; y < y_end; ++y) {
|
||||
int32 y_offset = layout->width * y;
|
||||
for (int32 x = x_start; x < x_end; ++x) {
|
||||
layout->ui_chroma_codes[y_offset + x] = (uint32) element->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void layout_chroma_codes_update_static(UILayout* layout)
|
||||
{
|
||||
// Reset all
|
||||
memset(layout->ui_chroma_codes_static, 0, layout->width * layout->height * sizeof(uint32));
|
||||
|
||||
// @question Are the dimension values below even absolute? They may be in relation to the parent?!
|
||||
for (int32 i = 0; i < layout->hash_map.buf.count; ++i) {
|
||||
if (!layout->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntry* entry = (HashEntry *) layout->hash_map.table[i];
|
||||
UIElement* element = (UIElement *) entry->value;
|
||||
|
||||
if (!element->is_dynamic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 y_start = element->dimension.y1 / 4;
|
||||
int32 y_end = element->dimension.y2 / 4;
|
||||
int32 x_start = element->dimension.x1 / 4;
|
||||
int32 x_end = element->dimension.x2 / 4;
|
||||
|
||||
for (int32 y = y_start; y < y_end; ++y) {
|
||||
int32 y_offset = layout->width * y;
|
||||
for (int32 x = x_start; x < x_end; ++x) {
|
||||
layout->ui_chroma_codes_static[y_offset + x] = (uint32) element->id;
|
||||
}
|
||||
}
|
||||
|
||||
// Now handle all next elements
|
||||
while (entry->next) {
|
||||
entry = entry->next;
|
||||
|
||||
element = (UIElement *) entry->value;
|
||||
|
||||
y_start = element->dimension.y1 / 4;
|
||||
y_end = element->dimension.y2 / 4;
|
||||
x_start = element->dimension.x1 / 4;
|
||||
x_end = element->dimension.x2 / 4;
|
||||
|
||||
for (int32 y = y_start; y < y_end; ++y) {
|
||||
int32 y_offset = layout->width * y;
|
||||
for (int32 x = x_start; x < x_end; ++x) {
|
||||
layout->ui_chroma_codes_static[y_offset + x] = (uint32) element->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
13
ui/UILink.h
Normal file
13
ui/UILink.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_LINK_H
|
||||
#define TOS_UI_LINK_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UILinkState {
|
||||
};
|
||||
|
||||
struct UILink {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
13
ui/UIPanel.h
Normal file
13
ui/UIPanel.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_PANEL_H
|
||||
#define TOS_UI_PANEL_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UIPanelState {
|
||||
};
|
||||
|
||||
struct UIPanel {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
13
ui/UISelect.h
Normal file
13
ui/UISelect.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_SELECT_H
|
||||
#define TOS_UI_SELECT_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UISelectState {
|
||||
};
|
||||
|
||||
struct UISelect {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
34
ui/UIStyleType.h
Normal file
34
ui/UIStyleType.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef TOS_UI_STYLE_TYPE_H
|
||||
#define TOS_UI_STYLE_TYPE_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
enum UIStyleType : byte {
|
||||
UI_STYLE_TYPE_DEFAULT, // = :visible
|
||||
UI_STYLE_TYPE_HIDDEN,
|
||||
UI_STYLE_TYPE_ACTIVE, // e.g. input
|
||||
UI_STYLE_TYPE_DISABLED, // disabled form elements
|
||||
UI_STYLE_TYPE_HOVER, // e.g. button
|
||||
UI_STYLE_TYPE_MANUAL,
|
||||
UI_STYLE_TYPE_SIZE,
|
||||
};
|
||||
|
||||
constexpr
|
||||
int32 ui_style_type_to_id(const char* str)
|
||||
{
|
||||
if (str_compare(":hidden", str) == 0) {
|
||||
return UI_STYLE_TYPE_HIDDEN;
|
||||
} else if (str_compare(":active", str) == 0) {
|
||||
return UI_STYLE_TYPE_ACTIVE;
|
||||
} else if (str_compare(":diabled", str) == 0) {
|
||||
return UI_STYLE_TYPE_DISABLED;
|
||||
} else if (str_compare(":hover", str) == 0) {
|
||||
return UI_STYLE_TYPE_HOVER;
|
||||
} else if (str_compare(":manual", str) == 0) {
|
||||
return UI_STYLE_TYPE_MANUAL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
13
ui/UITab.h
Normal file
13
ui/UITab.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_TAB_H
|
||||
#define TOS_UI_TAB_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UITabState {
|
||||
};
|
||||
|
||||
struct UITab {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
13
ui/UITable.h
Normal file
13
ui/UITable.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_TABLE_H
|
||||
#define TOS_UI_TABLE_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UITableState {
|
||||
};
|
||||
|
||||
struct UITable {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
13
ui/UIText.h
Normal file
13
ui/UIText.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_TEXT_H
|
||||
#define TOS_UI_TEXT_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UITextState {
|
||||
};
|
||||
|
||||
struct UIText {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
13
ui/UITextarea.h
Normal file
13
ui/UITextarea.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_TEXTAREA_H
|
||||
#define TOS_UI_TEXTAREA_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
struct UITextareaState {
|
||||
};
|
||||
|
||||
struct UITextarea {
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
364
ui/UITheme.h
364
ui/UITheme.h
|
|
@ -9,7 +9,7 @@
|
|||
#include "../font/Font.h"
|
||||
#include "../system/FileUtils.cpp"
|
||||
|
||||
#include "UIAttribute.h"
|
||||
#include "attribute/UIAttribute.h"
|
||||
#include "UIElementType.h"
|
||||
|
||||
#define UI_THEME_VERSION 1
|
||||
|
|
@ -20,43 +20,16 @@
|
|||
// WARNING: Make sure the order of this struct and UITheme is the same for the first elements
|
||||
// This allows us to cast between both
|
||||
struct UIThemeStyle {
|
||||
byte* data;
|
||||
|
||||
// A theme may have N named styles
|
||||
// The hashmap contains the offset where the respective style can be found
|
||||
// @performance Switch to perfect hash map
|
||||
HashMap hash_map;
|
||||
byte* data;
|
||||
|
||||
// It feels weird that this is here, especially considering we could have multiple fonts
|
||||
Font* font;
|
||||
};
|
||||
|
||||
// General theme for all scenes
|
||||
struct UITheme {
|
||||
// This one usually remains unchanged unless someone changes the theme
|
||||
UIThemeStyle ui_general;
|
||||
|
||||
// This one is scene specific and is loaded with the scene
|
||||
// We have a pointer that references the currently active scene
|
||||
// The other two elements contain the actual data.
|
||||
// This allows us to easily pre-fetch scene styles and the pointer allows us to easily switch between them
|
||||
// When loading a new scene we simply use the style that is currently not pointed to by primary_scene
|
||||
UIThemeStyle* primary_scene;
|
||||
UIThemeStyle ui_scene1;
|
||||
UIThemeStyle ui_scene2;
|
||||
|
||||
// @question This basically means we only support 1 font for the UI ?!
|
||||
// This is probably something to re-consider
|
||||
Font font;
|
||||
|
||||
// @todo add cursor styles
|
||||
// @todo what about ui audio?
|
||||
|
||||
char name[32];
|
||||
};
|
||||
|
||||
// @performance Consider to replace HashEntryInt64 with HashEntryVoidP.
|
||||
// This way we wouldn't have to do theme->data + entry->value and could just use entry->value
|
||||
// Of course this means during the saving and loading we need to convert to and from offsets
|
||||
// The problem is that the actual dumping and loading for that part doesn't happen in the hashmap but in the chunk_memory
|
||||
// The chunk_memory doesn't know how the value look like -> cannot do the conversion
|
||||
inline
|
||||
UIAttributeGroup* theme_style_group(UIThemeStyle* theme, const char* group_name)
|
||||
{
|
||||
|
|
@ -66,7 +39,7 @@ UIAttributeGroup* theme_style_group(UIThemeStyle* theme, const char* group_name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return (UIAttributeGroup *) (theme->data + entry->value);
|
||||
return (UIAttributeGroup *) entry->value;
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
@ -78,9 +51,10 @@ UIAttributeGroup* theme_style_group(UIThemeStyle* theme, const char* group_name,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return (UIAttributeGroup *) (theme->data + entry->value);
|
||||
return (UIAttributeGroup *) entry->value;
|
||||
}
|
||||
|
||||
static inline
|
||||
int compare_by_attribute_id(const void* a, const void* b) {
|
||||
UIAttribute* attr_a = (UIAttribute *) a;
|
||||
UIAttribute* attr_b = (UIAttribute *) b;
|
||||
|
|
@ -117,11 +91,6 @@ void theme_from_file_txt(
|
|||
// Use version for different handling
|
||||
int32 version = strtol(pos, &pos, 10); ++pos;
|
||||
|
||||
bool block_open = false;
|
||||
char block_name[32];
|
||||
char attribute_name[32];
|
||||
bool last_token_newline = false;
|
||||
|
||||
// We have to find how many groups are defined in the theme file.
|
||||
// Therefore we have to do an initial iteration
|
||||
int32 temp_group_count = 0;
|
||||
|
|
@ -134,13 +103,8 @@ void theme_from_file_txt(
|
|||
++temp_group_count;
|
||||
}
|
||||
|
||||
// Go to the end of the line
|
||||
str_move_to(&pos, '\n');
|
||||
|
||||
// Go to next line
|
||||
if (*pos != '\0') {
|
||||
++pos;
|
||||
}
|
||||
// Go to the next line
|
||||
str_move_past(&pos, '\n');
|
||||
}
|
||||
|
||||
// @performance This is probably horrible since we are not using a perfect hashing function (1 hash -> 1 index)
|
||||
|
|
@ -155,6 +119,10 @@ void theme_from_file_txt(
|
|||
// move past version string
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
bool block_open = false;
|
||||
char block_name[28];
|
||||
char attribute_name[32];
|
||||
bool last_token_newline = false;
|
||||
while (*pos != '\0') {
|
||||
str_skip_whitespace(&pos);
|
||||
|
||||
|
|
@ -175,7 +143,7 @@ void theme_from_file_txt(
|
|||
last_token_newline = false;
|
||||
|
||||
if (!block_open) {
|
||||
str_copy_move_until(&pos, block_name, " \n", sizeof(" \n") - 1);
|
||||
str_copy_move_until(&pos, block_name, " \r\n");
|
||||
|
||||
// All blocks need to start with #. In the past this wasn't the case and may not be in the future. This is why we keep this if here.
|
||||
if (*block_name == '#' || *block_name == '.') {
|
||||
|
|
@ -200,166 +168,16 @@ void theme_from_file_txt(
|
|||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, attribute_name, " :\n", sizeof(" :\n") - 1);
|
||||
str_copy_move_until(&pos, attribute_name, " :\n");
|
||||
|
||||
// Skip any white spaces or other delimeter
|
||||
str_skip_list(&pos, " \t:", sizeof(" \t:") - 1);
|
||||
|
||||
ASSERT_SIMPLE((*pos != '\0' && *pos != '\n'));
|
||||
|
||||
// Handle different attribute types
|
||||
// Parse attribute value
|
||||
UIAttribute attribute = {};
|
||||
if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_TYPE), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TYPE;
|
||||
|
||||
char str[32];
|
||||
str_copy_move_until(&pos, str, '\n');
|
||||
|
||||
for (int32 j = 0; j < UI_ELEMENT_TYPE_SIZE; ++j) {
|
||||
if (strcmp(str, ui_element_type_to_string((UIElementType) j)) == 0) {
|
||||
|
||||
attribute.value_int = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++pos;
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_STYLE), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_STYLE;
|
||||
|
||||
str_copy_move_until(&pos, attribute.value_str, '\n');
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_SIZE), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_SIZE;
|
||||
attribute.value_float = strtof(pos, &pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_WEIGHT), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_WEIGHT;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT;
|
||||
attribute.value_float = strtof(pos, &pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_ALIGN_H), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ALIGN_H;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_ALIGN_V), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ALIGN_V;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_ZINDEX), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ZINDEX;
|
||||
attribute.value_float = SWAP_ENDIAN_LITTLE(strtof(pos, &pos));
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG;
|
||||
|
||||
str_copy_move_until(&pos, attribute.value_str, '\n');
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY;
|
||||
attribute.value_float = SWAP_ENDIAN_LITTLE(strtof(pos, &pos));
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_WIDTH), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_WIDTH;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING_TOP), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_TOP;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING_RIGHT), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_RIGHT;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING_BOTTOM), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_BOTTOM;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING_LEFT), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_LEFT;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE;
|
||||
attribute.value_float = strtof(pos, &pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR), attribute_name) == 0) {
|
||||
++pos; // Skip '#'
|
||||
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR;
|
||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE;
|
||||
attribute.value_float = strtof(pos, &pos);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION;
|
||||
attribute.value_int = strtoul(pos, &pos, 10);
|
||||
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_TRANSITION_DURATION), attribute_name) == 0) {
|
||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TRANSITION_DURATION;
|
||||
attribute.value_float = strtof(pos, &pos);
|
||||
} else {
|
||||
str_move_to(&pos, '\n');
|
||||
|
||||
continue;
|
||||
}
|
||||
ui_attribute_parse_value(&attribute, attribute_name, pos);
|
||||
|
||||
// Again, currently this if check is redundant but it wasn't in the past and we may need it again in the future.
|
||||
if (block_name[0] == '#' || block_name[0] == '.') {
|
||||
|
|
@ -381,6 +199,52 @@ void theme_from_file_txt(
|
|||
qsort(temp_group->attributes, temp_group->attribute_size, sizeof(UIAttribute), compare_by_attribute_id);
|
||||
}
|
||||
|
||||
static inline
|
||||
void ui_theme_parse_group(HashEntryInt64* entry, byte* data, const byte** pos)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of theme->data
|
||||
|
||||
// Change offset to pointer
|
||||
entry->value = (uintptr_t) data + entry->value;
|
||||
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) entry->value;
|
||||
|
||||
group->attribute_size = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
||||
*pos += sizeof(group->attribute_size);
|
||||
|
||||
for (uint32 j = 0; j < group->attribute_size; ++j) {
|
||||
group->attributes[j].attribute_id = (UIAttributeType) SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].attribute_id);
|
||||
|
||||
group->attributes[j].datatype = *((UIAttributeDataType *) *pos);
|
||||
*pos += sizeof(group->attributes[j].datatype);
|
||||
|
||||
if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
group->attributes[j].value_int = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_int);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
group->attributes[j].value_float = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_float);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_STR) {
|
||||
memcpy(group->attributes[j].value_str, *pos, sizeof(group->attributes[j].value_str));
|
||||
*pos += sizeof(group->attributes[j].value_str);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_V4_F32) {
|
||||
group->attributes[j].value_v4_f32.x = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.x);
|
||||
|
||||
group->attributes[j].value_v4_f32.y = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.y);
|
||||
|
||||
group->attributes[j].value_v4_f32.z = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.z);
|
||||
|
||||
group->attributes[j].value_v4_f32.w = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Memory layout (data) - This is not the layout of the file itself, just how we represent it in memory
|
||||
// Hashmap
|
||||
// UIAttributeGroup - This is where the pointers point to (or what the offset represents)
|
||||
|
|
@ -401,6 +265,7 @@ int32 theme_from_data(
|
|||
UIThemeStyle* theme
|
||||
) {
|
||||
const byte* pos = data;
|
||||
const byte* max_pos = data;
|
||||
|
||||
int32 version = *((int32 *) pos);
|
||||
pos += sizeof(version);
|
||||
|
|
@ -422,33 +287,25 @@ int32 theme_from_data(
|
|||
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) theme->hash_map.table[i];
|
||||
|
||||
// This way we now could access the data directly without the silly theme->data + entry->value calc.
|
||||
pos = start + entry->value;
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) (theme->data + entry->value);
|
||||
|
||||
group->attribute_size = SWAP_ENDIAN_LITTLE(*((int32 *) pos));
|
||||
pos += sizeof(group->attribute_size);
|
||||
|
||||
// @performance The UIAttribute contains a char array which makes this WAY larger than it needs to be in 99% of the cases
|
||||
memcpy(group->attributes, pos, group->attribute_size * sizeof(UIAttribute));
|
||||
pos += group->attribute_size * sizeof(UIAttribute);
|
||||
ui_theme_parse_group(entry, theme->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
|
||||
// load all the next elements
|
||||
while (entry->next) {
|
||||
pos = start + entry->value;
|
||||
group = (UIAttributeGroup *) (theme->data + entry->value);
|
||||
|
||||
group->attribute_size = SWAP_ENDIAN_LITTLE(*((int32 *) pos));
|
||||
pos += sizeof(group->attribute_size);
|
||||
|
||||
// @performance The UIAttribute contains a char array which makes this WAY larger than it needs to be in 99% of the cases
|
||||
memcpy(group->attributes, pos, group->attribute_size * sizeof(UIAttribute));
|
||||
pos += group->attribute_size * sizeof(UIAttribute);
|
||||
|
||||
ui_theme_parse_group(entry, theme->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
return (int32) (pos - data);
|
||||
return (int32) (max_pos - data);
|
||||
}
|
||||
|
||||
// Calculates the maximum theme size
|
||||
|
|
@ -461,6 +318,49 @@ int64 theme_data_size(const UIThemeStyle* theme)
|
|||
+ theme->hash_map.buf.count * UI_ATTRIBUTE_TYPE_SIZE * sizeof(UIAttribute);
|
||||
}
|
||||
|
||||
// @todo Why do even need **pos, shouldn't it just be *pos
|
||||
static inline
|
||||
void ui_theme_serialize_group(HashEntryInt64* entry, byte* data, byte** pos, const byte* start)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of theme->data
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) (data + entry->value);
|
||||
|
||||
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attribute_size);
|
||||
*pos += sizeof(group->attribute_size);
|
||||
|
||||
for (uint32 j = 0; j < group->attribute_size; ++j) {
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].attribute_id);
|
||||
*pos += sizeof(group->attributes[j].attribute_id);
|
||||
|
||||
*((byte *) *pos) = group->attributes[j].datatype;
|
||||
*pos += sizeof(group->attributes[j].datatype);
|
||||
|
||||
if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_int);
|
||||
*pos += sizeof(group->attributes[j].value_int);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_float);
|
||||
*pos += sizeof(group->attributes[j].value_float);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_STR) {
|
||||
memcpy(*pos, group->attributes[j].value_str, sizeof(group->attributes[j].value_str));
|
||||
*pos += sizeof(group->attributes[j].value_str);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_V4_F32) {
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_v4_f32.x);
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.x);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_v4_f32.y);
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.y);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_v4_f32.z);
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.z);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_v4_f32.w);
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// File layout - binary
|
||||
// version
|
||||
// hashmap size
|
||||
|
|
@ -481,6 +381,7 @@ int32 theme_to_data(
|
|||
byte* data
|
||||
) {
|
||||
byte* pos = data;
|
||||
byte* max_pos = data;
|
||||
|
||||
// version
|
||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(UI_THEME_VERSION);
|
||||
|
|
@ -500,32 +401,23 @@ int32 theme_to_data(
|
|||
HashEntryInt64* entry = (HashEntryInt64 *) theme->hash_map.table[i];
|
||||
|
||||
pos = start + entry->value;
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) (theme->data + entry->value);
|
||||
|
||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(group->attribute_size);
|
||||
pos += sizeof(group->attribute_size);
|
||||
|
||||
// @performance The UIAttribute contains a char array which makes this WAY larger than it needs to be in 99% of the cases
|
||||
memcpy(pos, group->attributes, group->attribute_size * sizeof(UIAttribute));
|
||||
pos += sizeof(UIAttribute);
|
||||
ui_theme_serialize_group(entry, theme->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
|
||||
// save all the next elements
|
||||
while (entry->next) {
|
||||
pos = start + entry->value;
|
||||
group = (UIAttributeGroup *) (theme->data + entry->value);
|
||||
|
||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(group->attribute_size);
|
||||
pos += sizeof(group->attribute_size);
|
||||
|
||||
// @performance The UIAttribute contains a char array which makes this WAY larger than it needs to be in 99% of the cases
|
||||
memcpy(pos, group->attributes, group->attribute_size * sizeof(UIAttribute));
|
||||
pos += sizeof(UIAttribute);
|
||||
|
||||
ui_theme_serialize_group(entry, theme->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
return (int32) (pos - data);
|
||||
return (int32) (max_pos - data);
|
||||
}
|
||||
|
||||
#endif
|
||||
31
ui/UIWindow.h
Normal file
31
ui/UIWindow.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef TOS_UI_WINDOW_H
|
||||
#define TOS_UI_WINDOW_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "attribute/UIAttributeBorder.h"
|
||||
#include "attribute/UIAttributeShadow.h"
|
||||
#include "attribute/UIAttributeFont.h"
|
||||
#include "attribute/UIAttributeBackground.h"
|
||||
#include "UIAnimation.h"
|
||||
#include "UIStyleType.h"
|
||||
|
||||
struct UIWindowState {
|
||||
};
|
||||
|
||||
struct UIWindow {
|
||||
v4_int16 dimension;
|
||||
UIAnimation animation;
|
||||
byte padding;
|
||||
byte alignment;
|
||||
byte opacity;
|
||||
|
||||
uintptr_t background;
|
||||
UIBackgroundStyle background_style;
|
||||
UIAttributeBorder border;
|
||||
UIAttributeShadow shadow_outer;
|
||||
UIAttributeShadow shadow_inner;
|
||||
|
||||
UIWindow* styles[UI_STYLE_TYPE_SIZE];
|
||||
};
|
||||
|
||||
#endif
|
||||
246
ui/attribute/UIAttribute.h
Normal file
246
ui/attribute/UIAttribute.h
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
#ifndef TOS_UI_ATTRIBUTE_H
|
||||
#define TOS_UI_ATTRIBUTE_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
#include "../../utils/StringUtils.h"
|
||||
#include "../../math/Evaluator.h"
|
||||
#include "UIAttributeType.h"
|
||||
#include "UIAttributeDimension.h"
|
||||
|
||||
enum UIAttributeDataType : byte {
|
||||
UI_ATTRIBUTE_DATA_TYPE_INT,
|
||||
UI_ATTRIBUTE_DATA_TYPE_F32,
|
||||
UI_ATTRIBUTE_DATA_TYPE_STR,
|
||||
UI_ATTRIBUTE_DATA_TYPE_V4_F32,
|
||||
};
|
||||
|
||||
struct UIAttribute {
|
||||
// Attributes use ids (=type name) instead of strings
|
||||
UIAttributeType attribute_id;
|
||||
UIAttributeDataType datatype;
|
||||
|
||||
union {
|
||||
// @performance The string makes this struct really large when it is not needed in 95% of the cases
|
||||
char value_str[32];
|
||||
int32 value_int;
|
||||
f32 value_float;
|
||||
v4_f32 value_v4_f32;
|
||||
};
|
||||
};
|
||||
|
||||
struct UIAttributeGroup {
|
||||
int32 attribute_size;
|
||||
UIAttribute* attributes;
|
||||
};
|
||||
|
||||
UIAttribute* ui_attribute_from_group(UIAttributeGroup* group, UIAttributeType type)
|
||||
{
|
||||
if (!group->attributes) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32 left = 0;
|
||||
int32 right = type;
|
||||
|
||||
// Binary search since attributes are sorted by attribute_id
|
||||
// @performance Consider Eytzinger
|
||||
while (left <= right) {
|
||||
int32 mid = left + (right - left) / 2;
|
||||
|
||||
if (group->attributes[mid].attribute_id == type) {
|
||||
return &group->attributes[mid];
|
||||
} else if (group->attributes[mid].attribute_id < type) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
constexpr
|
||||
int32 ui_attribute_type_to_id(const char* attribute_name)
|
||||
{
|
||||
if (str_compare(attribute_name, "x") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_DIMENSION_X;
|
||||
} else if (str_compare(attribute_name, "y") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_DIMENSION_Y;
|
||||
} else if (str_compare(attribute_name, "width") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH;
|
||||
} else if (str_compare(attribute_name, "height") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT;
|
||||
} else if (str_compare(attribute_name, "font_name") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FONT_NAME;
|
||||
} else if (str_compare(attribute_name, "font_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FONT_COLOR;
|
||||
} else if (str_compare(attribute_name, "font_size") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FONT_SIZE;
|
||||
} else if (str_compare(attribute_name, "font_weight") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FONT_WEIGHT;
|
||||
} else if (str_compare(attribute_name, "font_line_height") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT;
|
||||
} else if (str_compare(attribute_name, "align_h") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_ALIGN_H;
|
||||
} else if (str_compare(attribute_name, "align_v") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_ALIGN_V;
|
||||
} else if (str_compare(attribute_name, "zindex") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_ZINDEX;
|
||||
} else if (str_compare(attribute_name, "style1") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_STYLE1;
|
||||
} else if (str_compare(attribute_name, "style2") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_STYLE2;
|
||||
} else if (str_compare(attribute_name, "style3") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_STYLE3;
|
||||
} else if (str_compare(attribute_name, "style4") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_STYLE4;
|
||||
} else if (str_compare(attribute_name, "style5") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_STYLE5;
|
||||
} else if (str_compare(attribute_name, "style6") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_STYLE6;
|
||||
} else if (str_compare(attribute_name, "style7") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_STYLE7;
|
||||
} else if (str_compare(attribute_name, "style8") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_STYLE8;
|
||||
} else if (str_compare(attribute_name, "foreground_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FOREGROUND_COLOR;
|
||||
} else if (str_compare(attribute_name, "foreground_img") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FOREGROUND_IMG;
|
||||
} else if (str_compare(attribute_name, "foreground_img_opacity") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FOREGROUND_IMG_OPACITY;
|
||||
} else if (str_compare(attribute_name, "foreground_img_position_v") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FOREGROUND_IMG_POSITION_V;
|
||||
} else if (str_compare(attribute_name, "foreground_img_position_h") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FOREGROUND_IMG_POSITION_H;
|
||||
} else if (str_compare(attribute_name, "foreground_img_style") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FOREGROUND_IMG_STYLE;
|
||||
} else if (str_compare(attribute_name, "background_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR;
|
||||
} else if (str_compare(attribute_name, "background_img") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BACKGROUND_IMG;
|
||||
} else if (str_compare(attribute_name, "background_img_opacity") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY;
|
||||
} else if (str_compare(attribute_name, "background_img_position_v") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V;
|
||||
} else if (str_compare(attribute_name, "background_img_position_h") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H;
|
||||
} else if (str_compare(attribute_name, "background_img_style") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE;
|
||||
} else if (str_compare(attribute_name, "border_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_COLOR;
|
||||
} else if (str_compare(attribute_name, "border_width") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_WIDTH;
|
||||
} else if (str_compare(attribute_name, "border_top_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR;
|
||||
} else if (str_compare(attribute_name, "border_top_width") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH;
|
||||
} else if (str_compare(attribute_name, "border_right_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR;
|
||||
} else if (str_compare(attribute_name, "border_right_width") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH;
|
||||
} else if (str_compare(attribute_name, "border_bottom_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR;
|
||||
} else if (str_compare(attribute_name, "border_bottom_width") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH;
|
||||
} else if (str_compare(attribute_name, "border_left_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR;
|
||||
} else if (str_compare(attribute_name, "border_left_width") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH;
|
||||
} else if (str_compare(attribute_name, "padding") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_PADDING;
|
||||
} else if (str_compare(attribute_name, "padding_top") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_PADDING_TOP;
|
||||
} else if (str_compare(attribute_name, "padding_right") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_PADDING_RIGHT;
|
||||
} else if (str_compare(attribute_name, "padding_bottom") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_PADDING_BOTTOM;
|
||||
} else if (str_compare(attribute_name, "padding_left") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_PADDING_LEFT;
|
||||
} else if (str_compare(attribute_name, "scroll_style") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SCROLL_STYLE;
|
||||
} else if (str_compare(attribute_name, "scroll_x") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SCROLL_X;
|
||||
} else if (str_compare(attribute_name, "scroll_y") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SCROLL_Y;
|
||||
} else if (str_compare(attribute_name, "shadow_inner_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR;
|
||||
} else if (str_compare(attribute_name, "shadow_inner_angle") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE;
|
||||
} else if (str_compare(attribute_name, "shadow_inner_distance") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE;
|
||||
} else if (str_compare(attribute_name, "shadow_outer_color") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR;
|
||||
} else if (str_compare(attribute_name, "shadow_outer_angle") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE;
|
||||
} else if (str_compare(attribute_name, "shadow_outer_distance") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE;
|
||||
} else if (str_compare(attribute_name, "transition_animation") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION;
|
||||
} else if (str_compare(attribute_name, "transition_duration") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_TRANSITION_DURATION;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_attribute_parse_value(UIAttribute* attr, const char* attribute_name, const char* pos)
|
||||
{
|
||||
attr->attribute_id = (UIAttributeType) ui_attribute_type_to_id(attribute_name);
|
||||
char value[64];
|
||||
|
||||
str_copy_until(pos, value, "\r\n");
|
||||
|
||||
if (is_integer(value)) {
|
||||
attr->value_int = strtoul(value, NULL, 10);
|
||||
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_INT;
|
||||
} else if (is_float(value)) {
|
||||
attr->value_float = strtof(value, NULL);
|
||||
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_F32;
|
||||
} else if (is_hex_color(value)) {
|
||||
++pos; // Skip '#'
|
||||
hexstr_to_rgba(&attr->value_v4_f32, pos);
|
||||
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_V4_F32;
|
||||
} else {
|
||||
str_copy_until(attr->value_str, value, "\r\n");
|
||||
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_STR;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_theme_assign_f32(f32* a, const UIAttribute* attr, int32 variable_count, const EvaluatorVariable* variables)
|
||||
{
|
||||
if (attr->datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
*a = (f32) attr->value_int;
|
||||
} else if (attr->datatype == UI_ATTRIBUTE_DATA_TYPE_F32) {
|
||||
*a = (f32) attr->value_float;
|
||||
} else if (attr->datatype == UI_ATTRIBUTE_DATA_TYPE_STR) {
|
||||
char value[32];
|
||||
memcpy(value, attr->value_str, ARRAY_COUNT(attr->value_str));
|
||||
*a = (f32) evaluator_evaluate(value, variable_count, variables);
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_theme_assign_dimension(UIAttributeDimension* dimension, const UIAttribute* attr, int32 variable_count, const EvaluatorVariable* variables)
|
||||
{
|
||||
switch (attr->attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_X: {
|
||||
ui_theme_assign_f32(&dimension->dimension.x, attr, variable_count, variables);
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH: {
|
||||
ui_theme_assign_f32(&dimension->dimension.width, attr, variable_count, variables);
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_Y: {
|
||||
ui_theme_assign_f32(&dimension->dimension.y, attr, variable_count, variables);
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT: {
|
||||
ui_theme_assign_f32(&dimension->dimension.height, attr, variable_count, variables);
|
||||
} break;
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
21
ui/attribute/UIAttributeBackground.h
Normal file
21
ui/attribute/UIAttributeBackground.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef TOS_UI_ATTRIBUTE_BACKGROUND_STYLE_H
|
||||
#define TOS_UI_ATTRIBUTE_BACKGROUND_STYLE_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
enum UIBackgroundStyle : byte {
|
||||
UI_BACKGROUND_STYLE_NONE = 1 << 0,
|
||||
UI_BACKGROUND_STYLE_COLOR_IMG = 1 << 1, // 0 = color, 1 = img
|
||||
UI_BACKGROUND_STYLE_STRETCH = 1 << 2, // 0 = none, 1 = stretch
|
||||
UI_BACKGROUND_STYLE_REPEAT = 1 << 3, // 0 = none, 1 = repeat
|
||||
};
|
||||
|
||||
struct UIAttributeBackground {
|
||||
UIBackgroundStyle background_style;
|
||||
union {
|
||||
void* background_image;
|
||||
uint32 background_color;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
12
ui/attribute/UIAttributeBorder.h
Normal file
12
ui/attribute/UIAttributeBorder.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef TOS_UI_ATTRIBUTE_BORDER_H
|
||||
#define TOS_UI_ATTRIBUTE_BORDER_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
struct UIAttributeBorder {
|
||||
// 4 bits per side
|
||||
uint16 thickness;
|
||||
uint32 color;
|
||||
};
|
||||
|
||||
#endif
|
||||
56
ui/attribute/UIAttributeDimension.h
Normal file
56
ui/attribute/UIAttributeDimension.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef TOS_UI_ATTRIBUTE_DIMENSION_H
|
||||
#define TOS_UI_ATTRIBUTE_DIMENSION_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
enum UIDimensionFlag : byte {
|
||||
// Are the values relative (based on container) or absolute
|
||||
UI_DIMENSION_POS_RELATIVE = 1 << 0,
|
||||
UI_DIMENSION_DIM_RELATIVE = 1 << 1,
|
||||
|
||||
// Are the values dynamically calculated?
|
||||
UI_DIMENSION_POS_DYN = 1 << 2,
|
||||
UI_DIMENSION_DIM_DYN = 1 << 3,
|
||||
|
||||
// Are the values modifiable by the user (live)
|
||||
UI_DIMENSION_POS_MODIFIABLE = 1 << 4,
|
||||
UI_DIMENSION_DIM_MODIFIABLE = 1 << 5,
|
||||
};
|
||||
|
||||
struct UIAttributeDimension {
|
||||
// @see UIDimensionFlag
|
||||
byte flag;
|
||||
|
||||
// @see UIAlign
|
||||
byte alignment;
|
||||
|
||||
v4_f32 dimension;
|
||||
|
||||
/*
|
||||
// We commented this out since we will try to work around it for now by simply reloading the UI,
|
||||
// whenever a in-game window gets resized, same as resizing the actual game window
|
||||
union {
|
||||
struct {
|
||||
f32 x, y;
|
||||
f32 width, height;
|
||||
};
|
||||
|
||||
// We can never have position and dimension both be dynamic
|
||||
// This isn't really a technical limitation, it's more a "what happens in reality" kind of reason
|
||||
// This allows us to save 40 bytes
|
||||
struct {
|
||||
char x_str[24];
|
||||
char y_str[24];
|
||||
f32 width, height;
|
||||
};
|
||||
|
||||
struct {
|
||||
f32 x, y;
|
||||
char width_str[24];
|
||||
char height_str[24];
|
||||
};
|
||||
};
|
||||
*/
|
||||
};
|
||||
|
||||
#endif
|
||||
21
ui/attribute/UIAttributeFont.h
Normal file
21
ui/attribute/UIAttributeFont.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef TOS_UI_ATTRIBUTE_FONT_H
|
||||
#define TOS_UI_ATTRIBUTE_FONT_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
enum UIFontDecoration : byte {
|
||||
UI_FONT_DECORATION_UNDERLINE = 1 << 0,
|
||||
UI_FONT_DECORATION_ITALIC = 1 << 1,
|
||||
};
|
||||
|
||||
struct UIAttributeFont {
|
||||
f32 size;
|
||||
uint32 color;
|
||||
f32 weight;
|
||||
UIAttributeShadow shadow_outer;
|
||||
byte decoration;
|
||||
|
||||
// @todo family?
|
||||
};
|
||||
|
||||
#endif
|
||||
13
ui/attribute/UIAttributeShadow.h
Normal file
13
ui/attribute/UIAttributeShadow.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TOS_UI_ATTRIBUTE_SHADOW_H
|
||||
#define TOS_UI_ATTRIBUTE_SHADOW_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
struct UIAttributeShadow {
|
||||
f32 angle;
|
||||
uint32 color;
|
||||
byte fade;
|
||||
byte offset;
|
||||
};
|
||||
|
||||
#endif
|
||||
109
ui/attribute/UIAttributeType.h
Normal file
109
ui/attribute/UIAttributeType.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#ifndef TOS_UI_ATTRIBUTE_TYPE_H
|
||||
#define TOS_UI_ATTRIBUTE_TYPE_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
enum UIAttributeType : uint16 {
|
||||
UI_ATTRIBUTE_TYPE_NONE,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_TYPE,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_MIN_VALUE,
|
||||
UI_ATTRIBUTE_TYPE_MAX_VALUE,
|
||||
UI_ATTRIBUTE_TYPE_MAX_INPUT_LENGTH,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_X,
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_Y,
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_CONTENT,
|
||||
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_H,
|
||||
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_V,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_FONT_NAME,
|
||||
UI_ATTRIBUTE_TYPE_FONT_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_FONT_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_FONT_SIZE,
|
||||
UI_ATTRIBUTE_TYPE_FONT_WEIGHT,
|
||||
UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_ALIGN_H,
|
||||
UI_ATTRIBUTE_TYPE_ALIGN_V,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_ZINDEX,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_POSITION_X,
|
||||
UI_ATTRIBUTE_TYPE_POSITION_Y,
|
||||
UI_ATTRIBUTE_TYPE_PARENT,
|
||||
|
||||
// Sub styles for components
|
||||
// E.g. scroll bar usess style1 for background box and style2 for movable bar
|
||||
UI_ATTRIBUTE_TYPE_STYLE1,
|
||||
UI_ATTRIBUTE_TYPE_STYLE2,
|
||||
UI_ATTRIBUTE_TYPE_STYLE3,
|
||||
UI_ATTRIBUTE_TYPE_STYLE4,
|
||||
UI_ATTRIBUTE_TYPE_STYLE5,
|
||||
UI_ATTRIBUTE_TYPE_STYLE6,
|
||||
UI_ATTRIBUTE_TYPE_STYLE7,
|
||||
UI_ATTRIBUTE_TYPE_STYLE8,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_FOREGROUND_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_FOREGROUND_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_FOREGROUND_IMG,
|
||||
UI_ATTRIBUTE_TYPE_FOREGROUND_IMG_OPACITY,
|
||||
UI_ATTRIBUTE_TYPE_FOREGROUND_IMG_POSITION_V,
|
||||
UI_ATTRIBUTE_TYPE_FOREGROUND_IMG_POSITION_H,
|
||||
UI_ATTRIBUTE_TYPE_FOREGROUND_IMG_STYLE,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H,
|
||||
UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_BORDER_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_PADDING,
|
||||
UI_ATTRIBUTE_TYPE_PADDING_TOP,
|
||||
UI_ATTRIBUTE_TYPE_PADDING_RIGHT,
|
||||
UI_ATTRIBUTE_TYPE_PADDING_BOTTOM,
|
||||
UI_ATTRIBUTE_TYPE_PADDING_LEFT,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_SCROLL_STYLE,
|
||||
UI_ATTRIBUTE_TYPE_SCROLL_X,
|
||||
UI_ATTRIBUTE_TYPE_SCROLL_Y,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE,
|
||||
UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE,
|
||||
|
||||
// @todo This isn't enough, we can have many animations (position, size, colors, ...)
|
||||
// Maybe we need to define an animation child which overwrites the defined values
|
||||
// Maybe it should use the same system as state dependent values like hover, active, ...
|
||||
UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION,
|
||||
UI_ATTRIBUTE_TYPE_TRANSITION_DURATION,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_SIZE,
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
58
utils/PerformanceProfiler.h
Normal file
58
utils/PerformanceProfiler.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <x86intrin.h>
|
||||
|
||||
// @todo consider to log/ignore outliers
|
||||
uint64_t measure_cycles(int count, size_t (*func)(const char *), const char *str) {
|
||||
uint64_t start = __rdtsc();
|
||||
for (int = 0; i < count; ++i) {
|
||||
func(str);
|
||||
}
|
||||
uint64_t end = __rdtsc();
|
||||
|
||||
return end - start;
|
||||
}
|
||||
|
||||
void compare_strlen(int count, const char *str) {
|
||||
uint64_t normal_cycles = measure_cycles(count, strlen, str);
|
||||
uint64_t optimized_cycles = measure_cycles(count, strlen_optimized, str);
|
||||
|
||||
printf("String length: %zu\n", strlen(str));
|
||||
printf("Normal strlen cycles: %lu\n", normal_cycles);
|
||||
printf("Optimized strlen cycles: %lu\n", optimized_cycles);
|
||||
printf("Speedup: %.2fx\n", (double)normal_cycles / optimized_cycles);
|
||||
}
|
||||
|
||||
char* generate_random_string(size_t length) {
|
||||
char *str = (char *) malloc(length + 1);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
str[i] = (char) rand();
|
||||
}
|
||||
|
||||
str[length] = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int main() {
|
||||
srand((unsigned int) time(NULL));
|
||||
|
||||
size_t lengths[] = {5, 16, 64, 256, 1024, 4096};
|
||||
const size_t num_lengths = sizeof(lengths) / sizeof(lengths[0]);
|
||||
|
||||
for (size_t i = 0; i < num_lengths; i++) {
|
||||
size_t length = lengths[i];
|
||||
char *random_string = generate_random_string(length);
|
||||
|
||||
printf("Test %zu (length: %zu):\n", i + 1, length);
|
||||
compare_strlen(100000, random_string);
|
||||
|
||||
free(random_string);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
#include <ctype.h>
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../utils/TestUtils.h"
|
||||
|
||||
inline
|
||||
int32 utf8_encode(uint32 codepoint, char* out)
|
||||
|
|
@ -198,6 +199,132 @@ void wchar_to_char(const char* __restrict str, char* __restrict dest)
|
|||
*dest = '\0';
|
||||
}
|
||||
|
||||
inline
|
||||
bool is_float(const char* str) {
|
||||
bool has_dot = false;
|
||||
|
||||
if (*str == '-' || *str == '+') {
|
||||
str++;
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
if (*str == '.') {
|
||||
if (has_dot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
has_dot = true;
|
||||
} else if (*str < '0' || *str > '9') {
|
||||
return false;
|
||||
}
|
||||
|
||||
++str;
|
||||
}
|
||||
|
||||
return has_dot;
|
||||
}
|
||||
|
||||
inline
|
||||
bool is_integer(const char* str) {
|
||||
if (*str == '-' || *str == '+') {
|
||||
str++;
|
||||
}
|
||||
|
||||
if (*str == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_int = false;
|
||||
while (*str) {
|
||||
if (*str < '0' || *str > '9') {
|
||||
return false;
|
||||
}
|
||||
|
||||
++str;
|
||||
is_int = true;
|
||||
}
|
||||
|
||||
return is_int;
|
||||
}
|
||||
|
||||
inline
|
||||
bool is_hex_color(const char* str)
|
||||
{
|
||||
if (str[0] != '#') {
|
||||
return false;
|
||||
}
|
||||
|
||||
++str;
|
||||
|
||||
while (*str) {
|
||||
if ((*str < 'A' || *str > 'F')
|
||||
|| (*str < 'a' || *str > 'f')
|
||||
|| (*str < '0' || *str > '9')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++str;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline
|
||||
bool str_is_alpha(char str) {
|
||||
return (str < 'A' || str > 'F')
|
||||
|| (str < 'a' || str > 'f');
|
||||
}
|
||||
|
||||
inline
|
||||
bool str_is_num(char str) {
|
||||
return str < '0' || str > '9';
|
||||
}
|
||||
|
||||
inline
|
||||
bool str_is_alphanum(char str) {
|
||||
return (str < 'A' || str > 'F')
|
||||
|| (str < 'a' || str > 'f')
|
||||
|| (str < '0' || str > '9');
|
||||
}
|
||||
|
||||
inline
|
||||
size_t str_length(const char* str)
|
||||
{
|
||||
const char* s = str;
|
||||
|
||||
// Quick check for very short strings
|
||||
while ((uintptr_t) s % sizeof(uintptr_t) != 0) {
|
||||
if (*s == '\0') {
|
||||
return s - str;
|
||||
}
|
||||
|
||||
++s;
|
||||
}
|
||||
|
||||
// Process words at a time
|
||||
const uintptr_t* word_ptr = (const uintptr_t *) s;
|
||||
const uintptr_t word_mask = (uintptr_t) -1 / 0xFF; // 0x01010101...
|
||||
|
||||
while (true) {
|
||||
uintptr_t word = *word_ptr++;
|
||||
// Detect null byte using word-level operation
|
||||
if ((word - word_mask) & ~word & (word_mask << 7)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Backtrack to find the exact null byte
|
||||
s = (const char *) (word_ptr - 1);
|
||||
for (size_t i = 0; i < sizeof(uintptr_t); ++i) {
|
||||
if (s[i] == '\0') {
|
||||
return s + i - str;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline constexpr
|
||||
int64 str_to_int(const char* str)
|
||||
{
|
||||
|
|
@ -391,33 +518,6 @@ int32 is_eol(const char* str)
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline
|
||||
void str_copy_until(const char* __restrict src, char* __restrict dest, char delim)
|
||||
{
|
||||
while (*src != delim && *src != '\0') {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
*dest = '\0';
|
||||
}
|
||||
|
||||
inline
|
||||
void str_copy_until(const char* __restrict src, char* __restrict dest, const char* __restrict delim, int32 len)
|
||||
{
|
||||
while (*src != '\0') {
|
||||
for (int32 i = 0; i < len; ++i) {
|
||||
if (*src == delim[i]) {
|
||||
*dest = '\0';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
*dest = '\0';
|
||||
}
|
||||
|
||||
inline
|
||||
int32 str_copy_until(char* __restrict dest, const char* __restrict src, char delim)
|
||||
{
|
||||
|
|
@ -432,11 +532,32 @@ int32 str_copy_until(char* __restrict dest, const char* __restrict src, char del
|
|||
return len;
|
||||
}
|
||||
|
||||
// @todo Inconsistent parameter order of dest and src with other functions
|
||||
inline
|
||||
void str_copy_short(char* __restrict dest, const char* __restrict src, int32 length, char delim = '\0')
|
||||
void str_copy_until(const char* __restrict src, char* __restrict dest, const char* __restrict delim)
|
||||
{
|
||||
size_t len = strlen(delim);
|
||||
|
||||
while (*src != '\0') {
|
||||
for (int32 i = 0; i < len; ++i) {
|
||||
if (*src == delim[i]) {
|
||||
*dest = '\0';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*dest++ = *src;
|
||||
++src;
|
||||
}
|
||||
|
||||
*dest = '\0';
|
||||
}
|
||||
|
||||
inline
|
||||
void str_copy_short(char* __restrict dest, const char* __restrict src, int32 length)
|
||||
{
|
||||
int32 i = -1;
|
||||
while (*src != delim && ++i < length) {
|
||||
while (*src != '\0' && ++i < length) {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
|
|
@ -444,9 +565,9 @@ void str_copy_short(char* __restrict dest, const char* __restrict src, int32 len
|
|||
}
|
||||
|
||||
inline
|
||||
void str_copy_short(char* __restrict dest, const char* __restrict src, char delim = '\0')
|
||||
void str_copy_short(char* __restrict dest, const char* __restrict src)
|
||||
{
|
||||
while (*src != delim) {
|
||||
while (*src != '\0') {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
|
|
@ -454,7 +575,7 @@ void str_copy_short(char* __restrict dest, const char* __restrict src, char deli
|
|||
}
|
||||
|
||||
inline
|
||||
void str_copy_long(char* __restrict dest, const char* __restrict src, char delim = '\0')
|
||||
void str_copy_long(char* __restrict dest, const char* __restrict src)
|
||||
{
|
||||
char* d = dest;
|
||||
const char *s = src;
|
||||
|
|
@ -495,10 +616,12 @@ void str_copy_move_until(char** __restrict src, char* __restrict dest, char deli
|
|||
}
|
||||
|
||||
inline
|
||||
void str_copy_move_until(char** __restrict src, char* __restrict dest, const char* __restrict delim, int32 len)
|
||||
void str_copy_move_until(char** __restrict src, char* __restrict dest, const char* __restrict delim)
|
||||
{
|
||||
size_t len = strlen(delim);
|
||||
|
||||
while (**src != '\0') {
|
||||
for (int32 i = 0; i < len; ++i) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (**src == delim[i]) {
|
||||
*dest = '\0';
|
||||
return;
|
||||
|
|
@ -649,6 +772,12 @@ void str_insert(char* __restrict dst, size_t insert_pos, const char* __restrict
|
|||
memcpy(dst + insert_pos, src, src_length);
|
||||
}
|
||||
|
||||
inline
|
||||
void str_remove(char* __restrict dst, size_t remove_pos, size_t remove_length) {
|
||||
size_t src_length = strlen(dst);
|
||||
memmove(dst + remove_pos, dst + remove_pos + remove_length, src_length - (remove_pos + remove_length) + 1);
|
||||
}
|
||||
|
||||
inline
|
||||
char* strtok(char* str, const char* __restrict delim, char* *key) {
|
||||
char* result;
|
||||
|
|
@ -726,6 +855,7 @@ void create_const_name(unsigned char* name)
|
|||
*name = '\0';
|
||||
}
|
||||
|
||||
constexpr inline
|
||||
int32 str_compare(const char* str1, const char* str2)
|
||||
{
|
||||
byte c1, c2;
|
||||
|
|
@ -733,11 +863,7 @@ int32 str_compare(const char* str1, const char* str2)
|
|||
do {
|
||||
c1 = (byte) *str1++;
|
||||
c2 = (byte) *str2++;
|
||||
|
||||
if (c1 == '\0') {
|
||||
return c1 - c2;
|
||||
}
|
||||
} while (c1 == c2);
|
||||
} while (c1 == c2 && c1 != '\0');
|
||||
|
||||
return c1 - c2;
|
||||
}
|
||||
|
|
@ -900,6 +1026,16 @@ void str_move_to(char** str, char delim)
|
|||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void str_move_to_pos(const char** str, int32 pos)
|
||||
{
|
||||
if (pos >= 0) {
|
||||
*str += pos;
|
||||
} else {
|
||||
*str = OMS_MAX(*str + (strlen(*str) - pos), *str);
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void str_move_past(char** str, char delim)
|
||||
{
|
||||
|
|
@ -930,11 +1066,10 @@ bool str_is_comment(char* str)
|
|||
return (*str == '/' && str[1] == '/') || (*str == '/' && str[1] == '*');
|
||||
}
|
||||
|
||||
// @question Isn't that basically like move_to? Consider to unify
|
||||
inline
|
||||
void str_skip(char** str, char delim)
|
||||
{
|
||||
while (**str == delim) {
|
||||
while (**str && **str == delim) {
|
||||
++(*str);
|
||||
}
|
||||
}
|
||||
|
|
@ -942,7 +1077,7 @@ void str_skip(char** str, char delim)
|
|||
inline
|
||||
void str_skip_whitespace(char** str)
|
||||
{
|
||||
while (**str == ' ' || **str == '\t') {
|
||||
while (**str && (**str == ' ' || **str == '\t')) {
|
||||
++(*str);
|
||||
}
|
||||
}
|
||||
|
|
@ -1024,6 +1159,55 @@ void str_pad(const char* input, char* output, char pad, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
inline
|
||||
int32 float_to_str(f64 value, char* buffer, int32 precision = 5)
|
||||
{
|
||||
ASSERT_SIMPLE(precision < 6);
|
||||
|
||||
char* start = buffer;
|
||||
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
value = -value;
|
||||
}
|
||||
|
||||
static const float powers_of_ten[] = {
|
||||
1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f, 100000.0f
|
||||
};
|
||||
|
||||
f32 scale = powers_of_ten[precision];
|
||||
value = OMS_ROUND_POSITIVE(value * scale) / scale;
|
||||
|
||||
// Handle integer part
|
||||
int32 int_part = (int32) value;
|
||||
f64 frac_part = value - int_part;
|
||||
|
||||
char temp[20];
|
||||
int32 index = 0;
|
||||
|
||||
do {
|
||||
temp[index++] = (int_part % 10) + '0';
|
||||
int_part /= 10;
|
||||
} while (int_part > 0);
|
||||
|
||||
while (index > 0) {
|
||||
*buffer++ = temp[--index];
|
||||
}
|
||||
|
||||
// Handle fractional part
|
||||
if (precision > 0) {
|
||||
*buffer++ = '.';
|
||||
while (precision--) {
|
||||
frac_part *= 10;
|
||||
int32 digit = (int32) frac_part;
|
||||
*buffer++ = (char) (digit + '0');
|
||||
frac_part -= digit;
|
||||
}
|
||||
}
|
||||
|
||||
return (int32) (buffer - start);
|
||||
}
|
||||
|
||||
void sprintf_fast(char* __restrict buffer, const char* __restrict format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
|
@ -1077,46 +1261,7 @@ void sprintf_fast(char* __restrict buffer, const char* __restrict format, ...) {
|
|||
format = prec_ptr - 1;
|
||||
}
|
||||
|
||||
if (val < 0) {
|
||||
*buffer++ = '-';
|
||||
val = -val;
|
||||
}
|
||||
|
||||
if (precision < 6) {
|
||||
static const float powers_of_ten[] = {
|
||||
1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f, 100000.0f
|
||||
};
|
||||
|
||||
f32 scale = powers_of_ten[precision];
|
||||
val = OMS_ROUND_POSITIVE(val * scale) / scale;
|
||||
}
|
||||
|
||||
// Handle integer part
|
||||
int32 int_part = (int32) val;
|
||||
f64 frac_part = val - int_part;
|
||||
|
||||
char temp[20];
|
||||
int32 index = 0;
|
||||
|
||||
do {
|
||||
temp[index++] = (int_part % 10) + '0';
|
||||
int_part /= 10;
|
||||
} while (int_part > 0);
|
||||
|
||||
while (index > 0) {
|
||||
*buffer++ = temp[--index];
|
||||
}
|
||||
|
||||
// Handle fractional part
|
||||
if (precision > 0) {
|
||||
*buffer++ = '.';
|
||||
while (precision--) {
|
||||
frac_part *= 10;
|
||||
int32 digit = (int32) frac_part;
|
||||
*buffer++ = (char) (digit + '0');
|
||||
frac_part -= digit;
|
||||
}
|
||||
}
|
||||
buffer += float_to_str(val, buffer, precision);
|
||||
} break;
|
||||
default: {
|
||||
// Handle unknown format specifiers
|
||||
|
|
@ -1189,46 +1334,7 @@ void sprintf_fast_iter(char* buffer, const char* format, ...) {
|
|||
format = prec_ptr - 1;
|
||||
}
|
||||
|
||||
if (val < 0) {
|
||||
*buffer++ = '-';
|
||||
val = -val;
|
||||
}
|
||||
|
||||
if (precision < 6) {
|
||||
static const float powers_of_ten[] = {
|
||||
1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f, 100000.0f
|
||||
};
|
||||
|
||||
f32 scale = powers_of_ten[precision];
|
||||
val = OMS_ROUND_POSITIVE(val * scale) / scale;
|
||||
}
|
||||
|
||||
// Handle integer part
|
||||
int32 int_part = (int32) val;
|
||||
f64 frac_part = val - int_part;
|
||||
|
||||
char temp[20];
|
||||
int32 index = 0;
|
||||
|
||||
do {
|
||||
temp[index++] = (int_part % 10) + '0';
|
||||
int_part /= 10;
|
||||
} while (int_part > 0);
|
||||
|
||||
while (index > 0) {
|
||||
*buffer++ = temp[--index];
|
||||
}
|
||||
|
||||
// Handle fractional part
|
||||
if (precision > 0) {
|
||||
*buffer++ = '.';
|
||||
while (precision--) {
|
||||
frac_part *= 10;
|
||||
int32 digit = (int32) frac_part;
|
||||
*buffer++ = (char) (digit + '0');
|
||||
frac_part -= digit;
|
||||
}
|
||||
}
|
||||
buffer += float_to_str(val, buffer, precision);
|
||||
} break;
|
||||
default: {
|
||||
// Handle unknown format specifiers
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user