cTextMsg pre-calculate layout

This commit is contained in:
2024-08-12 15:53:55 -05:00
committed by Celtic Minstrel
parent 5f97d8bfb3
commit c08a10867c
4 changed files with 108 additions and 58 deletions

View File

@@ -95,6 +95,29 @@ void cTextMsg::setFixed(bool w, bool h) {
fixedHeight = h;
}
void cTextMsg::calculate_layout() {
to_rect = frame;
msg = lbl;
for(const auto& key : keyRefs) {
size_t pos = msg.find_first_of(KEY_PLACEHOLDER);
if(pos == std::string::npos) break;
if(key && !parent->hasControl(*key)) continue;
cControl& ctrl = key ? parent->getControl(*key) : *this;
msg.replace(pos, 1, ctrl.getAttachedKeyDescription());
}
if(to_rect.bottom - to_rect.top < 20) { // essentially, it's a single line
style.lineHeight = 12;
to_rect.left += 3;
text_mode = eTextMode::LEFT_BOTTOM;
}else {
style.lineHeight = textSize + 2;
to_rect.inset(4,4);
text_mode = eTextMode::WRAP;
break_info = calculate_line_wrapping(to_rect, msg, style);
}
calculated = true;
}
void cTextMsg::recalcRect() {
if(fixedWidth && fixedHeight) return;
TextStyle style;
@@ -151,6 +174,7 @@ void cTextMsg::recalcRect() {
calc_rect.width() = combo.width() + 16;
}
frame = calc_rect;
calculate_layout();
}
cTextMsg::cTextMsg(cDialog& parent) :
@@ -189,10 +213,6 @@ void cTextMsg::draw(){
inWindow->setActive();
if(visible){
TextStyle style;
style.font = textFont;
style.pointSize = textSize;
style.underline = underlined;
drawFrame(2, frameStyle);
sf::Color draw_color = color;
if(depressed){
@@ -200,24 +220,9 @@ void cTextMsg::draw(){
draw_color.g = 256 - draw_color.g;
draw_color.b = 256 - draw_color.b;
}
std::string msg = lbl;
for(const auto& key : keyRefs) {
size_t pos = msg.find_first_of(KEY_PLACEHOLDER);
if(pos == std::string::npos) break;
if(key && !parent->hasControl(*key)) continue;
cControl& ctrl = key ? parent->getControl(*key) : *this;
msg.replace(pos, 1, ctrl.getAttachedKeyDescription());
}
style.colour = draw_color;
if(to_rect.bottom - to_rect.top < 20) { // essentially, it's a single line
style.lineHeight = 12;
to_rect.left += 3;
win_draw_string(*inWindow,to_rect,msg,eTextMode::LEFT_BOTTOM,style);
}else {
style.lineHeight = textSize + 2;
to_rect.inset(4,4);
win_draw_string(*inWindow,to_rect,msg,eTextMode::WRAP,style);
}
if (!calculated) calculate_layout();
win_draw_string(*inWindow,to_rect,msg,text_mode,style,break_info);
}
}

View File

@@ -55,5 +55,12 @@ private:
std::vector<boost::optional<std::string>> keyRefs;
std::string fromList;
bool underlined = false, fixedWidth = false, fixedHeight = false;
TextStyle style;
rectangle to_rect;
break_info_t break_info;
eTextMode text_mode;
std::string msg;
void calculate_layout();
bool calculated = false;
};
#endif

View File

