1717#include "render_bindings.h"
1818#include "tmpbuffer.h"
1919
20+ #define PIXEL_SCALE 256
21+
2022struct text {
2123 struct draw_primitive_external header ;
2224 int codepoint ;
@@ -72,8 +74,8 @@ submit(lua_State *L, void *m_, struct draw_primitive *prim, int n) {
7274 // todo: support multiply srbuffer
7375 luaL_error (L , "sr buffer is full" );
7476 }
75- tmp [count ].x = (float )p -> x / 256.0f ;
76- tmp [count ].y = (float )p -> y / 256.0f ;
77+ tmp [count ].x = (float )p -> x / PIXEL_SCALE ;
78+ tmp [count ].y = (float )p -> y / PIXEL_SCALE ;
7779 tmp [count ].sr_index = (float )sr_index ;
7880 ++ count ;
7981 } else {
@@ -372,6 +374,8 @@ struct layout {
372374 int height ;
373375 int top ;
374376 int line_height ;
377+ int line_gap ;
378+ int text_height ;
375379 int ascent ;
376380 int decent ;
377381 struct position pos [];
@@ -398,10 +402,10 @@ newline(struct block_context *ctx, struct text_primitive * prim, int n, struct l
398402 int align = ctx -> alignment & ALIGNMENT_MASK ;
399403 switch (align ) {
400404 case ALIGNMENT_CENTER :
401- offx = (ctx -> width - line_width ) / 2 * 256 ;
405+ offx = (ctx -> width - line_width ) / 2 * PIXEL_SCALE ;
402406 break ;
403407 case ALIGNMENT_RIGHT :
404- offx = (ctx -> width - line_width ) * 256 ;
408+ offx = (ctx -> width - line_width ) * PIXEL_SCALE ;
405409 break ;
406410 }
407411
@@ -414,7 +418,7 @@ newline(struct block_context *ctx, struct text_primitive * prim, int n, struct l
414418 }
415419 if (pos != NULL ) {
416420 for (i = from ;i < n ;i ++ ) {
417- pos -> pos [i ].x += offx / 256 ;
421+ pos -> pos [i ].x += offx / PIXEL_SCALE ;
418422 }
419423 }
420424 }
@@ -502,8 +506,10 @@ ltext_(lua_State *L, int gen_layout) {
502506 pos -> n = 0 ;
503507 pos -> width = ctx .width ;
504508 pos -> height = ctx .height ;
509+ pos -> text_height = 0 ;
505510 pos -> top = 0 ;
506511 pos -> line_height = ctx .ascent + ctx .decent - gap ;
512+ pos -> line_gap = gap ;
507513 pos -> ascent = ctx .ascent ;
508514 pos -> decent = ctx .decent ;
509515 } else {
@@ -557,8 +563,8 @@ ltext_(lua_State *L, int gen_layout) {
557563 }
558564
559565 if (prim ) {
560- prim [n ].pos .x = ctx .x * 256 ;
561- prim [n ].pos .y = ctx .y * 256 + dy * 256 ;
566+ prim [n ].pos .x = ctx .x * PIXEL_SCALE ;
567+ prim [n ].pos .y = ( ctx .y + dy ) * PIXEL_SCALE ;
562568 prim [n ].pos .sr = 0 ;
563569 prim [n ].pos .sprite = - material_id ;
564570 } else {
@@ -578,8 +584,8 @@ ltext_(lua_State *L, int gen_layout) {
578584 if (newline (& ctx , prim , n , pos ))
579585 break ;
580586 if (prim ) {
581- prim [n ].pos .x = ctx .x * 256 ;
582- prim [n ].pos .y = ctx .y * 256 + dy * 256 ;
587+ prim [n ].pos .x = ctx .x * PIXEL_SCALE ;
588+ prim [n ].pos .y = ( ctx .y + dy ) * PIXEL_SCALE ;
583589 } else {
584590 struct position * p = & pos -> pos [n ];
585591 p -> x = ctx .x ;
@@ -614,10 +620,10 @@ ltext_(lua_State *L, int gen_layout) {
614620 int valign = ctx .alignment & VALIGNMENT_MASK ;
615621 switch (valign ) {
616622 case VALIGNMENT_CENTER :
617- offy = (ctx .height - height ) / 2 * 256 ;
623+ offy = (ctx .height - height ) / 2 * PIXEL_SCALE ;
618624 break ;
619625 case VALIGNMENT_BOTTOM :
620- offy = (ctx .height - height ) * 256 ;
626+ offy = (ctx .height - height ) * PIXEL_SCALE ;
621627 break ;
622628 default :
623629 offy = 0 ;
@@ -635,10 +641,10 @@ ltext_(lua_State *L, int gen_layout) {
635641 } else {
636642 if (n > 0 )
637643 ++ n ;
638- pos -> height = height ;
644+ pos -> text_height = height ;
639645 pos -> n = n ;
640646 if (offy != 0 ) {
641- int offset = offy / 256 - ctx .ascent ;
647+ int offset = offy / PIXEL_SCALE - ctx .ascent ;
642648 for (i = 0 ;i < n ;i ++ ) {
643649 pos -> pos [i ].y += offset ;
644650 }
@@ -679,6 +685,61 @@ ltext_cursor(lua_State *L) {
679685 return 6 ;
680686}
681687
688+ static int
689+ get_pos (lua_State * L , int index ) {
690+ int isnum ;
691+ int r = lua_tointegerx (L , index , & isnum );
692+ if (isnum ) {
693+ return r ;
694+ }
695+ return (int )(luaL_checknumber (L , index ) + 0.5 );
696+ }
697+
698+ static int
699+ bsearch_pos (struct layout * pos , int x , int y ) {
700+ int from = 0 ;
701+ int to = pos -> n - 1 ;
702+ int advance_y = pos -> line_height + pos -> line_gap ;
703+ while (from < to ) {
704+ int mid = from + (to - from ) / 2 ;
705+ struct position * p = & pos -> pos [mid ];
706+ if (y < p -> y ) {
707+ to = mid ;
708+ } else if (y >= p -> y + advance_y ) {
709+ from = mid + 1 ;
710+ } else if (x < p -> x ) {
711+ to = mid ;
712+ } else if (x >= p -> x + p -> w ) {
713+ from = mid + 1 ;
714+ } else {
715+ return mid ;
716+ }
717+ }
718+ return from ;
719+ }
720+
721+ static int
722+ ltext_hit_test (lua_State * L ) {
723+ struct layout * pos = (struct layout * )lua_touserdata (L , 1 );
724+ int x = get_pos (L , 2 );
725+ int y = get_pos (L , 3 );
726+ int top = pos -> top + pos -> ascent ;
727+ if (y < top ) {
728+ lua_pushinteger (L , 0 );
729+ lua_pushboolean (L , y < 0 ); // out of box
730+ return 2 ;
731+ }
732+ if (y - top >= pos -> text_height ) {
733+ lua_pushinteger (L , pos -> n - 1 );
734+ lua_pushboolean (L , y >= pos -> height ); // out of box
735+ return 2 ;
736+ }
737+ int index = bsearch_pos (pos , x , y );
738+ lua_pushinteger (L , index );
739+ lua_pushboolean (L , (x < 0 || x >= pos -> width ));
740+ return 2 ;
741+ }
742+
682743static uint32_t
683744parse_alignment (lua_State * L , int index ) {
684745 const char * alignment_string = lua_tostring (L , index );
@@ -764,6 +825,7 @@ luaopen_material_text(lua_State *L) {
764825 if (luaL_newmetatable (L , "SOLUNA_TEXT_LAYOUT" )) {
765826 luaL_Reg meta [] = {
766827 { "cursor" , ltext_cursor },
828+ { "hit_test" , ltext_hit_test },
767829 { NULL , NULL },
768830 };
769831 luaL_setfuncs (L , meta , 0 );
0 commit comments