绘制文本编辑框中的光标等的思路
最近在用gdiplus api绘制文本框,就想一步步来,先封装个能有光标和选中背景的文本显示组件,在此基础上实现文本编辑会好弄点。另一方面套两层可以实现文字边距,而且计算坐标的时候不用再把边距给计算进去,不容易搞晕。
一开始为了效率考虑就使用二分法来确定文字中的坐标,但由于多字节字符串(汉字占两个字节),导致二分法截止条件设置错误,好几天了也没调试通,
网上搜了不少也根本没找到好的解决办法,今天终于调通了
发在论坛里,以前做过的帮我看看还有没有问题,也给需要的坛友提供点思路,以后自己也好找到
打的32位exe也不知道64位系统能不能用
luia.zip
(123.48 KB)
程序代码:
/** * @brief 获取字符串中某个index的子串占位rect * @param ctx ui上下文 * @param wgt widget ui组件 * @param style 文字样式(和draw方法必须相同,否则会不一致) * @param indx 字符在字符串中的index * @return 占位rect */ static gui_rect_t gui_itxt_indx_rect(gui_context_p ctx, gui_widget_p wgt, gui_text_style_t style, int indx) { char* str = wgt->text; int num = indx+1; char txt[num+1]; int j; for(j = 0; j<num; j++){ txt[j] = *(str+j); } txt[num] = '\0'; gui_rect_t rect = gui_base_measuretext(ctx->context, wgt->px, wgt->py, 65535, wgt->ph, txt, style); return rect; } /** * @brief 获取鼠标在文本中的位置和字符index(用于绘制文字中的光标和选中背景) * @param ctx ui 上下文 * @param wgt widget ui组件 * @param x 点击坐标x * @param y 点击坐标x * @param style 文字样式(和draw方法必须相同,否则会不一致) * @param indx 用于返回字符在字符串中的index * @param rec 占位rect */ static void gui_itxt_measure_pos(gui_context_p ctx, gui_widget_p wgt, int x, int y, gui_text_style_t style, int* indx, gui_rect_t* rec) { gui_rect_t rect = {0}; char* str = wgt->text; if(!str || strlen(str)<1)return; int m = 0; int n = strlen(str)-1; int i; byte bol = 0;// while(n - m > 4){//因为是多字节字符的字符串,汉字占两个字节,两个相邻汉字差4个char i=0; int mid = (m+n)/2; while(i <= mid){ if(*(str+i)<0){ if(i == mid){i--;break;}//汉字不能完整舍弃 if(i == mid-1){i++;break;}//汉字刚好占满结束 i+=2; }else{ if(i == mid)break; i++; } } rect = gui_itxt_indx_rect(ctx, wgt, style, i); if(rect.x+rect.w > x){ n = i; }else if(rect.x+rect.w < x){ m = i+1; }else{//点击位置正好为光标位置 bol = 1; break; } } if(bol){ rec->x = rect.x; rec->y = rect.y; rec->w = rect.w-2;//-2为一点差值,根据实际测试得来 rec->h = rect.h; *indx = i; return; } while(m <= n){//小于4个char逐char定位 byte bla = *(str+m) < 0;//同样需要处理汉字 int rlm = bla?m+1:m; rect = gui_itxt_indx_rect(ctx, wgt, style, rlm); *indx = rlm; if(x < rect.x+rect.w){ if(m == 0){//第0个字符处理 int per = rect.w/2;//用于处理电子字符左右半边,定位该字符前后 if(x <= rect.x + per){ rect.w=3; *indx = -1; } }else{ gui_rect_t recta = gui_itxt_indx_rect(ctx, wgt, style, m-1); int pera = (rect.w - recta.w)/2;//用于处理电子字符左右半边,定位该字符前后 if(x <= recta.x + recta.w + pera - 2){ rect = recta; *indx = m-1; } } break; } if(bla)m+=2; else m++; } rec->x = rect.x; rec->y = rect.y; rec->w = rect.w-2;//-2为一点差值,根据实际测试得来 rec->h = rect.h; }