예제 #1
0
void RichTextLabel::_find_click(ItemFrame* p_frame,const Point2i& p_click,Item **r_click_item,int *r_click_char,bool *r_outside) {

	if (r_click_item)
		*r_click_item=NULL;

	Size2 size = get_size();

	int ofs = vscroll->get_val();

	//todo, change to binary search
	int from_line = 0;

	while (from_line<p_frame->lines.size()) {

		if (p_frame->lines[from_line].height_accum_cache>=ofs)
			break;
		from_line++;
	}


	if (from_line>=p_frame->lines.size())
		return;


	int y = (p_frame->lines[from_line].height_accum_cache - p_frame->lines[from_line].height_cache) - ofs;
	Ref<Font> base_font=get_font("normal_font");
	Color base_color=get_color("default_color");


	while (y<size.height && from_line<p_frame->lines.size()) {

		_process_line(p_frame,Point2(),y,size.width-scroll_w,from_line,PROCESS_POINTER,base_font,base_color,p_click,r_click_item,r_click_char,r_outside);
		if (r_click_item && *r_click_item)
			return;
		from_line++;
	}


}
예제 #2
0
void RichTextLabel::_validate_line_caches(ItemFrame* p_frame) {

	if (p_frame->first_invalid_line==p_frame->lines.size())
		return;

	//validate invalid lines!s
	Size2 size = get_size();

	Ref<Font> base_font=get_font("normal_font");

	for(int i=p_frame->first_invalid_line;i<p_frame->lines.size();i++) {

		int y=0;
		_process_line(p_frame,Point2(),y,size.width-scroll_w,i,PROCESS_CACHE,base_font,Color());
		p_frame->lines[i].height_cache=y;
		p_frame->lines[i].height_accum_cache=y;

		if (i>0)
			p_frame->lines[i].height_accum_cache+=p_frame->lines[i-1].height_accum_cache;


	}

	int total_height=0;
	if (p_frame->lines.size())
		total_height=p_frame->lines[p_frame->lines.size()-1].height_accum_cache;

	main->first_invalid_line=p_frame->lines.size();

	updating_scroll=true;
	vscroll->set_max(total_height);
	vscroll->set_page(size.height);
	if (scroll_follow && scroll_following)
		vscroll->set_val(total_height-size.height);

	updating_scroll=false;

}
예제 #3
0
void RichTextLabel::_validate_line_caches() {

    if (first_invalid_line==lines.size())
        return;

    //validate invalid lines!s
    Size2 size = get_size();

    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;

        if (i>0)
            lines[i].height_accum_cache+=lines[i-1].height_accum_cache;


    }

    int total_height=0;
    if (lines.size())
        total_height=lines[lines.size()-1].height_accum_cache;

    first_invalid_line=lines.size();

    updating_scroll=true;
    vscroll->set_max(total_height);
    vscroll->set_page(size.height);
    if (scroll_follow && scroll_following)
        vscroll->set_val(total_height-size.height);

    updating_scroll=false;

}
예제 #4
0
void RichTextLabel::_process_line(ItemFrame *p_frame,const Vector2& p_ofs,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)
		*r_outside=false;
	if (p_mode==PROCESS_DRAW) {
		ci=get_canvas_item();

		if (r_click_item)
			*r_click_item=NULL;

	}
	Line &l = p_frame->lines[p_line];
	Item *it = l.from;


	int line_ofs=0;
	int margin=_find_margin(it,p_base_font);
	Align align=_find_align(it);;
	int line=0;
	int spaces=0;


	if (p_mode!=PROCESS_CACHE) {

		ERR_FAIL_INDEX(line,l.offset_caches.size());
		line_ofs = l.offset_caches[line];
	}

	if (p_mode==PROCESS_CACHE) {
		l.offset_caches.clear();
		l.height_caches.clear();
		l.char_count=0;
		l.minimum_width=0;
	}

	int wofs=margin;
	int spaces_size=0;
	int align_ofs=0;


	if (p_mode!=PROCESS_CACHE && align!=ALIGN_FILL)
		wofs+=line_ofs;

	int begin=wofs;

	Ref<Font> cfont = _find_font(it);
	if (cfont.is_null())
		cfont=p_base_font;

	//line height should be the font height for the first time, this ensures that an empty line will never have zero height and succesive newlines are displayed
	int line_height=cfont->get_height();

	Variant meta;

