Created
March 21, 2020 21:11
-
-
Save lighth7015/1f1edf3142452034940953164d6687f7 to your computer and use it in GitHub Desktop.
FLTK text justification
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Compile with FLTK (for Windows, OSX or Linux) | |
#include <FL/Fl.H> | |
#include <FL/Fl_Double_Window.H> | |
#include <FL/fl_draw.H> | |
#include <FL/Fl_Box.H> | |
#include <sstream> | |
#include <iterator> | |
#include <utility> | |
#include <string> | |
#include <vector> | |
#include <stdint.h> | |
const uint32_t CalcTextPadding(std::vector<std::string> line, uint32_t maxWidth ) { | |
uint32_t pTextSum = 0; | |
for ( std::string word : line ) { | |
for ( char c : word ) { | |
std::string letter( 1, c ); | |
pTextSum += fl_width( letter.c_str()); | |
} | |
} | |
uint32_t length = maxWidth - pTextSum; | |
return line.size() > 1? length / line.size() - 1: length; | |
} | |
auto ends_with (const std::string& input, const std::string &p ) -> bool { | |
bool result = false; | |
if (input.length() >= p.length()) { | |
result = (0 == input.compare (input.length() - p.length(), p.length(), p)); | |
} | |
return result; | |
} | |
class TextLabel: public Fl_Box { | |
typedef std::vector<std::string> Collection; | |
int calcLeftX, tw, th, sh, sw; | |
auto tokenize( std::string textBuf ) -> Collection { | |
std::istringstream stream(textBuf); | |
return Collection( std::istream_iterator<std::string>{stream}, | |
std::istream_iterator<std::string>()); | |
} | |
public: | |
TextLabel( const char* text, uint32_t x, uint32_t y, uint32_t width, uint32_t height) | |
: Fl_Box( x, y, width, height) | |
{ | |
copy_label(text); | |
align(FL_ALIGN_CENTER | FL_ALIGN_CLIP | FL_ALIGN_INSIDE | FL_ALIGN_WRAP); | |
color(FL_WHITE); | |
box(FL_FLAT_BOX); | |
} | |
void draw() override { | |
// DEBUG | |
Fl_Font faceName(fl_font()); | |
Fl_Fontsize faceSize(fl_size()); | |
fl_font(labelfont(), labelsize()); | |
fl_measure("W", sw, sh); | |
uint32_t top(y()); | |
uint32_t width = w(), start = x(), left = start; | |
/* | |
* make the box visible | |
*/ | |
draw_box(); | |
fl_color(FL_BLACK); | |
std::vector<Collection> lines; | |
Collection line; | |
int longestWord = 0, maxChars = width / sw , pos = 0, clw = 0, plw = 0; | |
for( std::string word : tokenize(label( ))) { | |
fl_measure(word.c_str(), tw, th); | |
left += tw + sw; | |
plw = clw; | |
clw += tw + sw; | |
if (longestWord < word.length()) { | |
longestWord = word.length(); | |
} | |
if (clw + fl_width(word.c_str()) > width) { | |
printf("Word (\"%s\", %-3d, %-2d) wraps ", word.c_str(), tw, sw / 2 ); | |
if (word.length() < longestWord) { | |
if ( plw + tw > width ) { | |
printf("next word to new line, clw(%d), width(%d)\n", clw, width); | |
line.push_back(word); | |
lines.push_back(line); | |
line.clear(); | |
} | |
else { | |
if (width - clw <= 15) { | |
printf("next word to new line, clw(%d), width(%d)\n", clw, width); | |
line.push_back(word); | |
lines.push_back(line); | |
line.clear(); | |
} | |
else { | |
printf("to new line, clw(%d), width(%d)\n", clw, width); | |
lines.push_back(line); | |
line.clear(); | |
line.push_back(word); | |
} | |
} | |
} | |
else { | |
printf("to new line, clw(%d), width(%d)\n", clw, width); | |
if (width - clw <= 25) { | |
line.push_back(word); | |
lines.push_back(line); | |
line.clear(); | |
} | |
else { | |
lines.push_back(line); | |
line.clear(); | |
line.push_back(word); | |
} | |
} | |
clw = 0; | |
} | |
else { | |
printf("Word \"%s\", clw = %d, width = %d\n", word.c_str(), clw, width); | |
line.push_back(word); | |
} | |
} | |
if (line.size() > 0) { | |
lines.push_back(line); | |
line.clear(); | |
} | |
fl_push_clip(x(), y(), w(), h()); | |
const uint32_t sxW(fl_width(" ") - 1); | |
for ( Collection row : lines ) { | |
left = start; | |
fl_color(labelcolor()); | |
int offset = (sw / 2) + (width / ((sw / 2) - longestWord)) / row.size(); | |
uint32_t unit = (sw / 2) * 1.25, spacing = 0, spwAvailable = width; | |
const uint32_t candidate = CalcTextPadding( row, w() ); | |
uint32_t sx = spacing < row.size()? spacing: 1; | |
if (candidate > 0 && row.size() >= 1) { | |
spacing = candidate / row.size(); | |
} | |
for ( std::string word: row ) { | |
fl_measure(word.c_str(), tw, th); | |
fl_draw( word.c_str(), left + 4, top + ((( th - 1 ) / 2) * 2) + 1); | |
if (tw + sw + unit <= spwAvailable) { | |
spwAvailable -= tw + sw + unit; | |
left += tw + (sxW * spacing); | |
} | |
else { | |
left += tw + spacing + sxW; | |
} | |
left += sx; | |
} | |
top += (sh / 2) + th; | |
} | |
fl_pop_clip(); | |
fl_color(FL_BLACK); | |
fl_font(faceName, faceSize); | |
} | |
}; | |
class Application: public Fl_Double_Window { | |
static const char* caption; | |
TextLabel* textLabel = new TextLabel( caption, 15, 15, 295, 170 ); | |
int status = 1; | |
public: | |
Application() | |
: Fl_Double_Window( 320, 200 ) | |
{ | |
resizable(textLabel); | |
show(); | |
status = Fl::run(); | |
} | |
int result() { | |
return status; | |
} | |
}; | |
const char* Application::caption = | |
"This is an fltk widget in a box, and is a very long " | |
"and lovely box. It is also very long to demonstrate " | |
"the general inability to properly wrap text."; | |
Application app; | |
int main() { | |
return app.result(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment