From c08a10867ca51ac84bdc483824bb2626b3975a49 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Mon, 12 Aug 2024 15:53:55 -0500 Subject: [PATCH] cTextMsg pre-calculate layout --- src/dialogxml/widgets/message.cpp | 47 +++++++------ src/dialogxml/widgets/message.hpp | 7 ++ src/gfx/render_text.cpp | 107 +++++++++++++++++++----------- src/gfx/render_text.hpp | 5 ++ 4 files changed, 108 insertions(+), 58 deletions(-) diff --git a/src/dialogxml/widgets/message.cpp b/src/dialogxml/widgets/message.cpp index 857dc205..3d692b6d 100644 --- a/src/dialogxml/widgets/message.cpp +++ b/src/dialogxml/widgets/message.cpp @@ -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); } } diff --git a/src/dialogxml/widgets/message.hpp b/src/dialogxml/widgets/message.hpp index 0efd30af..12730d7e 100644 --- a/src/dialogxml/widgets/message.hpp +++ b/src/dialogxml/widgets/message.hpp @@ -55,5 +55,12 @@ private: std::vector> 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 diff --git a/src/gfx/render_text.cpp b/src/gfx/render_text.cpp index c3891a27..e0522f7f 100644 --- a/src/gfx/render_text.cpp +++ b/src/gfx/render_text.cpp @@ -46,11 +46,10 @@ struct text_params_t { enum {RECTS, SNIPPETS} returnType; std::vector returnRects; std::vector snippets; + // Pre-calculated line wrapping: + break_info_t break_info; }; -// last_line_break, last_word_break -typedef std::vector> 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& hilites = options.hilite_ranges; std::vector& 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 draw_string_hilite(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,TextStyle style,std::vector hilites,sf::Color hiliteClr) { text_params_t params; params.mode = eTextMode::WRAP; diff --git a/src/gfx/render_text.hpp b/src/gfx/render_text.hpp index 24ce3806..c556bf90 100644 --- a/src/gfx/render_text.hpp +++ b/src/gfx/render_text.hpp @@ -37,6 +37,9 @@ struct TextStyle { void applyTo(sf::Text& text); }; +// elements: std::make_pair(last_line_break, last_word_break) +typedef std::vector> break_info_t; + struct snippet_t { std::string text; location at; @@ -53,6 +56,8 @@ enum class eTextMode { std::vector draw_string_hilite(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,TextStyle style,std::vector hilites,sf::Color hiliteClr); std::vector draw_string_sel(sf::RenderTarget& dest_window,rectangle dest_rect,std::string str,TextStyle style,std::vector 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