#define NEW_LINE \
{\
	if (p_mode!=PROCESS_CACHE) {\
		line++;\
		if (line < l.offset_caches.size())\
			line_ofs=l.offset_caches[line];\
		wofs=margin;\
		if (align!=ALIGN_FILL)\
			wofs+=line_ofs;\
	} else {\
		int used=wofs-margin;\
		switch(align) {\
			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-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;\
	align_ofs=0;\
	if (p_mode!=PROCESS_CACHE) {\
		lh=line<l.height_caches.size()?l.height_caches[line]:1;\
	}\
	if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh && p_click_pos.x<p_ofs.x+wofs) {\
		if (r_outside) *r_outside=true;\
		*r_click_item=it;\
		*r_click_char=rchar;\
		return;\
	}\
}


#define ENSURE_WIDTH(m_width) \
	if (p_mode==PROCESS_CACHE) { \
		l.minimum_width=MAX(l.minimum_width,wofs+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>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh && p_click_pos.x>p_ofs.x+wofs) {\
			if (r_outside) *r_outside=true;	\
			*r_click_item=it;\
			*r_click_char=rchar;\
			return;\
		}\
		NEW_LINE\
	}


#define ADVANCE(m_width) \
{\
	if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh && p_click_pos.x>=p_ofs.x+wofs && p_click_pos.x<p_ofs.x+wofs+m_width) {\
		if (r_outside) *r_outside=false;	\
		*r_click_item=it;\
		*r_click_char=rchar;\
		return;\
	}\
	wofs+=m_width;\
}

