Merge pull request #1028 from wesnoth/large-gui-canvas

Double maximum height of text labels
This commit is contained in:
Jyrki Vesterinen 2017-06-02 14:29:05 +03:00 committed by GitHub
commit 15fe590d0f
15 changed files with 943 additions and 197 deletions

View file

@ -3580,7 +3580,6 @@
<ClInclude Include="..\..\src\font\pango\escape.hpp" />
<ClInclude Include="..\..\src\font\pango\font.hpp" />
<ClInclude Include="..\..\src\font\pango\hyperlink.hpp" />
<ClInclude Include="..\..\src\font\pango\iter.hpp" />
<ClInclude Include="..\..\src\font\pango\stream_ops.hpp" />
<ClInclude Include="..\..\src\font\sdl_ttf.hpp" />
<ClInclude Include="..\..\src\font\standard_colors.hpp" />

View file

@ -2961,9 +2961,6 @@
<ClInclude Include="..\..\src\font\pango\hyperlink.hpp">
<Filter>Font\Pango</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\pango\iter.hpp">
<Filter>Font\Pango</Filter>
</ClInclude>
<ClInclude Include="..\..\src\font\pango\stream_ops.hpp">
<Filter>Font\Pango</Filter>
</ClInclude>

View file

@ -175,6 +175,7 @@
<ClInclude Include="..\..\src\serialization\preprocessor.hpp" />
<ClInclude Include="..\..\src\serialization\schema_validator.hpp" />
<ClInclude Include="..\..\src\serialization\string_utils.hpp" />
<ClInclude Include="..\..\src\serialization\string_view.hpp" />
<ClInclude Include="..\..\src\serialization\tokenizer.hpp" />
<ClInclude Include="..\..\src\serialization\unicode.hpp" />
<ClInclude Include="..\..\src\serialization\validator.hpp" />

View file

@ -130,6 +130,9 @@
<Filter>spirit_po</Filter>
</ClInclude>
<ClInclude Include="..\..\src\spirit_po.hpp" />
<ClInclude Include="..\..\src\serialization\string_view.hpp">
<Filter>Serialization</Filter>
</ClInclude>
<ClInclude Include="..\..\src\utils\type_trait_aliases.hpp">
<Filter>Utils</Filter>
</ClInclude>

View file

@ -1,47 +0,0 @@
/*
Copyright (C) 2008 - 2017 by Mark de Wever <koraq@xs4all.nl>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include <pango/pango.h>
namespace font {
/**
* Small helper wrapper for PangoLayoutIter*.
*
* Needed to make sure it gets freed properly.
*/
class p_itor
{
public:
explicit p_itor(PangoLayout* layout_)
: itor_(pango_layout_get_iter(layout_))
{
}
p_itor(const p_itor &) = delete;
p_itor & operator = (const p_itor &) = delete;
~p_itor() { pango_layout_iter_free(itor_); }
operator PangoLayoutIter*() { return itor_; }
private:
PangoLayoutIter* itor_;
};
} // end namespace font

View file

