diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /scene/gui/label.cpp | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'scene/gui/label.cpp')
-rw-r--r-- | scene/gui/label.cpp | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp new file mode 100644 index 0000000000..a2f837c3c4 --- /dev/null +++ b/scene/gui/label.cpp @@ -0,0 +1,619 @@ +/*************************************************************************/ +/* label.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 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 */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "label.h" +#include "print_string.h" +#include "globals.h" +#include "translation.h" + + +void Label::set_autowrap(bool p_autowrap) { + + autowrap=p_autowrap; + word_cache_dirty=true; + minimum_size_changed(); + + +} +bool Label::has_autowrap() const { + + return autowrap; +} + + +void Label::set_uppercase(bool p_uppercase) { + + uppercase=p_uppercase; + word_cache_dirty=true; + minimum_size_changed(); +} +bool Label::is_uppercase() const { + + return uppercase; +} + + +int Label::get_line_height() const { + + return get_font("font")->get_height(); + +} + + +void Label::_notification(int p_what) { + + if (p_what==NOTIFICATION_DRAW) { + + if (clip && !autowrap) + VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(),true); + + if (word_cache_dirty) + regenerate_word_cache(); + + + RID ci = get_canvas_item(); + + Size2 string_size; + Size2 size=get_size(); + + Ref<Font> font = get_font("font"); + Color font_color = get_color("font_color"); + Color font_color_shadow = get_color("font_color_shadow"); + bool use_outlinde = get_constant("shadow_as_outline"); + Point2 shadow_ofs(get_constant("shadow_offset_x"),get_constant("shadow_offset_y")); + + + int font_h = font->get_height(); + int line_from=(int)get_val(); // + p_exposed.pos.y / font_h; + int lines_visible = size.y/font_h; + int line_to=(int)get_val() + lines_visible; //p_exposed.pos.y+p_exposed.size.height / font_h; + int space_w=font->get_char_size(' ').width; + int lines_total = get_max(); + int chars_total=0; + + int vbegin=0,vsep=0; + + if (lines_total && lines_total < lines_visible) { + + + switch(valign) { + + case VALIGN_TOP: { + + //nothing + } break; + case VALIGN_CENTER: { + + vbegin=(lines_visible-lines_total) * font_h / 2; + vsep=0; + } break; + case VALIGN_BOTTOM: { + vbegin=(lines_visible-lines_total) * font_h; + vsep=0; + + } break; + case VALIGN_FILL: { + vbegin=0; + if (lines_total>1) { + vsep=(lines_visible-lines_total) * font_h / (lines_total-1); + } else { + vsep=0; + } + + } break; + } + } + + + WordCache *wc = word_cache; + if (!wc) + return; + + + int line=0; + while(wc) { + + /* handle lines not meant to be drawn quickly */ + if (line>line_to) + break; + if (line<line_from) { + + while (wc && wc->char_pos>=0) + wc=wc->next; + if (wc) + wc=wc->next; + line++; + continue; + } + + /* handle lines normally */ + + if (wc->char_pos<0) { + //empty line + wc=wc->next; + line++; + continue; + } + + WordCache *from=wc; + WordCache *to=wc; + + int taken=0; + int spaces=0; + while(to && to->char_pos>=0) { + + taken+=to->pixel_width; + if (to!=from) { + spaces++; + } + to=to->next; + } + + bool can_fill = to && to->char_pos==WordCache::CHAR_WRAPLINE; + + float x_ofs=0; + + switch (align) { + + case ALIGN_FILL: + case ALIGN_LEFT: { + + x_ofs=0; + } break; + case ALIGN_CENTER: { + + x_ofs=int(size.width-(taken+spaces*space_w))/2;; + + } break; + case ALIGN_RIGHT: { + + + x_ofs=int(size.width-(taken+spaces*space_w)); + + } break; + } + + int y_ofs=(line-(int)get_val())*font_h + font->get_ascent(); + y_ofs+=vbegin + line*vsep; + + while(from!=to) { + + // draw a word + int pos = from->char_pos; + if (from->char_pos<0) { + + ERR_PRINT("BUG"); + return; + } + if (from!=wc) { + /* spacing */ + x_ofs+=space_w; + if (can_fill && align==ALIGN_FILL && spaces) { + + x_ofs+=int((size.width-(taken+space_w*spaces))/spaces); + } + + + } + + + + if (font_color_shadow.a>0) { + + int chars_total_shadow = chars_total; //save chars drawn + float x_ofs_shadow=x_ofs; + for (int i=0;i<from->word_len;i++) { + + if (visible_chars < 0 || chars_total_shadow<visible_chars) { + CharType c = text[i+pos]; + CharType n = text[i+pos+1]; + if (uppercase) { + c=String::char_uppercase(c); + n=String::char_uppercase(c); + } + + float move=font->draw_char(ci, Point2( x_ofs_shadow, y_ofs )+shadow_ofs, c, n,font_color_shadow ); + if (use_outlinde) { + font->draw_char(ci, Point2( x_ofs_shadow, y_ofs )+Vector2(-shadow_ofs.x,shadow_ofs.y), c, n,font_color_shadow ); + font->draw_char(ci, Point2( x_ofs_shadow, y_ofs )+Vector2(shadow_ofs.x,-shadow_ofs.y), c, n,font_color_shadow ); + font->draw_char(ci, Point2( x_ofs_shadow, y_ofs )+Vector2(-shadow_ofs.x,-shadow_ofs.y), c, n,font_color_shadow ); + } + x_ofs_shadow+=move; + chars_total_shadow++; + } + } + + + } + for (int i=0;i<from->word_len;i++) { + + if (visible_chars < 0 || chars_total<visible_chars) { + CharType c = text[i+pos]; + CharType n = text[i+pos+1]; + if (uppercase) { + c=String::char_uppercase(c); + n=String::char_uppercase(c); + } + + x_ofs+=font->draw_char(ci,Point2( x_ofs, y_ofs ), c, n, font_color ); + chars_total++; + } + + } + from=from->next; + } + + wc=to?to->next:0; + line++; + + } + } + + if (p_what==NOTIFICATION_THEME_CHANGED) { + + word_cache_dirty=true; + update(); + } + if (p_what==NOTIFICATION_RESIZED) { + + word_cache_dirty=true; + } + +} + +Size2 Label::get_minimum_size() const { + + if (autowrap) + return Size2(1,1); + else { + + // don't want to mutable everything + if(word_cache_dirty) + const_cast<Label*>(this)->regenerate_word_cache(); + + Size2 ms=minsize; + if (clip) + ms.width=1; + return ms; + } +} + +int Label::get_longest_line_width() const { + + Ref<Font> font = get_font("font"); + int max_line_width=0; + int line_width=0; + + for (int i=0;i<text.size()+1;i++) { + + CharType current=i<text.length()?text[i]:' '; //always a space at the end, so the algo works + if (uppercase) + current=String::char_uppercase(current); + + if (current<32) { + + if (current=='\n') { + + if (line_width>max_line_width) + max_line_width=line_width; + line_width=0; + } + } else { + + int char_width=font->get_char_size(current).width; + line_width+=char_width; + } + + } + + if (line_width>max_line_width) + max_line_width=line_width; + + return max_line_width; +} + +int Label::get_line_count() const { + + if (!is_inside_scene()) + return 1; + if (word_cache_dirty) + const_cast<Label*>(this)->regenerate_word_cache(); + + return line_count; +} + +void Label::regenerate_word_cache() { + + while (word_cache) { + + WordCache *current=word_cache; + word_cache=current->next; + memdelete( current ); + } + + + int width=autowrap?get_size().width:get_longest_line_width(); + Ref<Font> font = get_font("font"); + + int current_word_size=0; + int word_pos=0; + int line_width=0; + int last_width=0; + line_count=1; + total_char_cache=0; + + WordCache *last=NULL; + + for (int i=0;i<text.size()+1;i++) { + + CharType current=i<text.length()?text[i]:' '; //always a space at the end, so the algo works + + if (uppercase) + current=String::char_uppercase(current); + + bool insert_newline=false; + + if (current<33) { + + if (current_word_size>0) { + + WordCache *wc = memnew( WordCache ); + if (word_cache) { + last->next=wc; + } else { + word_cache=wc; + } + last=wc; + + wc->pixel_width=current_word_size; + wc->char_pos=word_pos; + wc->word_len=i-word_pos; + current_word_size=0; + + } + + if (current=='\n') { + + insert_newline=true; + } else { + total_char_cache++; + } + + + } else { + + if (current_word_size==0) { + if (line_width>0) // add a space before the new word if a word existed before + line_width+=font->get_char_size(' ').width; + word_pos=i; + } + + int char_width=font->get_char_size(current).width; + current_word_size+=char_width; + line_width+=char_width; + total_char_cache++; + + } + + if ((autowrap && line_width>=width && last_width<width) || insert_newline) { + + WordCache *wc = memnew( WordCache ); + if (word_cache) { + last->next=wc; + } else { + word_cache=wc; + } + last=wc; + + wc->pixel_width=0; + wc->char_pos=insert_newline?WordCache::CHAR_NEWLINE:WordCache::CHAR_WRAPLINE; + + line_width=current_word_size; + line_count++; + + + } + + last_width=line_width; + + } + + + if (!autowrap) { + + minsize.width=width; + minsize.height=font->get_height()*line_count; + set_page( line_count ); + + } else { + + set_page( get_size().height / font->get_height() ); + } + + set_max(line_count); + + word_cache_dirty=false; + +} + + +void Label::set_align(Align p_align) { + + ERR_FAIL_INDEX(p_align,4); + align=p_align; + update(); +} + +Label::Align Label::get_align() const{ + + return align; +} + +void Label::set_valign(VAlign p_align) { + + ERR_FAIL_INDEX(p_align,4); + valign=p_align; + update(); +} + +Label::VAlign Label::get_valign() const{ + + return valign; +} + +void Label::set_text(const String& p_string) { + + String str = XL_MESSAGE(p_string); + + if (text==str) + return; + + text=str; + word_cache_dirty=true; + update(); + if (!autowrap) + minimum_size_changed(); + +} + +void Label::set_clip_text(bool p_clip) { + + if (clip==p_clip) + return; + clip=p_clip; + update(); + minimum_size_changed(); +} + +bool Label::is_clipping_text() const { + + return clip; +} + +String Label::get_text() const { + + return text; +} +void Label::set_visible_characters(int p_amount) { + + visible_chars=p_amount; + update(); +} + +void Label::set_percent_visible(float p_percent) { + + if (p_percent<0) + set_visible_characters(-1); + else + set_visible_characters(get_total_character_count()*p_percent); + percent_visible=p_percent; +} + +float Label::get_percent_visible() const{ + + return percent_visible; +} + + +int Label::get_total_character_count() const { + + if (word_cache_dirty) + const_cast<Label*>(this)->regenerate_word_cache(); + + return total_char_cache; +} + +void Label::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_align","align"),&Label::set_align); + ObjectTypeDB::bind_method(_MD("get_align"),&Label::get_align); + ObjectTypeDB::bind_method(_MD("set_valign","valign"),&Label::set_valign); + ObjectTypeDB::bind_method(_MD("get_valign"),&Label::get_valign); + ObjectTypeDB::bind_method(_MD("set_text","text"),&Label::set_text); + ObjectTypeDB::bind_method(_MD("get_text"),&Label::get_text); + ObjectTypeDB::bind_method(_MD("set_autowrap","enable"),&Label::set_autowrap); + ObjectTypeDB::bind_method(_MD("has_autowrap"),&Label::has_autowrap); + ObjectTypeDB::bind_method(_MD("set_uppercase","enable"),&Label::set_uppercase); + ObjectTypeDB::bind_method(_MD("is_uppercase"),&Label::is_uppercase); + ObjectTypeDB::bind_method(_MD("get_line_height"),&Label::get_line_height); + ObjectTypeDB::bind_method(_MD("get_line_count"),&Label::get_line_count); + ObjectTypeDB::bind_method(_MD("get_total_character_count"),&Label::get_total_character_count); + ObjectTypeDB::bind_method(_MD("set_visible_characters"),&Label::set_visible_characters); + ObjectTypeDB::bind_method(_MD("set_percent_visible","percent_visible"),&Label::set_percent_visible); + ObjectTypeDB::bind_method(_MD("get_percent_visible"),&Label::get_percent_visible); + + BIND_CONSTANT( ALIGN_LEFT ); + BIND_CONSTANT( ALIGN_CENTER ); + BIND_CONSTANT( ALIGN_RIGHT ); + BIND_CONSTANT( ALIGN_FILL ); + + BIND_CONSTANT( VALIGN_TOP ); + BIND_CONSTANT( VALIGN_CENTER ); + BIND_CONSTANT( VALIGN_BOTTOM ); + BIND_CONSTANT( VALIGN_FILL ); + + ADD_PROPERTY( PropertyInfo( Variant::STRING, "text",PROPERTY_HINT_MULTILINE_TEXT,"",PROPERTY_USAGE_DEFAULT_INTL), _SCS("set_text"),_SCS("get_text") ); + ADD_PROPERTY( PropertyInfo( Variant::INT, "align", PROPERTY_HINT_ENUM,"Left,Center,Right,Fill" ),_SCS("set_align"),_SCS("get_align") ); + ADD_PROPERTY( PropertyInfo( Variant::INT, "valign", PROPERTY_HINT_ENUM,"Top,Center,Bottom,Fill" ),_SCS("set_valign"),_SCS("get_valign") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "autowrap"),_SCS("set_autowrap"),_SCS("has_autowrap") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "uppercase"),_SCS("set_uppercase"),_SCS("is_uppercase") ); + ADD_PROPERTY( PropertyInfo( Variant::REAL, "percent_visible"),_SCS("set_percent_visible"),_SCS("get_percent_visible") ); + +} + +Label::Label(const String &p_text) { + + align=ALIGN_LEFT; + valign=VALIGN_TOP; + text=""; + word_cache=NULL; + word_cache_dirty=true; + autowrap=false; + line_count=0; + set_v_size_flags(0); + clip=false; + set_ignore_mouse(true); + total_char_cache=0; + visible_chars=-1; + percent_visible=-1; + set_text(p_text); + uppercase=false; +} + + +Label::~Label() { + + while (word_cache) { + + WordCache *current=word_cache; + word_cache=current->next; + memdelete( current ); + } +} + + |