diff options
Diffstat (limited to 'scene/gui/rich_text_label.cpp')
-rw-r--r-- | scene/gui/rich_text_label.cpp | 279 |
1 files changed, 252 insertions, 27 deletions
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 94df42fc8f..7a607786ee 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -58,7 +58,7 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item* p_item) { } -void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p_mode,const Ref<Font> &p_base_font,const Color &p_base_color,const Point2i& p_click_pos,Item **r_click_item,int *r_click_char,bool *r_outside) { +void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p_mode,const Ref<Font> &p_base_font,const Color &p_base_color,const Point2i& p_click_pos,Item **r_click_item,int *r_click_char,bool *r_outside,int p_char_count) { RID ci; if (r_outside) @@ -78,6 +78,8 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p int margin=_find_margin(it,p_base_font); Align align=_find_align(it);; int line=0; + int spaces=0; + if (p_mode!=PROCESS_CACHE) { @@ -85,7 +87,14 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p line_ofs = l.offset_caches[line]; } + if (p_mode==PROCESS_CACHE) { + l.offset_caches.clear(); + l.height_caches.clear(); + l.char_count=0; + } + int wofs=margin; + int spaces_size=0; if (p_mode!=PROCESS_CACHE && align!=ALIGN_FILL) wofs+=line_ofs; @@ -101,7 +110,6 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p Variant meta; - #define NEW_LINE \ {\ if (p_mode!=PROCESS_CACHE) {\ @@ -117,12 +125,15 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p case ALIGN_LEFT: l.offset_caches.push_back(0); break;\ case ALIGN_CENTER: l.offset_caches.push_back(((p_width-margin)-used)/2); break;\ case ALIGN_RIGHT: l.offset_caches.push_back(((p_width-margin)-used)); break;\ - case ALIGN_FILL: l.offset_caches.push_back(p_width-wofs); break;\ + case ALIGN_FILL: l.offset_caches.push_back((p_width-margin)-used+spaces_size); break;\ }\ l.height_caches.push_back(line_height);\ + l.space_caches.push_back(spaces);\ }\ y+=line_height+get_constant(SceneStringNames::get_singleton()->line_separation);\ line_height=0;\ + spaces=0;\ + spaces_size=0;\ wofs=begin;\ if (p_mode!=PROCESS_CACHE) {\ lh=line<l.height_caches.size()?l.height_caches[line]:1;\ @@ -138,6 +149,10 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p #define ENSURE_WIDTH(m_width) \ if (wofs + m_width > p_width) {\ + if (p_mode==PROCESS_CACHE) {\ + if (spaces>0) \ + spaces-=1;\ + }\ if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh && p_click_pos.x>wofs) {\ if (r_outside) *r_outside=true; \ *r_click_item=it;\ @@ -203,33 +218,76 @@ if (m_height > line_height) {\ underline=true; } + } else if (p_mode==PROCESS_CACHE) { + l.char_count+=text->text.length(); } rchar=0; + while(*c) { int end=0; int w=0; + int fw=0; lh=0; if (p_mode!=PROCESS_CACHE) { lh=line<l.height_caches.size()?l.height_caches[line]:1; } + bool found_space=false; while (c[end]!=0 && !(end && c[end-1]==' ' && c[end]!=' ')) { + int cw = font->get_char_size(c[end],c[end+1]).width; + if (c[end]=='\t') { + cw=tab_size*font->get_char_size(' ').width; + } w+=cw; - end++; + + if (c[end]==' ') { + + if (p_mode==PROCESS_CACHE) { + fw+=cw; + } else if (align==ALIGN_FILL && line<l.space_caches.size() && l.space_caches[line]>0) { + //print_line(String(c,end)+": "+itos(l.offset_caches[line])+"/"+itos(l.space_caches[line])); + //sub_space=cw; + found_space=true; + } else { + fw+=cw; + } + } else { + fw+=cw; + } + + end++; } - ENSURE_WIDTH(w); + ENSURE_WIDTH(w); + + + //print_line("END: "+String::chr(c[end])+"."); + if (end && c[end-1]==' ') { + spaces++; + if (p_mode==PROCESS_CACHE) { + spaces_size+=font->get_char_size(' ').width; + } + + if (found_space) { + int ln = MIN(l.offset_caches.size()-1,line); + + fw+=l.offset_caches[ln]/l.space_caches[ln]; + } + + } + { int ofs=0; + for(int i=0;i<end;i++) { int pofs=wofs+ofs; @@ -239,7 +297,11 @@ if (m_height > line_height) {\ if (p_mode==PROCESS_POINTER && r_click_char && p_click_pos.y>=y && p_click_pos.y<=y+lh) { //int o = (wofs+w)-p_click_pos.x; + int cw=font->get_char_size(c[i],c[i+1]).x; + if (c[i]=='\t') { + cw=tab_size*font->get_char_size(' ').width; + } if (p_click_pos.x-cw/2>pofs) { @@ -268,22 +330,36 @@ if (m_height > line_height) {\ } } - int cw; + int cw=0; + + bool visible = visible_characters<0 || p_char_count<visible_characters; if (selected) { cw = font->get_char_size(c[i],c[i+1]).x; draw_rect(Rect2(pofs,y,cw,lh),selection_bg); - font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],selection_fg); + if (visible) + font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],selection_fg); } else { - cw=font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],color); + if (visible) + cw=font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],color); + } + + p_char_count++; + if (c[i]=='\t') { + cw=tab_size*font->get_char_size(' ').width; } + + //print_line("draw char: "+String::chr(c[i])); + if (underline) { Color uc=color; - uc.a*=0.3; - VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,y+ascent+2),Point2(pofs+cw,y+ascent+2),uc); + uc.a*=0.5; + //VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,y+ascent+2),Point2(pofs+cw,y+ascent+2),uc); + int uy = y+lh-fh+ascent+2; + VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,uy),Point2(pofs+cw,uy),uc); } ofs+=cw; } @@ -292,7 +368,7 @@ if (m_height > line_height) {\ } - ADVANCE(w); + ADVANCE(fw); CHECK_HEIGHT(fh); //must be done somewhere c=&c[end]; } @@ -304,6 +380,8 @@ if (m_height > line_height) {\ lh=0; if (p_mode!=PROCESS_CACHE) lh = line<l.height_caches.size()?l.height_caches[line]:1; + else + l.char_count+=1; //images count as chars too ItemImage *img = static_cast<ItemImage*>(it); @@ -316,9 +394,12 @@ if (m_height > line_height) {\ ENSURE_WIDTH( img->image->get_width() ); - if (p_mode==PROCESS_DRAW) { + bool visible = visible_characters<0 || p_char_count<visible_characters; + + if (p_mode==PROCESS_DRAW && visible) { img->image->draw(ci,Point2(wofs,y+lh-font->get_descent()-img->image->get_height())); } + p_char_count++; ADVANCE( img->image->get_width() ); CHECK_HEIGHT( (img->image->get_height()+font->get_descent()) ); @@ -450,6 +531,23 @@ void RichTextLabel::_notification(int p_what) { update(); } break; + case NOTIFICATION_ENTER_TREE: { + + if (use_bbcode) + parse_bbcode(bbcode); + first_invalid_line=0; //invalidate ALL + update(); + + } break; + case NOTIFICATION_THEME_CHANGED: { + + if (is_inside_tree() && use_bbcode) { + parse_bbcode(bbcode); + //first_invalid_line=0; //invalidate ALL + //update(); + } + + } break; case NOTIFICATION_DRAW: { _validate_line_caches(); @@ -472,23 +570,26 @@ void RichTextLabel::_notification(int p_what) { //todo, change to binary search int from_line = 0; + int total_chars = 0; while (from_line<lines.size()) { if (lines[from_line].height_accum_cache>=ofs) break; from_line++; + total_chars+=lines[from_line].char_count; } if (from_line>=lines.size()) break; //nothing to draw int y = (lines[from_line].height_accum_cache - lines[from_line].height_cache) - ofs; - Ref<Font> base_font=get_font("default_font"); + Ref<Font> base_font=get_font("normal_font"); Color base_color=get_color("default_color"); while (y<size.height && from_line<lines.size()) { - _process_line(y,size.width-scroll_w,from_line,PROCESS_DRAW,base_font,base_color); + _process_line(y,size.width-scroll_w,from_line,PROCESS_DRAW,base_font,base_color,Point2i(),NULL,NULL,NULL,total_chars); + total_chars+=lines[from_line].char_count; from_line++; } } @@ -521,7 +622,7 @@ void RichTextLabel::_find_click(const Point2i& p_click,Item **r_click_item,int * int y = (lines[from_line].height_accum_cache - lines[from_line].height_cache) - ofs; - Ref<Font> base_font=get_font("default_font"); + Ref<Font> base_font=get_font("normal_font"); Color base_color=get_color("default_color"); @@ -634,12 +735,12 @@ void RichTextLabel::_input_event(InputEvent p_event) { case KEY_UP: { if (vscroll->is_visible()) - vscroll->set_val( vscroll->get_val() - get_font("default_font")->get_height() ); + vscroll->set_val( vscroll->get_val() - get_font("normal_font")->get_height() ); } break; case KEY_DOWN: { if (vscroll->is_visible()) - vscroll->set_val( vscroll->get_val() + get_font("default_font")->get_height() ); + vscroll->set_val( vscroll->get_val() + get_font("normal_font")->get_height() ); } break; case KEY_HOME: { @@ -856,13 +957,12 @@ void RichTextLabel::_validate_line_caches() { //validate invalid lines!s Size2 size = get_size(); - Ref<Font> base_font=get_font("default_font"); + Ref<Font> base_font=get_font("normal_font"); for(int i=first_invalid_line;i<lines.size();i++) { int y=0; _process_line(y,size.width-scroll_w,i,PROCESS_CACHE,base_font,Color()); - lines[i].height_cache=y; lines[i].height_accum_cache=y; @@ -1152,9 +1252,19 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { int pos = 0; List<String> tag_stack; - Ref<Font> base_font=get_font("default_font"); + Ref<Font> normal_font=get_font("normal_font"); + Ref<Font> bold_font=get_font("bold_font"); + Ref<Font> italics_font=get_font("italics_font"); + Ref<Font> bold_italics_font=get_font("bold_italics_font"); + Ref<Font> mono_font=get_font("mono_font"); + Color base_color=get_color("default_color"); + int indent_level=0; + + bool in_bold=false; + bool in_italics=false; + while(pos < p_bbcode.length()) { @@ -1182,12 +1292,18 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { String tag = p_bbcode.substr(brk_pos+1,brk_end-brk_pos-1); - if (tag.begins_with("/")) { + if (tag.begins_with("/") && tag_stack.size()) { bool tag_ok = tag_stack.size() && tag_stack.front()->get()==tag.substr(1,tag.length()); + if (tag_stack.front()->get()=="b") + in_bold=false; + if (tag_stack.front()->get()=="i") + in_italics=false; + if (tag_stack.front()->get()=="indent") + indent_level--; if (!tag_ok) { @@ -1205,19 +1321,27 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { } else if (tag=="b") { //use bold font - push_font(base_font); + in_bold=true; + if (in_italics) + push_font(bold_italics_font); + else + push_font(bold_font); pos=brk_end+1; tag_stack.push_front(tag); } else if (tag=="i") { //use italics font - push_font(base_font); + in_italics=true; + if (in_bold) + push_font(bold_italics_font); + else + push_font(italics_font); pos=brk_end+1; tag_stack.push_front(tag); } else if (tag=="code") { //use monospace font - push_font(base_font); + push_font(mono_font); pos=brk_end+1; tag_stack.push_front(tag); } else if (tag=="u") { @@ -1232,6 +1356,43 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { push_underline(); pos=brk_end+1; tag_stack.push_front(tag); + } else if (tag=="center") { + + //use underline + push_align(ALIGN_CENTER); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="fill") { + + //use underline + push_align(ALIGN_FILL); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="right") { + + //use underline + push_align(ALIGN_RIGHT); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="ul") { + + //use underline + push_list(LIST_DOTS); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="ol") { + + //use underline + push_list(LIST_NUMBERS); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="indent") { + + //use underline + indent_level++; + push_indent(indent_level); + pos=brk_end+1; + tag_stack.push_front(tag); } else if (tag=="url") { @@ -1244,6 +1405,7 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { pos=brk_end+1; tag_stack.push_front(tag); + } else if (tag.begins_with("url=")) { String url = tag.substr(4,tag.length()); @@ -1321,7 +1483,7 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { if (font.is_valid()) push_font(font); else - push_font(base_font); + push_font(normal_font); pos=brk_end+1; tag_stack.push_front("font"); @@ -1467,7 +1629,7 @@ void RichTextLabel::selection_copy() { if (text!="") { OS::get_singleton()->set_clipboard(text); - print_line("COPY: "+text); + //print_line("COPY: "+text); } } @@ -1477,7 +1639,29 @@ bool RichTextLabel::is_selection_enabled() const { return selection.enabled; } +void RichTextLabel::set_bbcode(const String& p_bbcode) { + bbcode=p_bbcode; + if (is_inside_tree() && use_bbcode) + parse_bbcode(p_bbcode); +} + +String RichTextLabel::get_bbcode() const { + + return bbcode; +} + +void RichTextLabel::set_use_bbcode(bool p_enable) { + if (use_bbcode==p_enable) + return; + use_bbcode=p_enable; + if (is_inside_tree() && use_bbcode) + parse_bbcode(bbcode); +} +bool RichTextLabel::is_using_bbcode() const { + + return use_bbcode; +} void RichTextLabel::_bind_methods() { @@ -1506,15 +1690,32 @@ void RichTextLabel::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_scroll_follow","follow"),&RichTextLabel::set_scroll_follow); ObjectTypeDB::bind_method(_MD("is_scroll_following"),&RichTextLabel::is_scroll_following); + ObjectTypeDB::bind_method(_MD("get_v_scroll"),&RichTextLabel::get_v_scroll); + ObjectTypeDB::bind_method(_MD("set_tab_size","spaces"),&RichTextLabel::set_tab_size); ObjectTypeDB::bind_method(_MD("get_tab_size"),&RichTextLabel::get_tab_size); + ObjectTypeDB::bind_method(_MD("set_selection_enabled","enabled"),&RichTextLabel::set_selection_enabled); ObjectTypeDB::bind_method(_MD("is_selection_enabled"),&RichTextLabel::is_selection_enabled); ObjectTypeDB::bind_method(_MD("parse_bbcode", "bbcode"),&RichTextLabel::parse_bbcode); ObjectTypeDB::bind_method(_MD("append_bbcode", "bbcode"),&RichTextLabel::append_bbcode); + ObjectTypeDB::bind_method(_MD("set_bbcode","text"),&RichTextLabel::set_bbcode); + ObjectTypeDB::bind_method(_MD("get_bbcode"),&RichTextLabel::get_bbcode); + + ObjectTypeDB::bind_method(_MD("set_visible_characters","amount"),&RichTextLabel::set_visible_characters); + ObjectTypeDB::bind_method(_MD("get_visible_characters"),&RichTextLabel::get_visible_characters); + + ObjectTypeDB::bind_method(_MD("get_total_character_count"),&RichTextLabel::get_total_character_count); + + ObjectTypeDB::bind_method(_MD("set_use_bbcode","enable"),&RichTextLabel::set_use_bbcode); + ObjectTypeDB::bind_method(_MD("is_using_bbcode"),&RichTextLabel::is_using_bbcode); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"bbcode/enabled"),_SCS("set_use_bbcode"),_SCS("is_using_bbcode")); + ADD_PROPERTY(PropertyInfo(Variant::STRING,"bbcode/bbcode",PROPERTY_HINT_MULTILINE_TEXT),_SCS("set_bbcode"),_SCS("get_bbcode")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"visible_characters",PROPERTY_HINT_RANGE,"-1,128000,1"),_SCS("set_visible_characters"),_SCS("get_visible_characters")); ADD_SIGNAL( MethodInfo("meta_clicked",PropertyInfo(Variant::NIL,"meta"))); @@ -1541,6 +1742,27 @@ void RichTextLabel::_bind_methods() { } + +void RichTextLabel::set_visible_characters(int p_visible) { + + visible_characters=p_visible; + update(); +} + +int RichTextLabel::get_visible_characters() const { + + return visible_characters; +} +int RichTextLabel::get_total_character_count() const { + + int tc=0; + for(int i=0;i<lines.size();i++) + tc+=lines[i].char_count; + + return tc; +} + + RichTextLabel::RichTextLabel() { @@ -1572,11 +1794,14 @@ RichTextLabel::RichTextLabel() { vscroll->set_step(1); vscroll->hide(); current_idx=1; + use_bbcode=false; selection.click=NULL; selection.active=false; selection.enabled=false; + visible_characters=-1; + } RichTextLabel::~RichTextLabel() { |