Use std::unique_ptr to manage the layout and context in pango_text

This makes it easier for one canvas to manage multiple layouts.
This commit is contained in:
Jyrki Vesterinen 2017-05-04 22:16:56 +03:00
parent 28e27238e0
commit 9c1b9af525
5 changed files with 40 additions and 102 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

@ -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"
@ -40,12 +39,12 @@ 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)
, rect_()
, surface_()
, text_()
@ -68,38 +67,27 @@ 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
{
this->rerender();
@ -128,7 +116,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 +157,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 +186,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 +201,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 +248,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
@ -309,8 +298,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 +410,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 +424,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 +436,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;
}
@ -499,23 +488,23 @@ void pango_text::recalculate(const bool force) const
surface_dirty_ = true;
p_font font{get_font_families(font_class_), font_size_, font_style_};
pango_layout_set_font_description(layout_, font.get());
pango_layout_set_font_description(layout_.get(), 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));
pango_layout_set_attributes (layout_, attribute_list);
pango_layout_set_attributes(layout_.get(), attribute_list);
pango_attr_list_unref(attribute_list);
}
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());
pango_cairo_font_map_get_default(),
context_.get(),
font.get());
PangoFontMetrics* m = pango_font_get_metrics(f, nullptr);
@ -541,10 +530,10 @@ void pango_text::recalculate(const bool force) const
*/
int hack = 4;
do {
pango_layout_set_width(layout_, maximum_width == -1
pango_layout_set_width(layout_.get(), maximum_width == -1
? -1
: (maximum_width + hack) * PANGO_SCALE);
pango_layout_get_pixel_extents(layout_, nullptr, &rect_);
pango_layout_get_pixel_extents(layout_.get(), nullptr, &rect_);
DBG_GUI_L << "pango_text::" << __func__
<< " text '" << gui2::debug_truncate(text_)
@ -671,7 +660,7 @@ void pango_text::rerender(const bool force) const
foreground_color_.a / 256.0
);
pango_cairo_show_layout(cr, layout_);
pango_cairo_show_layout(cr, layout_.get());
static_assert(sizeof(Uint32) == 4, "Something is wrong with our typedefs");
@ -745,7 +734,7 @@ bool pango_text::set_markup_helper(const std::string& text)
, 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_.get(), text.c_str(), text.size());
return true;
}
@ -782,7 +771,7 @@ 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_.get(), semi_escaped.c_str(), semi_escaped.size());
return true;
}

View file

@ -22,6 +22,8 @@
#include <pango/pango.h>
#include <pango/pangocairo.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
@ -81,8 +83,6 @@ public:
pango_text(const pango_text &) = delete;
pango_text & operator = (const pango_text &) = delete;
~pango_text();
/**
* Returns the rendered text.
*
@ -246,8 +246,8 @@ 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_;
/** The SDL surface to render upon used as a cache. */