1 /* Text.cpp
2  * Copyright (C) 2018, 2019  Sven Jähnichen
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "Text.hpp"
19 
20 namespace BWidgets
21 {
Text()22 Text::Text () : Text (0.0, 0.0, 0.0, 0.0, "text", "") {}
23 
Text(const std::string & text)24 Text::Text (const std::string& text) : Text (0.0, 0.0, BWIDGETS_DEFAULT_WIDTH, BWIDGETS_DEFAULT_HEIGHT, text, text) {}
25 
Text(const double x,const double y,const double width,const double height,const std::string & text)26 Text::Text (const double x, const double y, const double width, const double height, const std::string& text) :
27 		Text (x, y, width, height, text, text) {}
28 
Text(const double x,const double y,const double width,const double height,const std::string & name,const std::string & text,bool resizable)29 Text::Text (const double x, const double y, const double width, const double height, const std::string& name, const std::string& text, bool resizable) :
30 		Widget (x, y, width, height, name),
31 		textColors (BWIDGETS_DEFAULT_TEXT_COLORS), textFont (BWIDGETS_DEFAULT_FONT), textString (text), yResizable (resizable) {}
32 
Text(const Text & that)33 Text::Text (const Text& that) : Widget (that)
34 {
35 	textColors = that.textColors;
36 	textFont = that.textFont;
37 	textString = that.textString;
38 	yResizable = that.yResizable;
39 }
40 
~Text()41 Text::~Text () {}
42 
operator =(const Text & that)43 Text& Text::operator= (const Text& that)
44 {
45 	textColors = that.textColors;
46 	textFont = that.textFont;
47 	textString = that.textString;
48 	yResizable = that.yResizable;
49 	Widget::operator= (that);
50 	if (yResizable) resize (getExtends());
51 	return *this;
52 }
53 
clone() const54 Widget* Text::clone () const {return new Text (*this);}
55 
setText(const std::string & text)56 void Text::setText (const std::string& text)
57 {
58 	if (text != textString)
59 	{
60 		textString = text;
61 		if (yResizable) resize (getExtends());
62 		update ();
63 	}
64 }
getText() const65 std::string Text::getText () const {return textString;}
66 
setTextColors(const BColors::ColorSet & colorset)67 void Text::setTextColors (const BColors::ColorSet& colorset)
68 {
69 	textColors = colorset;
70 	update ();
71 }
getTextColors()72 BColors::ColorSet* Text::getTextColors () {return &textColors;}
73 
setFont(const BStyles::Font & font)74 void Text::setFont (const BStyles::Font& font)
75 {
76 	textFont = font;
77 	if (yResizable) resize (getExtends());
78 	update ();
79 }
getFont()80 BStyles::Font* Text::getFont () {return &textFont;}
81 
setYResizable(const bool resizable)82 void Text::setYResizable (const bool resizable) {yResizable = resizable;}
83 
isYResizable() const84 bool Text::isYResizable () const {return yResizable;}
85 
setWidth(const double width)86 void Text::setWidth (const double width)
87 {
88 	Widget::setWidth (width);
89 	if (yResizable) resize (getExtends());
90 }
91 
resize()92 void Text::resize () {resize (getExtends());}
93 
resize(const double width,const double height)94 void Text::resize (const double width, const double height) {resize (BUtilities::Point (width, height));}
95 
resize(const BUtilities::Point extends)96 void Text::resize (const BUtilities::Point extends)
97 {
98 	if (yResizable)
99 	{
100 		double ySize = getTextBlockHeight (getTextBlock()) + 2 * getYOffset();
101 		Widget::resize (BUtilities::Point (extends.x, ySize));
102 	}
103 	else Widget::resize (extends);
104 }
105 
applyTheme(BStyles::Theme & theme)106 void Text::applyTheme (BStyles::Theme& theme) {applyTheme (theme, name_);}
107 
applyTheme(BStyles::Theme & theme,const std::string & name)108 void Text::applyTheme (BStyles::Theme& theme, const std::string& name)
109 {
110 	Widget::applyTheme (theme, name);
111 
112 	// Color
113 	void* colorsPtr = theme.getStyle(name, BWIDGETS_KEYWORD_TEXTCOLORS);
114 	if (colorsPtr) textColors = *((BColors::ColorSet*) colorsPtr);
115 
116 	// Font
117 	void* fontPtr = theme.getStyle(name, BWIDGETS_KEYWORD_FONT);
118 	if (fontPtr) setFont (*((BStyles::Font*) fontPtr));
119 
120 	else if (colorsPtr) update ();
121 }
122 
getTextBlock()123 std::vector<std::string> Text::getTextBlock ()
124 {
125 	std::vector<std::string> textblock;
126 	double w = getEffectiveWidth ();
127 	double h = getEffectiveHeight ();
128 	cairo_t* cr = cairo_create (widgetSurface_);
129 	cairo_text_decorations decorations;
130 	strncpy (decorations.family, textFont.getFontFamily ().c_str (), 63);
131 	decorations.size = textFont.getFontSize ();
132 	decorations.slant = textFont.getFontSlant ();
133 	decorations.weight = textFont.getFontWeight ();
134 
135 	char* textCString = (char*) malloc (strlen (textString.c_str ()) + 1);
136 	if (textCString)
137 	{
138 		strcpy (textCString, textString.c_str ());
139 
140 		for (double y = 0; (yResizable || (y <= h)) && (strlen (textCString) > 0); )
141 		{
142 			char* outputtext = cairo_create_text_fitted (cr, w, decorations, textCString);
143 			if (outputtext[0] == '\0') break;
144 			cairo_text_extents_t ext = textFont.getTextExtents(cr, outputtext);
145 			textblock.push_back (std::string (outputtext));
146 			y += (ext.height * textFont.getLineSpacing ());
147 			cairo_text_destroy (outputtext);
148 		}
149 
150 		free (textCString);
151 	}
152 
153 	cairo_destroy (cr);
154 	return textblock;
155 }
156 
getTextBlockHeight(std::vector<std::string> textBlock)157 double Text::getTextBlockHeight (std::vector<std::string> textBlock)
158 {
159 	double blockheight = 0.0;
160 	cairo_t* cr = cairo_create (widgetSurface_);
161 
162 	for (std::string textline : textBlock)
163 	{
164 		//cairo_text_extents_t ext = textFont.getTextExtents(cr, textline.c_str ());
165 		blockheight += (textFont.getFontSize () * textFont.getLineSpacing ());
166 	}
167 
168 	cairo_destroy (cr);
169 	return blockheight;
170 }
171 
draw(const BUtilities::RectArea & area)172 void Text::draw (const BUtilities::RectArea& area)
173 {
174 	if ((!widgetSurface_) || (cairo_surface_status (widgetSurface_) != CAIRO_STATUS_SUCCESS)) return;
175 
176 	// Draw super class widget elements first
177 	Widget::draw (area);
178 
179 	cairo_t* cr = cairo_create (widgetSurface_);
180 
181 	if (cairo_status (cr) == CAIRO_STATUS_SUCCESS)
182 	{
183 		// Limit cairo-drawing area
184 		cairo_rectangle (cr, area.getX (), area.getY (), area.getWidth (), area.getHeight ());
185 		cairo_clip (cr);
186 
187 		double xoff = getXOffset ();
188 		double yoff = getYOffset ();
189 		double w = getEffectiveWidth ();
190 		double h = getEffectiveHeight ();
191 
192 		// textString -> textblock
193 		std::vector<std::string> textblock = getTextBlock ();
194 		double blockheight = getTextBlockHeight (textblock);
195 
196 		// Calculate vertical alignment of the textblock
197 		double y0 = 0;
198 		switch (textFont.getTextVAlign ())
199 		{
200 		case BStyles::TEXT_VALIGN_TOP:		y0 = 0;
201 							break;
202 		case BStyles::TEXT_VALIGN_MIDDLE:	y0 = h / 2 - blockheight / 2;
203 							break;
204 		case BStyles::TEXT_VALIGN_BOTTOM:	y0 = h - blockheight;
205 							break;
206 		default:				y0 = 0;
207 		}
208 
209 
210 		// Output of textblock
211 		BColors::Color lc = *textColors.getColor (BColors::NORMAL);
212 		cairo_set_source_rgba (cr, lc.getRed (), lc.getGreen (), lc.getBlue (), lc.getAlpha ());
213 		cairo_select_font_face (cr, textFont.getFontFamily ().c_str (), textFont.getFontSlant (), textFont.getFontWeight ());
214 		cairo_set_font_size (cr, textFont.getFontSize ());
215 		double ycount = 0.0;
216 
217 		for (std::string textline : textblock)
218 		{
219 			cairo_text_extents_t ext = textFont.getTextExtents (cr, textline);
220 
221 			double x0;
222 			switch (textFont.getTextAlign ())
223 			{
224 			case BStyles::TEXT_ALIGN_LEFT:		x0 = - ext.x_bearing;
225 								break;
226 			case BStyles::TEXT_ALIGN_CENTER:	x0 = w / 2 - ext.width / 2 - ext.x_bearing;
227 								break;
228 			case BStyles::TEXT_ALIGN_RIGHT:		x0 = w - ext.width - ext.x_bearing;
229 								break;
230 			default:				x0 = 0;
231 			}
232 
233 			cairo_move_to (cr, xoff + x0, yoff + y0 + ycount - ext.y_bearing);
234 			cairo_show_text (cr, textline.c_str ());
235 			ycount += (textFont.getFontSize () * textFont.getLineSpacing ());
236 		}
237 	}
238 
239 	cairo_destroy (cr);
240 }
241 
242 }
243