Merge pull request #1028 from wesnoth/large-gui-canvas
Double maximum height of text labels
This commit is contained in:
commit
15fe590d0f
15 changed files with 943 additions and 197 deletions
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -71,6 +71,8 @@ public:
|
|||
|
||||
void set_text_alpha(unsigned short alpha);
|
||||
|
||||
void set_link_aware(bool l);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Possible states of the widget.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 ---------{
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
632
src/serialization/string_view.hpp
Normal file
632
src/serialization/string_view.hpp
Normal 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
|
Loading…
Add table
Reference in a new issue