@ -21,7 +21,6 @@
#include "font/pango/escape.hpp"
#include "font/pango/font.hpp"
#include "font/pango/hyperlink.hpp"
#include "font/pango/iter.hpp"
#include "font/pango/stream_ops.hpp"
#include "gettext.hpp"
@ -35,17 +34,19 @@
#include <cassert>
#include <cstring>
#include <stdexcept>
namespace font {
pango_text::pango_text()
#if PANGO_VERSION_CHECK(1,22,0)
: context_(pango_font_map_create_context(pango_cairo_font_map_get_default()))
: context_(pango_font_map_create_context(pango_cairo_font_map_get_default()), g_object_unref)
#else
: context_(pango_cairo_font_map_create_context((
reinterpret_cast<PangoCairoFontMap*>(pango_cairo_font_map_get_default()))))
reinterpret_cast<PangoCairoFontMap*>(pango_cairo_font_map_get_default()))), g_object_unref)
#endif
, layout_(pango_layout_new(context_))
, layout_(pango_layout_new(context_.get()), g_object_unref)
, sublayouts_()
, rect_()
, surface_()
, text_()
@ -68,39 +69,28 @@ pango_text::pango_text()
, surface_buffer_()
{
// With 72 dpi the sizes are the same as with SDL_TTF so hardcoded.
pango_cairo_context_set_resolution(context_, 72.0);
pango_cairo_context_set_resolution(context_.get(), 72.0);
pango_layout_set_ellipsize(layout_, ellipse_mode_);
pango_layout_set_alignment(layout_, alignment_);
pango_layout_set_wrap(layout_, PANGO_WRAP_WORD_CHAR);
pango_layout_set_ellipsize(layout_.get(), ellipse_mode_);
pango_layout_set_alignment(layout_.get(), alignment_);
pango_layout_set_wrap(layout_.get(), PANGO_WRAP_WORD_CHAR);
/*
* Set the pango spacing a bit bigger since the default is deemed to small
* http://www.wesnoth.org/forum/viewtopic.php?p=358832#p358832
*/
pango_layout_set_spacing(layout_, 4 * PANGO_SCALE);
pango_layout_set_spacing(layout_.get(), 4 * PANGO_SCALE);
cairo_font_options_t *fo = cairo_font_options_create();
cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
cairo_font_options_set_hint_metrics(fo, CAIRO_HINT_METRICS_ON);
cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_DEFAULT);
pango_cairo_context_set_font_options(context_, fo);
pango_cairo_context_set_font_options(context_.get(), fo);
cairo_font_options_destroy(fo);
}
pango_text::~pango_text()
{
if(context_) {
g_object_unref(context_);
}
if(layout_) {
g_object_unref(layout_);
}
surface_.assign(nullptr);
}
surface& pango_text::render() const
surface& pango_text::render()
{
this->rerender();
return surface_;
@ -128,7 +118,7 @@ bool pango_text::is_truncated() const
{
this->recalculate();
return (pango_layout_is_ellipsized(layout_) != 0);
return (pango_layout_is_ellipsized(layout_.get()) != 0);
}
unsigned pango_text::insert_text(const unsigned offset, const std::string& text)
@ -169,22 +159,23 @@ gui2::point pango_text::get_cursor_position(
// First we need to determine the byte offset, if more routines need it it
// would be a good idea to make it a separate function.
p_itor itor{layout_};
std::unique_ptr<PangoLayoutIter, std::function<void(PangoLayoutIter*)>> itor(
pango_layout_get_iter(layout_.get()), pango_layout_iter_free);
// Go the wanted line.
if(line != 0) {
if(pango_layout_get_line_count(layout_) >= static_cast<int>(line)) {
if(pango_layout_get_line_count(layout_.get()) >= static_cast<int>(line)) {
return gui2::point(0, 0);
}
for(size_t i = 0; i < line; ++i) {
pango_layout_iter_next_line(itor);
pango_layout_iter_next_line(itor.get());
}
}
// Go the wanted column.
for(size_t i = 0; i < column; ++i) {
if(!pango_layout_iter_next_char(itor)) {
if(!pango_layout_iter_next_char(itor.get())) {
// It seems that the documentation is wrong and causes and off by
// one error... the result should be false if already at the end of
// the data when started.
@ -197,11 +188,11 @@ gui2::point pango_text::get_cursor_position(
}
// Get the byte offset
const int offset = pango_layout_iter_get_index(itor);
const int offset = pango_layout_iter_get_index(itor.get());
// Convert the byte offset in a position.
PangoRectangle rect;
pango_layout_get_cursor_pos(layout_, offset, &rect, nullptr);
pango_layout_get_cursor_pos(layout_.get(), offset, &rect, nullptr);
return gui2::point(PANGO_PIXELS(rect.x), PANGO_PIXELS(rect.y));
}
@ -212,12 +203,12 @@ std::string pango_text::get_token(const gui2::point & position, const char * del
// Get the index of the character.
int index, trailing;
if (!pango_layout_xy_to_index(layout_, position.x * PANGO_SCALE,
if (!pango_layout_xy_to_index(layout_.get(), position.x * PANGO_SCALE,
position.y * PANGO_SCALE, &index, &trailing)) {
return "";
}
std::string txt = pango_layout_get_text(layout_);
std::string txt = pango_layout_get_text(layout_.get());
std::string d(delim);
@ -259,12 +250,12 @@ gui2::point pango_text::get_column_line(const gui2::point& position) const
// Get the index of the character.
int index, trailing;
pango_layout_xy_to_index(layout_, position.x * PANGO_SCALE,
pango_layout_xy_to_index(layout_.get(), position.x * PANGO_SCALE,
position.y * PANGO_SCALE, &index, &trailing);
// Extract the line and the offset in pixels in that line.
int line, offset;
pango_layout_index_to_line_x(layout_, index, trailing, &line, &offset);
pango_layout_index_to_line_x(layout_.get(), index, trailing, &line, &offset);
offset = PANGO_PIXELS(offset);
// Now convert this offset to a column, this way is a bit hacky but haven't
@ -290,7 +281,10 @@ gui2::point pango_text::get_column_line(const gui2::point& position) const
bool pango_text::set_text(const std::string& text, const bool markedup)
{
if(markedup != markedup_text_ || text != text_) {
// assert(layout_);
sublayouts_.clear();
if(layout_ == nullptr) {
layout_.reset(pango_layout_new(context_.get()));
}
const ucs4::string wide = unicode_cast<ucs4::string>(text);
const std::string narrow = unicode_cast<utf8::string>(wide);
@ -300,7 +294,7 @@ bool pango_text::set_text(const std::string& text, const bool markedup)
<< "' contains invalid utf-8, trimmed the invalid parts.\n";
}
if(markedup) {
if(!this->set_markup(narrow)) {
if(!this->set_markup(narrow, *layout_)) {
return false;
}
} else {
@ -309,8 +303,8 @@ bool pango_text::set_text(const std::string& text, const bool markedup)
* leave the layout in an undefined state regarding markup so
* clear it unconditionally.
*/
pango_layout_set_attributes(layout_, nullptr);
pango_layout_set_text(layout_, narrow.c_str(), narrow.size());
pango_layout_set_attributes(layout_.get(), nullptr);
pango_layout_set_text(layout_.get(), narrow.c_str(), narrow.size());
}
text_ = narrow;
length_ = wide.size();
@ -421,7 +415,7 @@ pango_text& pango_text::set_maximum_height(int height, bool multiline)
if(height != maximum_height_) {
// assert(context_);
pango_layout_set_height(layout_, !multiline ? -1 : height * PANGO_SCALE);
pango_layout_set_height(layout_.get(), !multiline ? -1 : height * PANGO_SCALE);
maximum_height_ = height;
calculation_dirty_ = true;
surface_dirty_ = true;
@ -435,7 +429,7 @@ pango_text& pango_text::set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
if(ellipse_mode != ellipse_mode_) {
// assert(context_);
pango_layout_set_ellipsize(layout_, ellipse_mode);
pango_layout_set_ellipsize(layout_.get(), ellipse_mode);
ellipse_mode_ = ellipse_mode;
calculation_dirty_ = true;
surface_dirty_ = true;
@ -447,7 +441,7 @@ pango_text& pango_text::set_ellipse_mode(const PangoEllipsizeMode ellipse_mode)
pango_text &pango_text::set_alignment(const PangoAlignment alignment)
{
if (alignment != alignment_) {
pango_layout_set_alignment(layout_, alignment);
pango_layout_set_alignment(layout_.get(), alignment);
alignment_ = alignment;
surface_dirty_ = true;
}
@ -493,87 +487,96 @@ pango_text& pango_text::set_link_color(const color_t& color)
void pango_text::recalculate(const bool force) const
{
if(calculation_dirty_ || force) {
// assert(layout_);
assert(layout_ != nullptr);
calculation_dirty_ = false;
surface_dirty_ = true;
p_font font{get_font_families(font_class_), font_size_, font_style_};
pango_layout_set_font_description(layout_, font.get());
rect_ = calculate_size(*layout_);
}
}
if(font_style_ & pango_text::STYLE_UNDERLINE) {
PangoAttrList *attribute_list = pango_attr_list_new();
pango_attr_list_insert(attribute_list
, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE));
PangoRectangle pango_text::calculate_size(PangoLayout& layout) const
{
PangoRectangle size;
pango_layout_set_attributes (layout_, attribute_list);
pango_attr_list_unref(attribute_list);
}
p_font font{ get_font_families(font_class_), font_size_, font_style_ };
pango_layout_set_font_description(&layout, font.get());
int maximum_width = 0;
if(characters_per_line_ != 0) {
PangoFont* f = pango_font_map_load_font(
pango_cairo_font_map_get_default()
, context_
, font.get());
if(font_style_ & pango_text::STYLE_UNDERLINE) {
PangoAttrList *attribute_list = pango_attr_list_new();
pango_attr_list_insert(attribute_list
, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE));
PangoFontMetrics* m = pango_font_get_metrics(f, nullptr);
pango_layout_set_attributes(&layout, attribute_list);
pango_attr_list_unref(attribute_list);
}
int w = pango_font_metrics_get_approximate_char_width(m);
w *= characters_per_line_;
int maximum_width = 0;
if(characters_per_line_ != 0) {
PangoFont* f = pango_font_map_load_font(
pango_cairo_font_map_get_default(),
context_.get(),
font.get());
maximum_width = ceil(pango_units_to_double(w));
} else {
maximum_width = maximum_width_;
}
PangoFontMetrics* m = pango_font_get_metrics(f, nullptr);
if(maximum_width_ != -1) {
maximum_width = std::min(maximum_width, maximum_width_);
}
int w = pango_font_metrics_get_approximate_char_width(m);
w *= characters_per_line_;
/*
* See set_maximum_width for some more background info as well.
* In order to fix the problem first set a width which seems to render
* correctly then lower it to fit. For the campaigns the 4 does "the
* right thing" for the terrain labels it should use the value 0 to set
* the ellipse properly. Need to see whether this is a bug in pango or
* a bug in my understanding of the pango api.
*/
int hack = 4;
do {
pango_layout_set_width(layout_, maximum_width == -1
? -1
: (maximum_width + hack) * PANGO_SCALE);
pango_layout_get_pixel_extents(layout_, nullptr, &rect_);
maximum_width = ceil(pango_units_to_double(w));
} else {
maximum_width = maximum_width_;
}
DBG_GUI_L << "pango_text::" << __func__
<< " text '" << gui2::debug_truncate(text_)
<< "' maximum_width " << maximum_width
<< " hack " << hack
<< " width " << rect_.x + rect_.width
<< ".\n";
if(maximum_width_ != -1) {
maximum_width = std::min(maximum_width, maximum_width_);
}
--hack;
} while(maximum_width != -1
&& hack >= 0 && rect_.x + rect_.width > maximum_width);
/*
* See set_maximum_width for some more background info as well.
* In order to fix the problem first set a width which seems to render
* correctly then lower it to fit. For the campaigns the 4 does "the
* right thing" for the terrain labels it should use the value 0 to set
* the ellipse properly. Need to see whether this is a bug in pango or
* a bug in my understanding of the pango api.
*/
int hack = 4;
do {
pango_layout_set_width(&layout, maximum_width == -1
? -1
: (maximum_width + hack) * PANGO_SCALE);
pango_layout_get_pixel_extents(&layout, nullptr, &size);
DBG_GUI_L << "pango_text::" << __func__
<< " text '" << gui2::debug_truncate(text_)
<< "' font_size " << font_size_
<< " markedup_text " << markedup_text_
<< " font_style " << std::hex << font_style_ << std::dec
<< " maximum_width " << maximum_width
<< " maximum_height " << maximum_height_
<< " result " << rect_
<< ".\n";
if(maximum_width != -1 && rect_.x + rect_.width > maximum_width) {
DBG_GUI_L << "pango_text::" << __func__
<< " text '" << gui2::debug_truncate(text_)
<< " ' width " << rect_.x + rect_.width
<< " greater as the wanted maximum of " << maximum_width
<< ".\n";
}
<< " text '" << gui2::debug_truncate(text_)
<< "' maximum_width " << maximum_width
<< " hack " << hack
<< " width " << size.x + size.width
<< ".\n";
--hack;
} while(maximum_width != -1
&& hack >= 0 && size.x + size.width > maximum_width);
DBG_GUI_L << "pango_text::" << __func__
<< " text '" << gui2::debug_truncate(text_)
<< "' font_size " << font_size_
<< " markedup_text " << markedup_text_
<< " font_style " << std::hex << font_style_ << std::dec
<< " maximum_width " << maximum_width
<< " maximum_height " << maximum_height_
<< " result " << size
<< ".\n";
if(maximum_width != -1 && size.x + size.width > maximum_width) {
DBG_GUI_L << "pango_text::" << __func__
<< " text '" << gui2::debug_truncate(text_)
<< " ' width " << size.x + size.width
<< " greater as the wanted maximum of " << maximum_width
<< ".\n";
}
return size;
}
/***
@ -633,17 +636,56 @@ static void from_cairo_format(Uint32 & c)
c = (static_cast<Uint32>(a) << 24) | (static_cast<Uint32>(r) << 16) | (static_cast<Uint32>(g) << 8) | static_cast<Uint32>(b);
}
void pango_text::rerender(const bool force) const
void pango_text::render(PangoLayout& layout, const PangoRectangle& rect, const size_t surface_buffer_offset, const unsigned stride)
{
int width = rect.x + rect.width;
int height = rect.y + rect.height;
if(maximum_width_ > 0) { width = std::min(width, maximum_width_); }
if(maximum_height_ > 0) { height = std::min(height, maximum_height_); }
cairo_format_t format = CAIRO_FORMAT_ARGB32;
unsigned char* buffer = &surface_buffer_[surface_buffer_offset];
std::unique_ptr<cairo_surface_t, std::function<void(cairo_surface_t*)>> cairo_surface(
cairo_image_surface_create_for_data(buffer, format, width, height, stride), cairo_surface_destroy);
std::unique_ptr<cairo_t, std::function<void(cairo_t*)>> cr(cairo_create(cairo_surface.get()), cairo_destroy);
if(cairo_status(cr.get()) == CAIRO_STATUS_INVALID_SIZE) {
if(!is_surface_split()) {
split_surface();
PangoRectangle upper_rect = calculate_size(*sublayouts_[0]);
PangoRectangle lower_rect = calculate_size(*sublayouts_[1]);
render(*sublayouts_[0], upper_rect, 0u, stride);
render(*sublayouts_[1], lower_rect, upper_rect.height * stride, stride);
return;
} else {
// If this occurs in practice, it can be fixed by implementing recursive splitting.
throw std::length_error("Text is too long to render");
}
}
/* set color (used for foreground). */
cairo_set_source_rgba(cr.get(),
foreground_color_.r / 256.0,
foreground_color_.g / 256.0,
foreground_color_.b / 256.0,
foreground_color_.a / 256.0
);
pango_cairo_show_layout(cr.get(), &layout);
}
void pango_text::rerender(const bool force)
{
if(surface_dirty_ || force) {
// assert(layout_);
assert(layout_.get());
this->recalculate(force);
surface_dirty_ = false;
// TODO: This looks broken to me. If rect_.x is negative, shouldn't that increase width?
// I think it maybe should be
// int width = (-rect_.x) + rect_.width;
int width = rect_.x + rect_.width;
int height = rect_.y + rect_.height;
if(maximum_width_ > 0) { width = std::min(width, maximum_width_); }
@ -655,23 +697,11 @@ void pango_text::rerender(const bool force) const
this->create_surface_buffer(stride * height);
if (!surface_buffer_.size()) {
// surface_.assign(nullptr);
surface_.assign(create_neutral_surface(0, 0));
return;
}
cairo_surface_t* cairo_surface = cairo_image_surface_create_for_data(&surface_buffer_[0], format, width, height, stride);
cairo_t* cr = cairo_create(cairo_surface);
/* set color (used for foreground). */
cairo_set_source_rgba(cr,
foreground_color_.r / 256.0,
foreground_color_.g / 256.0,
foreground_color_.b / 256.0,
foreground_color_.a / 256.0
);
pango_cairo_show_layout(cr, layout_);
render(*layout_, rect_, 0u, stride);
static_assert(sizeof(Uint32) == 4, "Something is wrong with our typedefs");
@ -688,8 +718,6 @@ void pango_text::rerender(const bool force) const
surface_.assign(SDL_CreateRGBSurfaceFrom(
&surface_buffer_[0], width, height, 32, stride, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000));
cairo_destroy(cr);
cairo_surface_destroy(cairo_surface);
}
}
@ -704,8 +732,8 @@ void pango_text::create_surface_buffer(const size_t size) const
for (auto & c : surface_buffer_) { c = 0; }
}
bool pango_text::set_markup(const std::string & text) {
return this->set_markup_helper(link_aware_ ? this->format_link_tokens(text) : text);
bool pango_text::set_markup(utils::string_view text, PangoLayout& layout) {
return this->set_markup_helper(link_aware_ ? this->format_link_tokens(text.to_string()) : text, layout);
}
std::string pango_text::format_link_tokens(const std::string & text) const {
@ -739,13 +767,13 @@ std::string pango_text::handle_token(const std::string & token) const
}
}
bool pango_text::set_markup_helper(const std::string& text)
bool pango_text::set_markup_helper(utils::string_view text, PangoLayout& layout)
{
if(pango_parse_markup(text.c_str(), text.size()
, 0, nullptr, nullptr, nullptr, nullptr)) {
if(pango_parse_markup(text.data(), text.size(),
0, nullptr, nullptr, nullptr, nullptr)) {
/* Markup is valid so set it. */
pango_layout_set_markup(layout_, text.c_str(), text.size());
pango_layout_set_markup(&layout, text.data(), text.size());
return true;
}
@ -757,7 +785,7 @@ bool pango_text::set_markup_helper(const std::string& text)
* So only try to recover from broken ampersands, by simply replacing them
* with the escaped version.
*/
std::string semi_escaped{semi_escape_text(text)};
std::string semi_escaped{semi_escape_text(text.to_string())};
/*
* If at least one ampersand is replaced the semi-escaped string
@ -773,7 +801,7 @@ bool pango_text::set_markup_helper(const std::string& text)
<< " text '" << text
<< "' has broken markup, set to normal text.\n";
this->set_text(_("The text contains invalid markup: ") + text, false);
this->set_text(_("The text contains invalid markup: ") + text.to_string(), false);
return false;
}
@ -782,8 +810,34 @@ bool pango_text::set_markup_helper(const std::string& text)
<< " text '" << text
<< "' has unescaped ampersands '&', escaped them.\n";
pango_layout_set_markup(layout_, semi_escaped.c_str(), semi_escaped.size());
pango_layout_set_markup(&layout, semi_escaped.c_str(), semi_escaped.size());
return true;
}
void pango_text::split_surface()
{
auto text_parts = utils::vertical_split(text_);
PangoLayout* upper_layout = pango_layout_new(context_.get());
PangoLayout* lower_layout = pango_layout_new(context_.get());
set_markup(text_parts.first, *upper_layout);
set_markup(text_parts.second, *lower_layout);
copy_layout_properties(*layout_, *upper_layout);
copy_layout_properties(*layout_, *lower_layout);
sublayouts_.emplace_back(upper_layout, g_object_unref);
sublayouts_.emplace_back(lower_layout, g_object_unref);
layout_.reset(nullptr);
}
void pango_text::copy_layout_properties(PangoLayout& src, PangoLayout& dst)
{
pango_layout_set_alignment(&dst, pango_layout_get_alignment(&src));
pango_layout_set_height(&dst, pango_layout_get_height(&src));
pango_layout_set_ellipsize(&dst, pango_layout_get_ellipsize(&src));
}
} // namespace font

View file

@ -17,11 +17,14 @@
#include "font/font_options.hpp"
#include "color.hpp"
#include "sdl/surface.hpp"
#include "serialization/string_utils.hpp"
#include "serialization/unicode_types.hpp"
#include <pango/pango.h>
#include <pango/pangocairo.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
@ -81,15 +84,13 @@ public:
pango_text(const pango_text &) = delete;
pango_text & operator = (const pango_text &) = delete;
~pango_text();
/**
* Returns the rendered text.
*
* Before rendering it tests whether a redraw is needed and if so it first
* redraws the surface before returning it.
*/
surface& render() const;
surface& render();
/** Returns the width needed for the text. */
int get_width() const;
@ -246,10 +247,13 @@ public:
private:
/***** ***** ***** ***** Pango variables ***** ***** ***** *****/
PangoContext* context_;
PangoLayout* layout_;
std::unique_ptr<PangoContext, std::function<void(void*)>> context_;
std::unique_ptr<PangoLayout, std::function<void(void*)>> layout_;
mutable PangoRectangle rect_;
// Used if the text is too long to fit into a single Cairo surface.
std::vector<std::unique_ptr<PangoLayout, std::function<void(void*)>>> sublayouts_;
/** The SDL surface to render upon used as a cache. */
mutable surface surface_;
@ -350,6 +354,9 @@ private:
*/
void recalculate(const bool force = false) const;
/** Calculates surface size. */
PangoRectangle calculate_size(PangoLayout& layout) const;
/** The dirty state of the surface. */
mutable bool surface_dirty_;
@ -361,7 +368,10 @@ private:
* @param force Render even if not dirty? This parameter is
* also send to recalculate().
*/
void rerender(const bool force = false) const;
void rerender(const bool force = false);
void render(PangoLayout& layout, const PangoRectangle& rect,
const size_t surface_buffer_offset, const unsigned stride);
/**
* Buffer to store the image on.
@ -398,11 +408,32 @@ private:
* unrecoverable error occurred and the text is
* set as plain text with an error message.
*/
bool set_markup(const std::string& text);
bool set_markup(utils::string_view text, PangoLayout& layout);
bool set_markup_helper(const std::string & text);
bool set_markup_helper(utils::string_view text, PangoLayout& layout);
std::string format_link_tokens(const std::string & text) const;
/** Splits the text to two Cairo surfaces.
*
* The implementation isn't recursive: the function only splits the text once.
* As a result, it only doubles the maximum surface height to 64,000 pixels
* or so.
* The reason for this is that a recursive implementation would be more complex
* and it's unnecessary for now, as the longest surface in the game
* (end credits) is only about 40,000 pixels high with the default_large widget
* definition.
* If we need even larger surfaces in the future, the implementation can be made
* recursive.
*/
void split_surface();
bool is_surface_split() const
{
return sublayouts_.size() > 0;
}
static void copy_layout_properties(PangoLayout& src, PangoLayout& dst);
std::string format_link_tokens(const std::string & text) const;
std::string handle_token(const std::string & token) const;
};

View file

@ -71,10 +71,12 @@ static void parse_about_tag(const config& cfg, std::stringstream& ss)
void end_credits::pre_show(window& window)
{
timer_id_ = add_timer(10, std::bind(&end_credits::timer_callback, this), true);
// Delay a little before beginning the scrolling
last_scroll_ = SDL_GetTicks() + 3000;
window.set_callback_next_draw([this]()
{
timer_id_ = add_timer(10, std::bind(&end_credits::timer_callback, this), true);
// Delay a little before beginning the scrolling
last_scroll_ = SDL_GetTicks() + 3000;
});
connect_signal_pre_key_press(window, std::bind(&end_credits::key_press_callback, this, _5));
@ -125,6 +127,7 @@ void end_credits::pre_show(window& window)
text_widget_ = find_widget<scroll_label>(&window, "text", false, true);
text_widget_->set_use_markup(true);
text_widget_->set_link_aware(false);
text_widget_->set_label((focus_ss.str().empty() ? ss : focus_ss).str());
// HACK: always hide the scrollbar, even if it's needed.

View file

@ -103,6 +103,13 @@ void scroll_label::set_text_alpha(unsigned short alpha)
}
}
void scroll_label::set_link_aware(bool l)
{
if(label* widget = get_internal_label()) {
widget->set_link_aware(l);
}
}
void scroll_label::set_self_active(const bool active)
{
state_ = active ? ENABLED : DISABLED;

View file

@ -71,6 +71,8 @@ public:
void set_text_alpha(unsigned short alpha);
void set_link_aware(bool l);
private:
/**
* Possible states of the widget.

View file

@ -840,6 +840,11 @@ void window::draw()
std::vector<widget*> call_stack;
populate_dirty_list(*this, call_stack);
assert(dirty_list_.empty());
if(callback_next_draw_ != nullptr) {
callback_next_draw_();
callback_next_draw_ = nullptr;
}
}
void window::undraw()

View file

@ -28,6 +28,7 @@
#include "gui/core/window_builder.hpp"
#include "gui/widgets/panel.hpp"
#include <functional>
#include <map>
#include <memory>
#include <string>
@ -490,6 +491,17 @@ public:
};
}
/**
* Sets a callback that will be called after the window is drawn next time.
* The callback is automatically removed after calling it once.
* Useful if you need to do something after the window is drawn for the first time
* and it's timing-sensitive (i.e. pre_show is too early).
*/
void set_callback_next_draw(std::function<void()> func)
{
callback_next_draw_ = func;
}
private:
/** Needed so we can change what's drawn on the screen. */
CVideo& video_;
@ -797,6 +809,7 @@ private:
bool& handled);
std::function<bool(window&)> exit_hook_ = [](window&)->bool { return true; };
std::function<void()> callback_next_draw_;
};
// }---------- DEFINITION ---------{

View file

@ -26,6 +26,7 @@
#include "utils/general.hpp"
#include <cassert>
#include <array>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
@ -381,6 +382,35 @@ std::vector<std::string> parenthetical_split(const std::string& val,
return res;
}
std::pair<string_view, string_view> vertical_split(const std::string& val)
{
// Count the number of lines.
int num_lines = std::count(val.begin(), val.end(), '\n') + 1;
if(num_lines < 2) {
throw std::logic_error("utils::vertical_split: the string contains only one line");
}
// Split the string at the point where we have encountered
// (number of lines / 2 - 1) line separators.
int split_point = 0;
int num_found_line_separators = 0;
for(size_t i = 0; i < val.size(); ++i) {
if(val[i] == '\n') {
++num_found_line_separators;
if(num_found_line_separators >= num_lines / 2 - 1) {
split_point = i;
break;
}
}
}
assert(split_point != 0);
return { string_view(val.data(), split_point),
string_view(&val[split_point + 1], val.size() - (split_point + 1)) };
}
// Modify a number by string representing integer difference, or optionally %
int apply_modifier( const int number, const std::string &amount, const int minimum ) {
// wassert( amount.empty() == false );

View file

@ -16,12 +16,15 @@
#pragma once
#include "font/constants.hpp"
#include "serialization/string_view.hpp"
#include <algorithm>
#include <map>
#include <ostream>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
class t_string;
@ -144,6 +147,19 @@ std::vector<std::string> square_parenthetical_split(
const std::string& right = ")]",
const int flags = REMOVE_EMPTY | STRIP_SPACES);
/**
* Splits a string into two parts as evenly as possible based on lines.
* For example, if the string contains 3288 lines, then both parts will
* be 1644 lines long.
*
* The line separator in between won't be in either of the parts the
* function returns.
*
* Because this function is intended for extremely long strings
* (kilobytes long), it returns string_views for performance.
*/
std::pair<string_view, string_view> vertical_split(const std::string& val);
/**
* Generates a new string joining container items in a list.
*

View file

@ -0,0 +1,632 @@
/*
Copyright (c) Marshall Clow 2012-2015.
Copyright (c) Beman Dawes 2015
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/* This file is the Boost string_view implementation in a single header file.
We have an in-tree copy because not all versions of Boost we support have
that class. */
#pragma once
#if BOOST_VERSION > 106100
/* Boost string_view is available, so we can just use it. */
#include <boost/utility/string_view.hpp>
namespace utils {
using string_view = boost::string_view;
}
#else
#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/utility/string_view_fwd.hpp>
#include <boost/throw_exception.hpp>
#include <cstddef>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <string>
#include <cstring>
#include <iosfwd>
#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || (defined(BOOST_GCC) && ((BOOST_GCC+0) / 100) <= 406)
// GCC 4.6 cannot handle a defaulted function with noexcept specifier
#define BOOST_STRING_VIEW_NO_CXX11_DEFAULTED_NOEXCEPT_FUNCTIONS
#endif
namespace utils {
namespace detail {
// A helper functor because sometimes we don't have lambdas
template <typename charT, typename traits>
class string_view_traits_eq {
public:
string_view_traits_eq(charT ch) : ch_(ch) {}
bool operator()(charT val) const { return traits::eq(ch_, val); }
charT ch_;
};
}
template<typename charT, typename traits> // traits defaulted in string_view_fwd.hpp
class basic_string_view {
public:
// types
typedef traits traits_type;
typedef charT value_type;
typedef charT* pointer;
typedef const charT* const_pointer;
typedef charT& reference;
typedef const charT& const_reference;
typedef const_pointer const_iterator; // impl-defined
typedef const_iterator iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef const_reverse_iterator reverse_iterator;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
static BOOST_CONSTEXPR_OR_CONST size_type npos = size_type(-1);
// construct/copy
BOOST_CONSTEXPR basic_string_view() BOOST_NOEXCEPT
: ptr_(NULL), len_(0) {}
// by defaulting these functions, basic_string_ref becomes
// trivially copy/move constructible.
BOOST_CONSTEXPR basic_string_view(const basic_string_view &rhs) BOOST_NOEXCEPT
#ifndef BOOST_STRING_VIEW_NO_CXX11_DEFAULTED_NOEXCEPT_FUNCTIONS
= default;
#else
: ptr_(rhs.ptr_), len_(rhs.len_) {}
#endif
basic_string_view& operator=(const basic_string_view &rhs) BOOST_NOEXCEPT
#ifndef BOOST_STRING_VIEW_NO_CXX11_DEFAULTED_NOEXCEPT_FUNCTIONS
= default;
#else
{
ptr_ = rhs.ptr_;
len_ = rhs.len_;
return *this;
}
#endif
template<typename Allocator>
basic_string_view(const std::basic_string<charT, traits, Allocator>& str) BOOST_NOEXCEPT
: ptr_(str.data()), len_(str.length()) {}
// #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)
// // Constructing a string_view from a temporary string is a bad idea
// template<typename Allocator>
// basic_string_view( std::basic_string<charT, traits, Allocator>&&)
// = delete;
// #endif
BOOST_CONSTEXPR basic_string_view(const charT* str)
: ptr_(str), len_(traits::length(str)) {}
BOOST_CONSTEXPR basic_string_view(const charT* str, size_type len)
: ptr_(str), len_(len) {}
// iterators
BOOST_CONSTEXPR const_iterator begin() const BOOST_NOEXCEPT { return ptr_; }
BOOST_CONSTEXPR const_iterator cbegin() const BOOST_NOEXCEPT { return ptr_; }
BOOST_CONSTEXPR const_iterator end() const BOOST_NOEXCEPT { return ptr_ + len_; }
BOOST_CONSTEXPR const_iterator cend() const BOOST_NOEXCEPT { return ptr_ + len_; }
const_reverse_iterator rbegin() const BOOST_NOEXCEPT { return const_reverse_iterator(end()); }
const_reverse_iterator crbegin() const BOOST_NOEXCEPT { return const_reverse_iterator(end()); }
const_reverse_iterator rend() const BOOST_NOEXCEPT { return const_reverse_iterator(begin()); }
const_reverse_iterator crend() const BOOST_NOEXCEPT { return const_reverse_iterator(begin()); }
// capacity
BOOST_CONSTEXPR size_type size() const BOOST_NOEXCEPT { return len_; }
BOOST_CONSTEXPR size_type length() const BOOST_NOEXCEPT { return len_; }
BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT { return len_; }
BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT { return len_ == 0; }
// element access
BOOST_CONSTEXPR const_reference operator[](size_type pos) const BOOST_NOEXCEPT { return ptr_[pos]; }
BOOST_CONSTEXPR const_reference at(size_t pos) const {
return pos >= len_ ? BOOST_THROW_EXCEPTION(std::out_of_range("boost::string_view::at")), ptr_[0] : ptr_[pos];
}
BOOST_CONSTEXPR const_reference front() const { return ptr_[0]; }
BOOST_CONSTEXPR const_reference back() const { return ptr_[len_ - 1]; }
BOOST_CONSTEXPR const_pointer data() const BOOST_NOEXCEPT { return ptr_; }
// modifiers
void clear() BOOST_NOEXCEPT{ len_ = 0; } // Boost extension
BOOST_CXX14_CONSTEXPR void remove_prefix(size_type n) {
if(n > len_)
n = len_;
ptr_ += n;
len_ -= n;
}
BOOST_CXX14_CONSTEXPR void remove_suffix(size_type n) {
if(n > len_)
n = len_;
len_ -= n;
}
BOOST_CXX14_CONSTEXPR void swap(basic_string_view& s) BOOST_NOEXCEPT {
std::swap(ptr_, s.ptr_);
std::swap(len_, s.len_);
}
// basic_string_view string operations
#ifndef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
template<typename Allocator>
explicit operator std::basic_string<charT, traits, Allocator>() const {
return std::basic_string<charT, traits, Allocator>(begin(), end());
}
#endif
#ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
template<typename Allocator = std::allocator<charT> >
std::basic_string<charT, traits, Allocator> to_string(const Allocator& a = Allocator()) const {
return std::basic_string<charT, traits, Allocator>(begin(), end(), a);
}
#else
std::basic_string<charT, traits> to_string() const {
return std::basic_string<charT, traits>(begin(), end());
}
template<typename Allocator>
std::basic_string<charT, traits, Allocator> to_string(const Allocator& a) const {
return std::basic_string<charT, traits, Allocator>(begin(), end(), a);
}
#endif
size_type copy(charT* s, size_type n, size_type pos = 0) const {
if(pos > size())
BOOST_THROW_EXCEPTION(std::out_of_range("string_view::copy"));
size_type rlen = (std::min)(n, len_ - pos);
traits_type::copy(s, data() + pos, rlen);
return rlen;
}
BOOST_CXX14_CONSTEXPR basic_string_view substr(size_type pos, size_type n = npos) const {
if(pos > size())
BOOST_THROW_EXCEPTION(std::out_of_range("string_view::substr"));
return basic_string_view(data() + pos, (std::min)(size() - pos, n));
}
BOOST_CXX14_CONSTEXPR int compare(basic_string_view x) const BOOST_NOEXCEPT {
const int cmp = traits::compare(ptr_, x.ptr_, (std::min)(len_, x.len_));
return cmp != 0 ? cmp : (len_ == x.len_ ? 0 : len_ < x.len_ ? -1 : 1);
}
BOOST_CXX14_CONSTEXPR int compare(size_type pos1, size_type n1, basic_string_view x)
const BOOST_NOEXCEPT {
return substr(pos1, n1).compare(x);
}
BOOST_CXX14_CONSTEXPR int compare(size_type pos1, size_type n1,
basic_string_view x, size_type pos2, size_type n2) const {
return substr(pos1, n1).compare(x.substr(pos2, n2));
}
BOOST_CXX14_CONSTEXPR int compare(const charT* x) const {
return compare(basic_string_view(x));
}
BOOST_CXX14_CONSTEXPR int compare(size_type pos1, size_type n1, const charT* x) const {
return substr(pos1, n1).compare(basic_string_view(x));
}
BOOST_CXX14_CONSTEXPR int compare(size_type pos1, size_type n1,
const charT* x, size_type n2) const {
return substr(pos1, n1).compare(basic_string_view(x, n2));
}
// Searches
BOOST_CONSTEXPR bool starts_with(charT c) const BOOST_NOEXCEPT { // Boost extension
return !empty() && traits::eq(c, front());
}
BOOST_CONSTEXPR bool starts_with(basic_string_view x) const BOOST_NOEXCEPT { // Boost extension
return len_ >= x.len_ && traits::compare(ptr_, x.ptr_, x.len_) == 0;
}
BOOST_CONSTEXPR bool ends_with(charT c) const BOOST_NOEXCEPT { // Boost extension
return !empty() && traits::eq(c, back());
}
BOOST_CONSTEXPR bool ends_with(basic_string_view x) const BOOST_NOEXCEPT { // Boost extension
return len_ >= x.len_ &&
traits::compare(ptr_ + len_ - x.len_, x.ptr_, x.len_) == 0;
}
// find
BOOST_CXX14_CONSTEXPR size_type find(basic_string_view s, size_type pos = 0) const BOOST_NOEXCEPT {
if(pos > size())
return npos;
if(s.empty())
return pos;
const_iterator iter = std::search(this->cbegin() + pos, this->cend(),
s.cbegin(), s.cend(), traits::eq);
return iter == this->cend() ? npos : std::distance(this->cbegin(), iter);
}
BOOST_CXX14_CONSTEXPR size_type find(charT c, size_type pos = 0) const BOOST_NOEXCEPT
{ return find(basic_string_view(&c, 1), pos); }
BOOST_CXX14_CONSTEXPR size_type find(const charT* s, size_type pos, size_type n) const BOOST_NOEXCEPT
{ return find(basic_string_view(s, n), pos); }
BOOST_CXX14_CONSTEXPR size_type find(const charT* s, size_type pos = 0) const BOOST_NOEXCEPT
{ return find(basic_string_view(s), pos); }
// rfind
BOOST_CXX14_CONSTEXPR size_type rfind(basic_string_view s, size_type pos = npos) const BOOST_NOEXCEPT {
if(len_ < s.len_)
return npos;
if(pos > len_ - s.len_)
pos = len_ - s.len_;
if(s.len_ == 0u) // an empty string is always found
return pos;
for(const charT* cur = ptr_ + pos;; --cur) {
if(traits::compare(cur, s.ptr_, s.len_) == 0)
return cur - ptr_;
if(cur == ptr_)
return npos;
};
}
BOOST_CXX14_CONSTEXPR size_type rfind(charT c, size_type pos = npos) const BOOST_NOEXCEPT
{ return rfind(basic_string_view(&c, 1), pos); }
BOOST_CXX14_CONSTEXPR size_type rfind(const charT* s, size_type pos, size_type n) const BOOST_NOEXCEPT
{ return rfind(basic_string_view(s, n), pos); }
BOOST_CXX14_CONSTEXPR size_type rfind(const charT* s, size_type pos = npos) const BOOST_NOEXCEPT
{ return rfind(basic_string_view(s), pos); }
// find_first_of
BOOST_CXX14_CONSTEXPR size_type find_first_of(basic_string_view s, size_type pos = 0) const BOOST_NOEXCEPT {
if(pos >= len_ || s.len_ == 0)
return npos;
const_iterator iter = std::find_first_of
(this->cbegin() + pos, this->cend(), s.cbegin(), s.cend(), traits::eq);
return iter == this->cend() ? npos : std::distance(this->cbegin(), iter);
}
BOOST_CXX14_CONSTEXPR size_type find_first_of(charT c, size_type pos = 0) const BOOST_NOEXCEPT
{ return find_first_of(basic_string_view(&c, 1), pos); }
BOOST_CXX14_CONSTEXPR size_type find_first_of(const charT* s, size_type pos, size_type n) const BOOST_NOEXCEPT
{ return find_first_of(basic_string_view(s, n), pos); }
BOOST_CXX14_CONSTEXPR size_type find_first_of(const charT* s, size_type pos = 0) const BOOST_NOEXCEPT
{ return find_first_of(basic_string_view(s), pos); }
// find_last_of
BOOST_CXX14_CONSTEXPR size_type find_last_of(basic_string_view s, size_type pos = npos) const BOOST_NOEXCEPT {
if(s.len_ == 0u)
return npos;
if(pos >= len_)
pos = 0;
else
pos = len_ - (pos + 1);
const_reverse_iterator iter = std::find_first_of
(this->crbegin() + pos, this->crend(), s.cbegin(), s.cend(), traits::eq);
return iter == this->crend() ? npos : reverse_distance(this->crbegin(), iter);
}
BOOST_CXX14_CONSTEXPR size_type find_last_of(charT c, size_type pos = npos) const BOOST_NOEXCEPT
{ return find_last_of(basic_string_view(&c, 1), pos); }
BOOST_CXX14_CONSTEXPR size_type find_last_of(const charT* s, size_type pos, size_type n) const BOOST_NOEXCEPT
{ return find_last_of(basic_string_view(s, n), pos); }
BOOST_CXX14_CONSTEXPR size_type find_last_of(const charT* s, size_type pos = npos) const BOOST_NOEXCEPT
{ return find_last_of(basic_string_view(s), pos); }
// find_first_not_of
BOOST_CXX14_CONSTEXPR size_type find_first_not_of(basic_string_view s, size_type pos = 0) const BOOST_NOEXCEPT {
if(pos >= len_)
return npos;
if(s.len_ == 0)
return pos;
const_iterator iter = find_not_of(this->cbegin() + pos, this->cend(), s);
return iter == this->cend() ? npos : std::distance(this->cbegin(), iter);
}
BOOST_CXX14_CONSTEXPR size_type find_first_not_of(charT c, size_type pos = 0) const BOOST_NOEXCEPT
{ return find_first_not_of(basic_string_view(&c, 1), pos); }
BOOST_CXX14_CONSTEXPR size_type find_first_not_of(const charT* s, size_type pos, size_type n) const BOOST_NOEXCEPT
{ return find_first_not_of(basic_string_view(s, n), pos); }
BOOST_CXX14_CONSTEXPR size_type find_first_not_of(const charT* s, size_type pos = 0) const BOOST_NOEXCEPT
{ return find_first_not_of(basic_string_view(s), pos); }
// find_last_not_of
BOOST_CXX14_CONSTEXPR size_type find_last_not_of(basic_string_view s, size_type pos = npos) const BOOST_NOEXCEPT {
if(pos >= len_)
pos = len_ - 1;
if(s.len_ == 0u)
return pos;
pos = len_ - (pos + 1);
const_reverse_iterator iter = find_not_of(this->crbegin() + pos, this->crend(), s);
return iter == this->crend() ? npos : reverse_distance(this->crbegin(), iter);
}
BOOST_CXX14_CONSTEXPR size_type find_last_not_of(charT c, size_type pos = npos) const BOOST_NOEXCEPT
{ return find_last_not_of(basic_string_view(&c, 1), pos); }
BOOST_CXX14_CONSTEXPR size_type find_last_not_of(const charT* s, size_type pos, size_type n) const BOOST_NOEXCEPT
{ return find_last_not_of(basic_string_view(s, n), pos); }
BOOST_CXX14_CONSTEXPR size_type find_last_not_of(const charT* s, size_type pos = npos) const BOOST_NOEXCEPT
{ return find_last_not_of(basic_string_view(s), pos); }
private:
template <typename r_iter>
size_type reverse_distance(r_iter first, r_iter last) const BOOST_NOEXCEPT {
// Portability note here: std::distance is not NOEXCEPT, but calling it with a string_view::reverse_iterator will not throw.
return len_ - 1 - std::distance(first, last);
}
template <typename Iterator>
Iterator find_not_of(Iterator first, Iterator last, basic_string_view s) const BOOST_NOEXCEPT {
for(; first != last; ++first)
if(0 == traits::find(s.ptr_, s.len_, *first))
return first;
return last;
}
const charT *ptr_;
std::size_t len_;
};
// Comparison operators
// Equality
template<typename charT, typename traits>
inline bool operator==(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
if(x.size() != y.size()) return false;
return x.compare(y) == 0;
}
// Inequality
template<typename charT, typename traits>
inline bool operator!=(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
if(x.size() != y.size()) return true;
return x.compare(y) != 0;
}
// Less than
template<typename charT, typename traits>
inline bool operator<(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return x.compare(y) < 0;
}
// Greater than
template<typename charT, typename traits>
inline bool operator>(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return x.compare(y) > 0;
}
// Less than or equal to
template<typename charT, typename traits>
inline bool operator<=(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return x.compare(y) <= 0;
}
// Greater than or equal to
template<typename charT, typename traits>
inline bool operator>=(basic_string_view<charT, traits> x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return x.compare(y) >= 0;
}
// "sufficient additional overloads of comparison functions"
template<typename charT, typename traits, typename Allocator>
inline bool operator==(basic_string_view<charT, traits> x,
const std::basic_string<charT, traits, Allocator> & y) BOOST_NOEXCEPT {
return x == basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits, typename Allocator>
inline bool operator==(const std::basic_string<charT, traits, Allocator> & x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) == y;
}
template<typename charT, typename traits>
inline bool operator==(basic_string_view<charT, traits> x,
const charT * y) BOOST_NOEXCEPT {
return x == basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits>
inline bool operator==(const charT * x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) == y;
}
template<typename charT, typename traits, typename Allocator>
inline bool operator!=(basic_string_view<charT, traits> x,
const std::basic_string<charT, traits, Allocator> & y) BOOST_NOEXCEPT {
return x != basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits, typename Allocator>
inline bool operator!=(const std::basic_string<charT, traits, Allocator> & x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) != y;
}
template<typename charT, typename traits>
inline bool operator!=(basic_string_view<charT, traits> x,
const charT * y) BOOST_NOEXCEPT {
return x != basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits>
inline bool operator!=(const charT * x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) != y;
}
template<typename charT, typename traits, typename Allocator>
inline bool operator<(basic_string_view<charT, traits> x,
const std::basic_string<charT, traits, Allocator> & y) BOOST_NOEXCEPT {
return x < basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits, typename Allocator>
inline bool operator<(const std::basic_string<charT, traits, Allocator> & x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) < y;
}
template<typename charT, typename traits>
inline bool operator<(basic_string_view<charT, traits> x,
const charT * y) BOOST_NOEXCEPT {
return x < basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits>
inline bool operator<(const charT * x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) < y;
}
template<typename charT, typename traits, typename Allocator>
inline bool operator>(basic_string_view<charT, traits> x,
const std::basic_string<charT, traits, Allocator> & y) BOOST_NOEXCEPT {
return x > basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits, typename Allocator>
inline bool operator>(const std::basic_string<charT, traits, Allocator> & x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) > y;
}
template<typename charT, typename traits>
inline bool operator>(basic_string_view<charT, traits> x,
const charT * y) BOOST_NOEXCEPT {
return x > basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits>
inline bool operator>(const charT * x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) > y;
}
template<typename charT, typename traits, typename Allocator>
inline bool operator<=(basic_string_view<charT, traits> x,
const std::basic_string<charT, traits, Allocator> & y) BOOST_NOEXCEPT {
return x <= basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits, typename Allocator>
inline bool operator<=(const std::basic_string<charT, traits, Allocator> & x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) <= y;
}
template<typename charT, typename traits>
inline bool operator<=(basic_string_view<charT, traits> x,
const charT * y) BOOST_NOEXCEPT {
return x <= basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits>
inline bool operator<=(const charT * x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) <= y;
}
template<typename charT, typename traits, typename Allocator>
inline bool operator>=(basic_string_view<charT, traits> x,
const std::basic_string<charT, traits, Allocator> & y) BOOST_NOEXCEPT {
return x >= basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits, typename Allocator>
inline bool operator>=(const std::basic_string<charT, traits, Allocator> & x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) >= y;
}
template<typename charT, typename traits>
inline bool operator>=(basic_string_view<charT, traits> x,
const charT * y) BOOST_NOEXCEPT {
return x >= basic_string_view<charT, traits>(y);
}
template<typename charT, typename traits>
inline bool operator>=(const charT * x,
basic_string_view<charT, traits> y) BOOST_NOEXCEPT {
return basic_string_view<charT, traits>(x) >= y;
}
namespace detail {
template<class charT, class traits>
inline void sv_insert_fill_chars(std::basic_ostream<charT, traits>& os, std::size_t n) {
enum { chunk_size = 8 };
charT fill_chars[chunk_size];
std::fill_n(fill_chars, static_cast< std::size_t >(chunk_size), os.fill());
for(; n >= chunk_size && os.good(); n -= chunk_size)
os.write(fill_chars, static_cast< std::size_t >(chunk_size));
if(n > 0 && os.good())
os.write(fill_chars, n);
}
template<class charT, class traits>
void sv_insert_aligned(std::basic_ostream<charT, traits>& os, const basic_string_view<charT, traits>& str) {
const std::size_t size = str.size();
const std::size_t alignment_size = static_cast< std::size_t >(os.width()) - size;
const bool align_left = (os.flags() & std::basic_ostream<charT, traits>::adjustfield) == std::basic_ostream<charT, traits>::left;
if(!align_left) {
detail::sv_insert_fill_chars(os, alignment_size);
if(os.good())
os.write(str.data(), size);
} else {
os.write(str.data(), size);
if(os.good())
detail::sv_insert_fill_chars(os, alignment_size);
}
}
} // namespace detail
// Inserter
template<class charT, class traits>
inline std::basic_ostream<charT, traits>&
operator<<(std::basic_ostream<charT, traits>& os,
const basic_string_view<charT, traits>& str) {
if(os.good()) {
const std::size_t size = str.size();
const std::size_t w = static_cast< std::size_t >(os.width());
if(w <= size)
os.write(str.data(), size);
else
detail::sv_insert_aligned(os, str);
os.width(0);
}
return os;
}
typedef basic_string_view<char, std::char_traits<char> > string_view;
typedef basic_string_view<wchar_t, std::char_traits<wchar_t> > wstring_view;
#ifndef BOOST_NO_CXX11_CHAR16_T
typedef basic_string_view<char16_t, std::char_traits<char16_t> > u16string_view;
#endif
#ifndef BOOST_NO_CXX11_CHAR32_T
typedef basic_string_view<char32_t, std::char_traits<char32_t> > u32string_view;
#endif
}
#endif