@@ -46,11 +46,10 @@ struct text_params_t {
enum {RECTS, SNIPPETS} returnType;
std::vector<rectangle> returnRects;
std::vector<snippet_t> snippets;
// Pre-calculated line wrapping:
break_info_t break_info;
};
// last_line_break, last_word_break
typedef std::vector<std::pair<unsigned short, unsigned short>> break_info_t;
static void push_snippets(size_t start, size_t end, text_params_t& options, size_t& iHilite, const std::string& str, location loc) {
std::vector<hilite_t>& hilites = options.hilite_ranges;
std::vector<snippet_t>& snippets = options.snippets;
@@ -90,14 +89,57 @@ static void push_snippets(size_t start, size_t end, text_params_t& options, size
} while(start < upper_bound);
}
break_info_t calculate_line_wrapping(rectangle dest_rect, std::string str, TextStyle style) {
break_info_t break_info;
if(str.empty()) return break_info; // Nothing to do!
short line_height = style.lineHeight;
sf::Text str_to_draw;
style.applyTo(str_to_draw);
short str_len = str.length();
unsigned short last_line_break = 0,last_word_break = 0;
str_to_draw.setString(str);
short total_width = str_to_draw.getLocalBounds().width;
if(total_width < dest_rect.width()){
// The text fits on one line, so break_info won't end up being used by win_draw_string anyway
return break_info;
}
auto text_len = [&str_to_draw](size_t i) -> int {
return str_to_draw.findCharacterPos(i).x;
};
short i;
for(i = 0; text_len(i) != text_len(i + 1) && i < str_len; i++) {
if(((text_len(i) - text_len(last_line_break) > (dest_rect.width() - 6))
&& (last_word_break >= last_line_break)) || (str[i] == '|')) {
if(str[i] == '|') {
last_word_break = i + 1;
} else if(last_line_break == last_word_break)
last_word_break = i;
break_info.push_back(std::make_pair(last_line_break, last_word_break));
last_line_break = last_word_break;
}
if(str[i] == ' ')
last_word_break = i + 1;
}
if(i - last_line_break > 0) {
std::string snippet = str.substr(last_line_break);
if(!snippet.empty())
break_info.push_back(std::make_pair(last_line_break, str.length() + 1));
}
return break_info;
}
static void win_draw_string(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,text_params_t& options) {
if(str.empty()) return; // Nothing to do!
short line_height = options.style.lineHeight;
sf::Text str_to_draw;
options.style.applyTo(str_to_draw);
short str_len;
unsigned short last_line_break = 0,last_word_break = 0;
short total_width = 0;
short adjust_x = 1, adjust_y = 10;
str_to_draw.setString("fj"); // Something that has both an ascender and a descender
@@ -105,53 +147,36 @@ static void win_draw_string(sf::RenderTarget& dest_window,rectangle dest_rect,st
adjust_y -= str_to_draw.getLocalBounds().height;
str_to_draw.setString(str);
str_len = str.length();
if(str_len == 0) {
return;
}
short total_width = str_to_draw.getLocalBounds().width;
eTextMode mode = options.mode;
total_width = str_to_draw.getLocalBounds().width;
if(mode == eTextMode::WRAP && total_width < dest_rect.width())
mode = eTextMode::LEFT_TOP;
if(mode == eTextMode::LEFT_TOP && str.find('|') != std::string::npos)
mode = eTextMode::WRAP;
auto text_len = [&str_to_draw](size_t i) -> int {
return str_to_draw.findCharacterPos(i).x;
};
// Special stuff
size_t iHilite = 0;
location moveTo;
line_height -= 2; // TODO: ...why are we arbitrarily reducing the line height from the requested value?
break_info_t break_info;
if(!options.showBreaks){
for(int i=0; i < str.length(); ++i){
if(str[i] == '|') str[i] = ' ';
}
}
if(mode == eTextMode::WRAP) {
moveTo = location(dest_rect.left + adjust_x, dest_rect.top + adjust_y);
short i;
for(i = 0; text_len(i) != text_len(i + 1) && i < str_len; i++) {
if(((text_len(i) - text_len(last_line_break) > (dest_rect.width() - 6))
&& (last_word_break >= last_line_break)) || (str[i] == '|')) {
if(str[i] == '|') {
if(!options.showBreaks) str[i] = ' ';
last_word_break = i + 1;
} else if(last_line_break == last_word_break)
last_word_break = i;
break_info.push_back(std::make_pair(last_line_break, last_word_break));
last_line_break = last_word_break;
}
if(str[i] == ' ')
last_word_break = i + 1;
break_info_t break_info = options.break_info;
// It is better to pre-calculate line-wrapping and pass it in the options,
// but if you don't, this will handle line-wrapping every frame:
if(break_info.empty()){
break_info = calculate_line_wrapping(dest_rect, str, options.style);
}
if(i - last_line_break > 0) {
std::string snippet = str.substr(last_line_break);
if(!snippet.empty())
break_info.push_back(std::make_pair(last_line_break, str.length() + 1));
}
moveTo = location(dest_rect.left + adjust_x, dest_rect.top + adjust_y);
for(auto break_info_pair : break_info){
push_snippets(break_info_pair.first, break_info_pair.second, options, iHilite, str, moveTo);
@@ -210,6 +235,14 @@ void win_draw_string(sf::RenderTarget& dest_window,rectangle dest_rect,std::stri
win_draw_string(dest_window, dest_rect, str, params);
}
void win_draw_string(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,eTextMode mode,TextStyle style,break_info_t break_info) {
text_params_t params;
params.mode = mode;
params.style = style;
params.break_info = break_info;
win_draw_string(dest_window, dest_rect, str, params);
}
std::vector<rectangle> draw_string_hilite(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,TextStyle style,std::vector<hilite_t> hilites,sf::Color hiliteClr) {
text_params_t params;
params.mode = eTextMode::WRAP;

View File

@@ -37,6 +37,9 @@ struct TextStyle {
void applyTo(sf::Text& text);
};
// elements: std::make_pair(last_line_break, last_word_break)
typedef std::vector<std::pair<unsigned short, unsigned short>> break_info_t;
struct snippet_t {
std::string text;
location at;
@@ -53,6 +56,8 @@ enum class eTextMode {
std::vector<rectangle> draw_string_hilite(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,TextStyle style,std::vector<hilite_t> hilites,sf::Color hiliteClr);
std::vector<snippet_t> draw_string_sel(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,TextStyle style,std::vector<hilite_t> hilites,sf::Color hiliteClr);
void win_draw_string(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,eTextMode mode,TextStyle style);
void win_draw_string(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,eTextMode mode,TextStyle style, break_info_t break_info);
break_info_t calculate_line_wrapping(rectangle dest_rect, std::string str, TextStyle style);
size_t string_length(std::string str, TextStyle style, short* height = nullptr);
#endif