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