#define CHECK_HEIGHT( m_height ) \
if (m_height > line_height) {\
	line_height=m_height;\
}

	Color selection_fg;
	Color selection_bg;

	if (p_mode==PROCESS_DRAW) {


		selection_fg = get_color("font_color_selected");
		selection_bg = get_color("selection_color");
	}
	int rchar=0;
	int lh=0;

	while (it) {

		switch(it->type) {

			case ITEM_TEXT: {

				ItemText *text = static_cast<ItemText*>(it);

				Ref<Font> font=_find_font(it);
				if (font.is_null())
					font=p_base_font;

				const CharType *c = text->text.c_str();
				const CharType *cf=c;
				int fh=font->get_height();
				int ascent = font->get_ascent();
				Color color;
				bool underline=false;

				if (p_mode==PROCESS_DRAW) {
					color=_find_color(text,p_base_color);
					underline=_find_underline(text);
					if (_find_meta(text,&meta)) {

						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;
					}

					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;
						}

						if (end>0 && w+cw+begin > p_width ) {
							break; //don't allow lines longer than assigned width
						}

						w+=cw;
						fw+=cw;

						end++;
					}

					ENSURE_WIDTH(w);

					if (end && c[end-1]==' ') {
						if (p_mode==PROCESS_CACHE) {
							spaces_size+=font->get_char_size(' ').width;
						} else if (align==ALIGN_FILL) {
							int ln = MIN(l.offset_caches.size()-1,line);
							if (l.space_caches[ln]) {
								align_ofs = spaces * l.offset_caches[ln] / l.space_caches[ln];
							}
						}
						spaces++;

					}


					{

						int ofs=0;

						for(int i=0;i<end;i++) {
							int pofs=wofs+ofs;


							if (p_mode==PROCESS_POINTER && r_click_char && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.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>p_ofs.x+align_ofs+pofs) {

									rchar=int((&c[i])-cf);
								}


								ofs+=cw;
							} else if (p_mode==PROCESS_DRAW) {

								bool selected=false;
								if (selection.active) {

									int cofs = (&c[i])-cf;
									if ((text->index > selection.from->index || (text->index == selection.from->index && cofs >=selection.from_char)) && (text->index < selection.to->index || (text->index == selection.to->index && cofs <=selection.to_char))) {
										selected=true;
									}
								}

								int cw=0;

								bool visible = visible_characters<0 || p_char_count<visible_characters;
								if (c[i]=='\t')
									visible=false;

								if (selected) {

									cw = font->get_char_size(c[i],c[i+1]).x;
									draw_rect(Rect2(p_ofs.x+pofs,p_ofs.y+y,cw,lh),selection_bg);
									if (visible)
										font->draw_char(ci,p_ofs+Point2(align_ofs+pofs,y+lh-(fh-ascent)),c[i],c[i+1],selection_fg);

								} else {
									if (visible)
										cw=font->draw_char(ci,p_ofs+Point2(align_ofs+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;
								}


								if (underline) {
									Color uc=color;
									uc.a*=0.5;
									int uy = y+lh-fh+ascent+2;
									VS::get_singleton()->canvas_item_add_line(ci,p_ofs+Point2(align_ofs+pofs,uy),p_ofs+Point2(align_ofs+pofs+cw,uy),uc);
								}
								ofs+=cw;
							}

						}
					}


					ADVANCE(fw);
					CHECK_HEIGHT(fh); //must be done somewhere
					c=&c[end];
				}


			} break;
			case ITEM_IMAGE: {

				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);

				Ref<Font> font=_find_font(it);
				if (font.is_null())
					font=p_base_font;

				if (p_mode==PROCESS_POINTER && r_click_char)
					*r_click_char=0;

				ENSURE_WIDTH( img->image->get_width() );

				bool visible = visible_characters<0 || p_char_count<visible_characters;

				if (p_mode==PROCESS_DRAW && visible) {
					img->image->draw(ci,p_ofs+Point2(align_ofs+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()) );

			} break;
			case ITEM_NEWLINE: {


				lh=0;
				if (p_mode!=PROCESS_CACHE)
					lh = line<l.height_caches.size()?l.height_caches[line]:1;

			} break;
			case ITEM_TABLE: {

				lh=0;
				ItemTable *table = static_cast<ItemTable*>(it);
				int hseparation=get_constant("table_hseparation");
				int vseparation=get_constant("table_vseparation");
				Color ccolor = _find_color(table,p_base_color);
				Vector2 draw_ofs = Point2(wofs,y);

				if (p_mode==PROCESS_CACHE) {

					int idx=0;
					//set minimums to zero
					for(int i=0;i<table->columns.size();i++) {
						table->columns[i].min_width=0;
						table->columns[i].width=0;
					}
					//compute minimum width for each cell
					for (List<Item*>::Element *E=table->subitems.front();E;E=E->next()) {
						ERR_CONTINUE(E->get()->type!=ITEM_FRAME); //children should all be frames
						ItemFrame *frame = static_cast<ItemFrame*>(E->get());

						int column = idx % table->columns.size();

						int ly=0;


						for(int i=0;i<frame->lines.size();i++) {

							_process_line(frame,Point2(),ly,p_width,i,PROCESS_CACHE,cfont,Color());
							table->columns[column].min_width=MAX( table->columns[i].min_width, frame->lines[i].minimum_width );
						}
						idx++;
					}

					//compute available width and total radio (for expanders)


					int total_ratio=0;
					int available_width=p_width - hseparation * (table->columns.size() -1);
					table->total_width=hseparation;

					for(int i=0;i<table->columns.size();i++) {
						available_width-=table->columns[i].min_width;
						if (table->columns[i].expand)
							total_ratio+=table->columns[i].expand_ratio;
					}

					//assign actual widths

					for(int i=0;i<table->columns.size();i++) {
						table->columns[i].width = table->columns[i].min_width;
						if (table->columns[i].expand)
							table->columns[i].width+=table->columns[i].expand_ratio*available_width/total_ratio;
						table->total_width+=table->columns[i].width+hseparation;
					}

					//compute caches properly again with the right width
					idx=0;
					for (List<Item*>::Element *E=table->subitems.front();E;E=E->next()) {
						ERR_CONTINUE(E->get()->type!=ITEM_FRAME); //children should all be frames
						ItemFrame *frame = static_cast<ItemFrame*>(E->get());

						int column = idx % table->columns.size();


						for(int i=0;i<frame->lines.size();i++) {

							int ly=0;
							_process_line(frame,Point2(),ly,table->columns[column].width,i,PROCESS_CACHE,cfont,Color());
							frame->lines[i].height_cache=ly; //actual height
							frame->lines[i].height_accum_cache=ly; //actual height
						}
						idx++;
					}

				}



				Point2 offset(align_ofs+hseparation,vseparation);

				int row_height=0;
				//draw using computed caches
				int idx=0;
				for (List<Item*>::Element *E=table->subitems.front();E;E=E->next()) {
					ERR_CONTINUE(E->get()->type!=ITEM_FRAME); //children should all be frames
					ItemFrame *frame = static_cast<ItemFrame*>(E->get());

					int column = idx % table->columns.size();

					int ly=0;
					int yofs=0;


					int lines_h = frame->lines[frame->lines.size()-1].height_accum_cache - (frame->lines[0].height_accum_cache - frame->lines[0].height_cache);
					int lines_ofs = p_ofs.y+offset.y+draw_ofs.y;

					bool visible = lines_ofs < get_size().height && lines_ofs+lines_h >=0;

					for(int i=0;i<frame->lines.size();i++) {


						if (visible) {
							if (p_mode==PROCESS_DRAW) {
								_process_line(frame,p_ofs+offset+draw_ofs+Vector2(0,yofs),ly,table->columns[column].width,i,PROCESS_DRAW,cfont,ccolor);
							} else if (p_mode==PROCESS_POINTER) {
								_process_line(frame,p_ofs+offset+draw_ofs+Vector2(0,yofs),ly,table->columns[column].width,i,PROCESS_POINTER,cfont,ccolor,p_click_pos,r_click_item,r_click_char,r_outside);
							}
						}

						yofs+=frame->lines[i].height_cache;
						if (p_mode==PROCESS_CACHE) {
							frame->lines[i].height_accum_cache=offset.y+draw_ofs.y+frame->lines[i].height_cache;
						}

					}

					row_height=MAX(yofs,row_height);
					offset.x+=table->columns[column].width+hseparation;

					if (column==table->columns.size()-1) {

						offset.y+=row_height+vseparation;
						offset.x=hseparation;
						row_height=0;
					}
					idx++;
				}

				int total_height = offset.y;
				if (row_height) {
					total_height=row_height+vseparation;
				}



				ADVANCE( table->total_width );
				CHECK_HEIGHT( total_height );

			} break;

			default: {}

		}


		Item *itp = it;

		it = _get_next_item(it);

		if (p_mode == PROCESS_POINTER && r_click_item && itp && !it && p_click_pos.y>p_ofs.y+y+lh) {
			//at the end of all, return this
			if (r_outside) *r_outside=true;
			*r_click_item=itp;
			*r_click_char=rchar;
			return;
		}

		if (it && (p_line+1 < p_frame->lines.size()) && p_frame->lines[p_line+1].from==it) {

			if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=p_ofs.y+y && p_click_pos.y<=p_ofs.y+y+lh) {
				//went to next line, but pointer was on the previous one
				if (r_outside) *r_outside=true;
				*r_click_item=itp;
				*r_click_char=rchar;
				return;
			}

			break;
		}
	}

	NEW_LINE;

