Fri, 12 Apr 2024 22:03:15 +0200
improve ui/text.h interface a lot
src/ascension/scene.h | file | annotate | diff | comparison | revisions | |
src/ascension/ui/text.h | file | annotate | diff | comparison | revisions | |
src/ascension/utils.h | file | annotate | diff | comparison | revisions | |
src/context.c | file | annotate | diff | comparison | revisions | |
src/error.c | file | annotate | diff | comparison | revisions | |
src/text.c | file | annotate | diff | comparison | revisions | |
test/snake.c | file | annotate | diff | comparison | revisions |
1.1 --- a/src/ascension/scene.h Wed Apr 10 19:43:34 2024 +0200 1.2 +++ b/src/ascension/scene.h Fri Apr 12 22:03:15 2024 +0200 1.3 @@ -65,8 +65,15 @@ 1.4 bool hidden; 1.5 bool need_graphics_update; 1.6 bool need_transform_update; 1.7 + /** 1.8 + * Custom flags for this node. 1.9 + * The #ASC_SCENE_NODE_FLAGS_MASK bits are reserved for general flags. 1.10 + */ 1.11 + uint32_t flags; 1.12 }; 1.13 1.14 +#define ASC_SCENE_NODE_FLAGS_MASK 0xFF000000 1.15 + 1.16 /** 1.17 * Place this as first member of a structure that shall be used as a scene node. 1.18 */
2.1 --- a/src/ascension/ui/text.h Wed Apr 10 19:43:34 2024 +0200 2.2 +++ b/src/ascension/ui/text.h Fri Apr 12 22:03:15 2024 +0200 2.3 @@ -31,17 +31,27 @@ 2.4 #include "font.h" 2.5 #include "../scene.h" 2.6 #include "../texture.h" 2.7 +#include "../utils.h" 2.8 + 2.9 +#include <cx/string.h> 2.10 2.11 typedef struct AscText { 2.12 extend_asc_scene_node; 2.13 - char *text; 2.14 + cxmutstr text; 2.15 AscFont const *font; 2.16 asc_col4i color; 2.17 - unsigned max_width; 2.18 - bool centered; 2.19 + unsigned short max_width; 2.20 + unsigned short offx; 2.21 AscTexture tex; 2.22 } AscText; 2.23 2.24 +enum asc_text_alignment { 2.25 + ASC_TEXT_ALIGN_LEFT = 0x00, 2.26 + ASC_TEXT_ALIGN_CENTERED = 0x01, 2.27 + ASC_TEXT_ALIGN_RIGHT = 0x02 2.28 +}; 2.29 +#define ASC_TEXT_ALIGNMENT_MASK 0x03 2.30 +#define ASC_TEXT_CENTERED_FLAG 0x04 2.31 2.32 /** 2.33 * Creates a text node. 2.34 @@ -59,11 +69,65 @@ 2.35 AscSceneNode *asc_text(int x, int y, char const* text); 2.36 2.37 /** 2.38 - * Provides access to the text data fields. 2.39 + * Sets the text alignment. 2.40 * 2.41 - * @param node scene node created by asc_text() 2.42 + * @param node the text node 2.43 */ 2.44 -#define asc_text_data(node) ((AscText*)node) 2.45 +__attribute__((__nonnull__)) 2.46 +static inline void asc_text_alignment( 2.47 + AscSceneNode *node, 2.48 + enum asc_text_alignment alignment 2.49 +) { 2.50 + asc_set_flag_masked(node->flags, ASC_TEXT_ALIGNMENT_MASK, alignment); 2.51 + asc_node_update(node); 2.52 +} 2.53 + 2.54 +/** 2.55 + * Decides whether the text shall be centered. 2.56 + * 2.57 + * @param node the text node 2.58 + * @param centered true when the text shall be centered 2.59 + */ 2.60 +__attribute__((__nonnull__)) 2.61 +static inline void asc_text_centered( 2.62 + AscSceneNode *node, 2.63 + bool centered 2.64 +) { 2.65 + if (centered) { 2.66 + asc_clear_flag(node->flags, ASC_TEXT_ALIGN_CENTERED); 2.67 + } else { 2.68 + asc_set_flag(node->flags, ASC_TEXT_ALIGN_CENTERED); 2.69 + } 2.70 + asc_node_update(node); 2.71 +} 2.72 + 2.73 +/** 2.74 + * Sets a new maximum width for the text. 2.75 + * 2.76 + * @param node the text node 2.77 + * @param max_width the new maximum width 2.78 + */ 2.79 +__attribute__((__nonnull__)) 2.80 +static inline void asc_text_max_width( 2.81 + AscSceneNode *node, 2.82 + unsigned max_width 2.83 +) { 2.84 + ((AscText*)node)->max_width = max_width; 2.85 + asc_node_update(node); 2.86 +} 2.87 + 2.88 +/** 2.89 + * 2.90 + * @param node 2.91 + * @param format 2.92 + * @param ... 2.93 + */ 2.94 +__attribute__((__nonnull__)) 2.95 +void asc_text_printf( 2.96 + AscSceneNode *node, 2.97 + char const* format, 2.98 + ... 2.99 +); 2.100 2.101 /** 2.102 * Releases all the memory of this node.
3.1 --- a/src/ascension/utils.h Wed Apr 10 19:43:34 2024 +0200 3.2 +++ b/src/ascension/utils.h Fri Apr 12 22:03:15 2024 +0200 3.3 @@ -30,17 +30,11 @@ 3.4 3.5 #include <stdbool.h> 3.6 3.7 -static inline bool asc_test_flag(unsigned int reg, int flag) { 3.8 - return (reg & flag) == flag; 3.9 -} 3.10 - 3.11 -static inline void asc_set_flag(unsigned int *reg, int flag) { 3.12 - *reg |= flag; 3.13 -} 3.14 - 3.15 -static inline void asc_clear_flag(unsigned int *reg, int flag) { 3.16 - *reg &= ~flag; 3.17 -} 3.18 +#define asc_test_flag(reg, flag) ((reg & flag) == flag) 3.19 +#define asc_test_flag_masked(reg, mask, flag) ((reg & mask) == flag) 3.20 +#define asc_clear_flag(reg, flag) (reg &= ~flag) 3.21 +#define asc_set_flag(reg, flag) (reg |= flag) 3.22 +#define asc_set_flag_masked(reg, mask, flag) (reg = (reg & ~mask) | flag) 3.23 3.24 #endif /* ASCENSION_UTILS_H */ 3.25
4.1 --- a/src/context.c Wed Apr 10 19:43:34 2024 +0200 4.2 +++ b/src/context.c Fri Apr 12 22:03:15 2024 +0200 4.3 @@ -66,7 +66,7 @@ 4.4 } 4.5 SDL_ClearError(); 4.6 asc_context.total_nanos = asc_nanos(); 4.7 - asc_set_flag(&asc_context.flags, ASC_FLAG_INITILIZED); 4.8 + asc_set_flag(asc_context.flags, ASC_FLAG_INITILIZED); 4.9 asc_dprintf("Ascension context initialized."); 4.10 } 4.11 4.12 @@ -104,7 +104,7 @@ 4.13 while (SDL_PollEvent(&event)) { 4.14 switch (event.type) { 4.15 case SDL_QUIT: 4.16 - asc_set_flag(&asc_context.flags, ASC_FLAG_QUIT); 4.17 + asc_set_flag(asc_context.flags, ASC_FLAG_QUIT); 4.18 break; 4.19 case SDL_WINDOWEVENT: { 4.20 if (event.window.event == SDL_WINDOWEVENT_RESIZED)
5.1 --- a/src/error.c Wed Apr 10 19:43:34 2024 +0200 5.2 +++ b/src/error.c Fri Apr 12 22:03:15 2024 +0200 5.3 @@ -51,7 +51,7 @@ 5.4 cxBufferWrite(text.ptr, 1, text.length, buf); 5.5 cxBufferPut(buf, '\n'); 5.6 5.7 - asc_set_flag(&asc_context.flags, ASC_FLAG_HAS_ERROR); 5.8 + asc_set_flag(asc_context.flags, ASC_FLAG_HAS_ERROR); 5.9 } 5.10 5.11 bool asc_has_error(void) { 5.12 @@ -68,7 +68,7 @@ 5.13 5.14 void asc_clear_error(void) { 5.15 cxBufferClear(&asc_context.error_buffer); 5.16 - asc_clear_flag(&asc_context.flags, ASC_FLAG_HAS_ERROR); 5.17 + asc_clear_flag(asc_context.flags, ASC_FLAG_HAS_ERROR); 5.18 } 5.19 5.20 void asc_error_gl(unsigned code, cxstring message) {
6.1 --- a/src/text.c Wed Apr 10 19:43:34 2024 +0200 6.2 +++ b/src/text.c Fri Apr 12 22:03:15 2024 +0200 6.3 @@ -30,6 +30,8 @@ 6.4 #include "ascension/error.h" 6.5 #include "ascension/shader.h" 6.6 6.7 +#include <cx/printf.h> 6.8 + 6.9 #include <GL/glew.h> 6.10 6.11 static void asc_text_draw(AscText const *node) { 6.12 @@ -58,19 +60,27 @@ 6.13 6.14 // Render text onto a surface 6.15 TTF_Font *font = asc_font_cache_validate(node->font)->ptr; 6.16 + static int alignments[] = { 6.17 + TTF_WRAPPED_ALIGN_LEFT, 6.18 + TTF_WRAPPED_ALIGN_CENTER, 6.19 + TTF_WRAPPED_ALIGN_RIGHT 6.20 + }; 6.21 TTF_SetFontWrappedAlign( 6.22 - font, node->centered ? 6.23 - TTF_WRAPPED_ALIGN_CENTER : TTF_WRAPPED_ALIGN_LEFT 6.24 - ); 6.25 + font, alignments[node->base.flags & ASC_TEXT_ALIGNMENT_MASK]); 6.26 SDL_Surface *surface = TTF_RenderUTF8_Blended_Wrapped( 6.27 - font, node->text, asc_col_sdl(node->color), node->max_width 6.28 + font, node->text.ptr, asc_col_sdl(node->color), node->max_width 6.29 ); 6.30 if (surface == NULL) { 6.31 asc_error(SDL_GetError()); 6.32 return; 6.33 } 6.34 asc_set_scale2d(&node->base, surface->w, surface->h); 6.35 - asc_update_transform((AscSceneNode *) node); 6.36 + if (asc_test_flag(node->base.flags, ASC_TEXT_CENTERED_FLAG)) { 6.37 + unsigned short newoffx = surface->w / 2; 6.38 + asc_vec2i pos = asc_get_position2d(&node->base); 6.39 + asc_set_position2d(&node->base, pos.x + node->offx - newoffx, pos.y); 6.40 + node->offx = newoffx; 6.41 + } 6.42 6.43 // Transfer Image Data 6.44 asc_texture_from_surface(&node->tex, surface); 6.45 @@ -80,6 +90,7 @@ 6.46 } 6.47 6.48 AscSceneNode *asc_text(int x, int y, char const *text) { 6.49 + // TODO: implement fancy struct "named param" initialization 6.50 AscText *node = calloc(1, sizeof(AscText)); 6.51 6.52 node->base.render_group = ASC_RENDER_GROUP_SPRITE_BLEND; 6.53 @@ -91,8 +102,10 @@ 6.54 node->base.position.y = (float) y; 6.55 node->font = asc_context.active_font; 6.56 node->color = asc_context.ink; 6.57 - if (text != NULL) { 6.58 - node->text = strdup(text); 6.59 + if (text == NULL) { 6.60 + node->text = cx_mutstr(strdup(" ")); 6.61 + } else { 6.62 + node->text = cx_mutstr(strdup(text)); 6.63 } 6.64 6.65 // initialize 6.66 @@ -103,6 +116,19 @@ 6.67 6.68 void asc_text_free(AscText *node) { 6.69 asc_texture_destroy(&node->tex); 6.70 - free(node->text); 6.71 + cx_strfree(&node->text); 6.72 free(node); 6.73 -} 6.74 \ No newline at end of file 6.75 +} 6.76 + 6.77 +void asc_text_printf( 6.78 + AscSceneNode *node, 6.79 + char const* format, 6.80 + ... 6.81 +) { 6.82 + cxmutstr text = ((AscText*)node)->text; 6.83 + va_list ap; 6.84 + va_start(ap, format); 6.85 + cx_vsprintf(&text.ptr, &text.length, format, ap); 6.86 + va_end(ap); 6.87 + asc_node_update(node); 6.88 +}
7.1 --- a/test/snake.c Wed Apr 10 19:43:34 2024 +0200 7.2 +++ b/test/snake.c Fri Apr 12 22:03:15 2024 +0200 7.3 @@ -41,8 +41,7 @@ 7.4 fps /= asc_context.frame_nanos; 7.5 if (fps != last_fps) { 7.6 last_fps = fps; 7.7 - snprintf(asc_text_data(node)->text, 11, "%"PRIu64" FPS", fps); 7.8 - asc_node_update(node); 7.9 + asc_text_printf(node, "%"PRIu64" FPS", fps); 7.10 } 7.11 } 7.12 } 7.13 @@ -50,9 +49,9 @@ 7.14 static void create_fps_counter(void) { 7.15 asc_set_font(asc_font(ASC_FONT_REGULAR, 24)); 7.16 asc_ink_rgb(255, 0, 0); 7.17 - AscSceneNode* node = asc_text(10, 10, "XXXXXXX FPS"); 7.18 + AscSceneNode* node = asc_text(10, 10, NULL); 7.19 asc_scene_add_behavior(node, update_fps_counter); 7.20 - asc_scene_node_link(asc_window_active->ui, node); 7.21 + asc_add_ui_node(node); 7.22 } 7.23 7.24 static void update_score_counter(AscSceneNode *node) { 7.25 @@ -73,7 +72,7 @@ 7.26 asc_ink_rgb(0, 255, 0); 7.27 AscSceneNode* node = asc_text(0, 0, "Score: 0"); 7.28 asc_scene_add_behavior(node, update_score_counter); 7.29 - asc_scene_node_link(asc_window_active->ui, node); 7.30 + asc_add_ui_node(node); 7.31 } 7.32 7.33 int main(int argc, char** argv) { 7.34 @@ -94,7 +93,6 @@ 7.35 create_fps_counter(); 7.36 create_score_counter(); 7.37 7.38 - 7.39 // Main Loop 7.40 do { 7.41 // quit application on any error