Skip to content

Instantly share code, notes, and snippets.

@lighth7015
Created March 21, 2020 21:11
Show Gist options
  • Save lighth7015/1f1edf3142452034940953164d6687f7 to your computer and use it in GitHub Desktop.
Save lighth7015/1f1edf3142452034940953164d6687f7 to your computer and use it in GitHub Desktop.
FLTK text justification
// 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