#undef NEW_LINE
#undef ENSURE_WIDTH
#undef ADVANCE
#undef CHECK_HEIGHT

}
예제 #5
0
void RichTextLabel::_notification(int p_what) {

	switch (p_what) {

		case NOTIFICATION_RESIZED: {

			main->first_invalid_line=0; //invalidate ALL
			update();

		} break;
		case NOTIFICATION_ENTER_TREE: {

			set_bbcode(bbcode);
			main->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(main);
			_update_scroll();


			RID ci=get_canvas_item();
			Size2 size = get_size();

			VisualServer::get_singleton()->canvas_item_set_clip(ci,true);

			if (has_focus()) {
				VisualServer::get_singleton()->canvas_item_add_clip_ignore(ci,true);
				draw_style_box(get_stylebox("focus"),Rect2(Point2(),size));
				VisualServer::get_singleton()->canvas_item_add_clip_ignore(ci,false);
			}

			int ofs = vscroll->get_val();

			//todo, change to binary search

			int from_line = 0;
			int total_chars = 0;
			while (from_line<main->lines.size()) {

				if (main->lines[from_line].height_accum_cache>=ofs)
					break;
				from_line++;
				total_chars+=main->lines[from_line].char_count;
			}

			if (from_line>=main->lines.size())
				break; //nothing to draw

			int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs;
			Ref<Font> base_font=get_font("normal_font");
			Color base_color=get_color("default_color");

			while (y<size.height && from_line<main->lines.size()) {

				_process_line(main,Point2(),y,size.width-scroll_w,from_line,PROCESS_DRAW,base_font,base_color,Point2i(),NULL,NULL,NULL,total_chars);
				total_chars+=main->lines[from_line].char_count;
				from_line++;
			}
		}
	}
}