Split text attribute handling into its own API (#9890)

This commit is contained in:
Charles Dang 2025-02-15 00:33:46 -05:00 committed by GitHub
parent 93509c8499
commit 99ee4a7703
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 385 additions and 241 deletions

View File

@ -370,6 +370,8 @@
<Unit filename="../../src/floating_label.hpp" />
<Unit filename="../../src/floating_textbox.cpp" />
<Unit filename="../../src/floating_textbox.hpp" />
<Unit filename="../../src/font/attributes.cpp" />
<Unit filename="../../src/font/attributes.hpp" />
<Unit filename="../../src/font/constants.cpp" />
<Unit filename="../../src/font/constants.hpp" />
<Unit filename="../../src/font/error.hpp" />

View File

@ -19,6 +19,7 @@
04C748F7835C62498D27442D /* edit_pbl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6FA542D78393E8FF067775DA /* edit_pbl.cpp */; };
04DC4E59AEDBC1A0AFDCA8CC /* units_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CAF74C3AB8D456EA3E756396 /* units_dialog.cpp */; };
0554467DB5FE99D85ABCDCA0 /* edit_pbl_translation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00574699A982AA23F12B39E0 /* edit_pbl_translation.cpp */; };
0813449DBC67700714FA3ACD /* attributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57724A59B2F7D072D91DD787 /* attributes.cpp */; };
08964907BF0C2F261FC984DC /* reachmap_options.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 231C4A6BB2F1A717F0D6E2E2 /* reachmap_options.hpp */; };
0DA840E1AD033775DD626F42 /* markup.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D7B540678519F0EBD7C19A17 /* markup.hpp */; };
1234567890ABCDEF12345678 /* file_progress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1234567890ABCDEF12345680 /* file_progress.cpp */; };
@ -35,6 +36,7 @@
365D4F89BD511BC074E639D7 /* migrate_version_selection.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B3DE4F95AF72C6F6BC37E695 /* migrate_version_selection.hpp */; };
36B146FAA79A55E9F43723B1 /* general.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84234C54BB84519421FD4136 /* general.cpp */; };
36D74F7F8D7655ACCABE562D /* edit_pbl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6FA542D78393E8FF067775DA /* edit_pbl.cpp */; };
38C444C497ECBE2DF9BB2319 /* attributes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57724A59B2F7D072D91DD787 /* attributes.cpp */; };
393E4C9DAEE19E12B2B168B5 /* edit_unit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A05D48F0A2C022FC128C8B3E /* edit_unit.cpp */; };
3C0F4FFA9A0331ED6846D216 /* spritesheet_generator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D284B9A81882806D8B25006 /* spritesheet_generator.hpp */; };
3C254DF5B7DF196F2041955F /* mp_report.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 58C649488B3014E6F7254B62 /* mp_report.cpp */; };
@ -653,6 +655,7 @@
7A0347D48BDB52B1430D9E79 /* migrate_version_selection.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B3DE4F95AF72C6F6BC37E695 /* migrate_version_selection.hpp */; };
7A7146D7893AA09891352019 /* test_schema_validator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CF14AB694764953E2CB3AF7 /* test_schema_validator.cpp */; };
7BFC4DF5BFF8CF75855BA662 /* prompt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67044415B63F5888193BD7A6 /* prompt.cpp */; };
7C4740B081A838A09779FAAA /* attributes.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B56948C5B90E74C62F2D078F /* attributes.hpp */; };
7FDF4E8D9C94E7DA8F41F7BB /* tod_new_schedule.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 5D46466DBCD81B13621C7342 /* tod_new_schedule.hpp */; };
805143B8BABF92CA79BEC8F5 /* gui_test_dialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FBD4033B4B52E9424819B5F /* gui_test_dialog.cpp */; };
80724CBB88A3B6C36D1E3199 /* spritesheet_generator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D284B9A81882806D8B25006 /* spritesheet_generator.hpp */; };
@ -1143,6 +1146,7 @@
9C6342BC8A95B6D23D384486 /* gui_test_dialog.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 0110429EAA81AED07D53B749 /* gui_test_dialog.hpp */; };
9FE64884AE8121CDBABF7D8A /* preferences.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D64406873FEB11E9758A05 /* preferences.cpp */; };
AC4242F78B39C571E34AF48F /* edit_unit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A05D48F0A2C022FC128C8B3E /* edit_unit.cpp */; };
B14E4163984EED844169EF4F /* attributes.hpp in Headers */ = {isa = PBXBuildFile; fileRef = B56948C5B90E74C62F2D078F /* attributes.hpp */; };
B45C431C9B7250C3321F8BC2 /* preferences.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2CFD4922B64EA6C9F71F71A2 /* preferences.hpp */; };
B508D193100146E300B12852 /* engine_fai.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B508D191100146E300B12852 /* engine_fai.cpp */; };
B513B2290ED36BFB0006E551 /* libcairo.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B513B2270ED36BFB0006E551 /* libcairo.2.dylib */; };
@ -2119,6 +2123,7 @@
4944F41A1354FBFF0027E614 /* teleport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = teleport.cpp; sourceTree = "<group>"; };
49478712172FF6F8002B7ABA /* tristate_button.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tristate_button.cpp; sourceTree = "<group>"; };
49478713172FF6F8002B7ABA /* tristate_button.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = tristate_button.hpp; sourceTree = "<group>"; };
57724A59B2F7D072D91DD787 /* attributes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = attributes.cpp; path = font/attributes.cpp; sourceTree = "<group>"; };
58C649488B3014E6F7254B62 /* mp_report.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = mp_report.cpp; sourceTree = "<group>"; };
5D46466DBCD81B13621C7342 /* tod_new_schedule.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tod_new_schedule.hpp; sourceTree = "<group>"; };
620A386215E9364E00A4F513 /* attack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = attack.cpp; sourceTree = "<group>"; };
@ -2687,6 +2692,7 @@
B55BE04A11234B1A00154E6C /* lobby_info.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = lobby_info.hpp; sourceTree = "<group>"; };
B561F366104B1042001369F5 /* component.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = component.cpp; sourceTree = "<group>"; };
B561F367104B1042001369F5 /* component.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = component.hpp; sourceTree = "<group>"; };
B56948C5B90E74C62F2D078F /* attributes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = attributes.hpp; path = font/attributes.hpp; sourceTree = "<group>"; };
B5951A811013BB0800C10B66 /* chat_events.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = chat_events.hpp; sourceTree = "<group>"; };
B5951A831013BB0800C10B66 /* multiplayer_error_codes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = multiplayer_error_codes.hpp; sourceTree = "<group>"; };
B5951A841013BB0800C10B66 /* resources.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resources.cpp; sourceTree = "<group>"; };
@ -5145,6 +5151,8 @@
46F92EF42174FEBD00602C1C /* standard_colors.hpp */,
46F92F0C2174FEC000602C1C /* text.cpp */,
46F92F0D2174FEC000602C1C /* text.hpp */,
57724A59B2F7D072D91DD787 /* attributes.cpp */,
B56948C5B90E74C62F2D078F /* attributes.hpp */,
);
name = font;
sourceTree = "<group>";
@ -5201,6 +5209,7 @@
F7524948ADF9BC097E6D8DBC /* charconv.hpp in Headers */,
00424091A60B5901585B212F /* units_dialog.hpp in Headers */,
48C54CF8AD9615C43EB823E7 /* addon_server_info.hpp in Headers */,
B14E4163984EED844169EF4F /* attributes.hpp in Headers */,
08964907BF0C2F261FC984DC /* reachmap_options.hpp in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -5226,6 +5235,7 @@
92644A76AAB2F29A77107DC0 /* charconv.hpp in Headers */,
96D34041A501AB7F5B7AD596 /* units_dialog.hpp in Headers */,
E60E437B8712EC8D22CA2608 /* addon_server_info.hpp in Headers */,
7C4740B081A838A09779FAAA /* attributes.hpp in Headers */,
63B0402A889C6663911DC677 /* reachmap_options.hpp in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -5963,6 +5973,7 @@
99494BD0ABBAE79FB3814E00 /* charconv.cpp in Sources */,
04DC4E59AEDBC1A0AFDCA8CC /* units_dialog.cpp in Sources */,
DA2B4478B9C7AB102479C322 /* addon_server_info.cpp in Sources */,
0813449DBC67700714FA3ACD /* attributes.cpp in Sources */,
6C4A4F7982769422C51DC3E6 /* reachmap_options.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -6637,6 +6648,7 @@
355942A786D57DD0A6A93E2A /* units_dialog.cpp in Sources */,
52074B55B6C7AC8A1AE8BEA8 /* addon_server_info.cpp in Sources */,
144E49509EAC409649899BD4 /* test_lua_ptr.cpp in Sources */,
38C444C497ECBE2DF9BB2319 /* attributes.cpp in Sources */,
E875402885AA34096C34E3B0 /* reachmap_options.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -8,6 +8,7 @@ display.cpp
display_context.cpp
events.cpp
floating_label.cpp
font/attributes.cpp
font/font_config.cpp
font/sdl_ttf_compat.cpp
font/standard_colors.cpp

174
src/font/attributes.cpp Normal file
View File

@ -0,0 +1,174 @@
/*
Copyright (C) 2025
Part of the Battle for Wesnoth Project https://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.
*/
#include "font/attributes.hpp"
#include "font/font_config.hpp"
#include "color.hpp"
#include "gui/core/log.hpp"
#include "preferences/preferences.hpp"
#include "tstring.hpp"
#include "video.hpp"
#include <memory>
namespace font
{
namespace
{
/**
* Private helper class to manage a single PangoAttribute.
*
* This object owns its attribute until relinquished to an attribute_list
* by calling @ref add_to or @ref modify_in.
*/
class attribute
{
public:
attribute(PangoAttribute* attr, unsigned offset_start, unsigned offset_end)
: value_(attr, &pango_attribute_destroy)
{
attr->start_index = offset_start;
attr->end_index = offset_end;
}
void add_to(font::attribute_list& list)
{
list.insert(value_.release());
}
void modify_in(font::attribute_list& list)
{
list.modify(value_.release());
}
private:
std::unique_ptr<PangoAttribute, void(*)(PangoAttribute*)> value_;
};
/** Pango sometimes handles colors as 16 bit integers. */
constexpr std::tuple<uint16_t, uint16_t, uint16_t> color_to_uint16(const color_t& color)
{
return {
color.r / 255.0 * std::numeric_limits<uint16_t>::max(),
color.g / 255.0 * std::numeric_limits<uint16_t>::max(),
color.b / 255.0 * std::numeric_limits<uint16_t>::max()
};
}
} // anon namespace
void add_attribute_size(attribute_list& list, unsigned offset_start, unsigned offset_end, int size)
{
// TODO: we shouldn't be doing scaling stuff here...
size = prefs::get().font_scaled(size) * video::get_pixel_scale();
attribute attr {
pango_attr_size_new_absolute(PANGO_SCALE * size),
offset_start, offset_end
};
DBG_GUI_D << "attribute: size";
DBG_GUI_D << "attribute start: " << offset_start << " end : " << offset_end;
attr.add_to(list);
}
void add_attribute_weight(attribute_list& list, unsigned offset_start, unsigned offset_end, PangoWeight weight)
{
attribute attr {
pango_attr_weight_new(weight),
offset_start, offset_end
};
DBG_GUI_D << "attribute: weight";
DBG_GUI_D << "attribute start: " << offset_start << " end : " << offset_end;
attr.add_to(list);
}
void add_attribute_style(attribute_list& list, unsigned offset_start, unsigned offset_end, PangoStyle style)
{
attribute attr {
pango_attr_style_new(style),
offset_start, offset_end
};
DBG_GUI_D << "attribute: style";
DBG_GUI_D << "attribute start: " << offset_start << " end : " << offset_end;
attr.add_to(list);
}
void add_attribute_underline(attribute_list& list, unsigned offset_start, unsigned offset_end, PangoUnderline underline)
{
attribute attr {
pango_attr_underline_new(underline),
offset_start, offset_end
};
DBG_GUI_D << "attribute: underline";
DBG_GUI_D << "attribute start: " << offset_start << " end : " << offset_end;
attr.add_to(list);
}
void add_attribute_fg_color(attribute_list& list, unsigned offset_start, unsigned offset_end, const color_t& color)
{
auto [col_r, col_g, col_b] = color_to_uint16(color);
attribute attr {
pango_attr_foreground_new(col_r, col_g, col_b),
offset_start, offset_end
};
DBG_GUI_D << "attribute: fg color";
DBG_GUI_D << "attribute start: " << offset_start << " end : " << offset_end;
DBG_GUI_D << "color: " << col_r << "," << col_g << "," << col_b;
attr.add_to(list);
}
void add_attribute_bg_color(attribute_list& list, unsigned offset_start, unsigned offset_end, const color_t& color)
{
auto [col_r, col_g, col_b] = color_to_uint16(color);
attribute attr {
pango_attr_background_new(col_r, col_g, col_b),
offset_start, offset_end
};
DBG_GUI_D << "highlight start: " << offset_start << "end : " << offset_end;
DBG_GUI_D << "highlight color: " << col_r << "," << col_g << "," << col_b;
attr.modify_in(list);
}
void add_attribute_font_family(attribute_list& list, unsigned offset_start, unsigned offset_end, font::family_class family)
{
const t_string& family_name = get_font_families(family);
attribute attr {
pango_attr_family_new(family_name.c_str()),
offset_start, offset_end
};
DBG_GUI_D << "attribute: font family";
DBG_GUI_D << "attribute start: " << offset_start << " end : " << offset_end;
DBG_GUI_D << "font family: " << family;
attr.add_to(list);
}
} // namespace font

148
src/font/attributes.hpp Normal file
View File

@ -0,0 +1,148 @@
/*
Copyright (C) 2025
Part of the Battle for Wesnoth Project https://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-layout.h>
#include "font/font_options.hpp"
struct color_t;
namespace font
{
/** Helper class to encapsulate the management of a PangoAttrList. */
class attribute_list
{
public:
attribute_list()
: attributes_(pango_attr_list_new())
{
}
~attribute_list()
{
pango_attr_list_unref(attributes_);
}
attribute_list(const attribute_list&) = delete;
attribute_list& operator=(const attribute_list&) = delete;
void insert(PangoAttribute* attr)
{
pango_attr_list_insert(attributes_, attr);
}
void modify(PangoAttribute* attr)
{
pango_attr_list_change(attributes_, attr);
}
void apply_to(PangoLayout* layout) const
{
pango_layout_set_attributes(layout, attributes_);
}
void splice_into(PangoAttrList* target) const
{
pango_attr_list_splice(target, attributes_, 0, 0);
}
private:
PangoAttrList* attributes_;
};
//
// The following free functions are thin wrappers around the corresponding
// pango_attr_new methods. For more details, refer to the Pango docs.
//
/**
* Add Pango font weight attribute to a specific portion of text. This changes the font weight
* of the corresponding part of the text.
*
* @param list The attribute list to which to append this attribute.
* @param offset_start Byte index of the cursor where font weight change starts
* @param offset_end Byte index of the cursor where font weight change ends
* @param weight Pango font weight
*/
void add_attribute_weight(attribute_list& list, unsigned offset_start, unsigned offset_end, PangoWeight weight);
/**
* Add Pango font style attribute to a specific portion of text, used to set italic/oblique text
*
* @param list The attribute list to which to append this attribute.
* @param offset_start Byte index of the cursor where font style change starts
* @param offset_end Byte index of the cursor where font style change ends
* @param style Pango font style (normal/italic/oblique)
*/
void add_attribute_style(attribute_list& list, unsigned offset_start, unsigned offset_end, PangoStyle style);
/**
* Add Pango underline attribute to a specific portion of text. This adds an underline to the
* corresponding part of the text.
*
* @param list The attribute list to which to append this attribute.
* @param offset_start Byte index of the cursor where underline starts
* @param offset_end Byte index of the cursor where underline change ends
* @param underline Pango underline style
*/
void add_attribute_underline(attribute_list& list, unsigned offset_start, unsigned offset_end, PangoUnderline underline);
/**
* Add Pango fg color attribute to a specific portion of text. This changes the foreground
* color of the corresponding part of the text.
*
* @param list The attribute list to which to append this attribute.
* @param offset_start Byte index of the cursor where color change starts
* @param offset_end Byte index of the cursor where color change ends
* @param color Foreground color
*/
void add_attribute_fg_color(attribute_list& list, unsigned offset_start, unsigned offset_end, const color_t& color);
/**
* Mark a specific portion of text for highlighting. Used for selection box.
* BGColor is set in set_text(), this just marks the area to be colored.
* Markup not used because the user may enter their own markup or special characters
*
* @param list The attribute list to which to append this attribute.
* @param offset_start Byte index of the cursor where selection/highlight starts
* @param offset_end Byte index of the cursor where selection/highlight ends
* @param color Highlight/Background color
*/
void add_attribute_bg_color(attribute_list& list, unsigned offset_start, unsigned offset_end, const color_t& color);
/**
* Add Pango font size attribute to a specific portion of text. This changes the font size
* of the corresponding part of the text.
*
* @param list The attribute list to which to append this attribute.
* @param offset_start Byte index of the cursor where size change starts
* @param offset_end Byte index of the cursor where size change ends
* @param size Font size
*/
void add_attribute_size(attribute_list& list, unsigned offset_start, unsigned offset_end, int size);
/**
* Add Pango font family attribute to a specific portion of text. This changes
* the font family of the corresponding part of the text.
*
* @param list The attribute list to which to append this attribute.
* @param offset_start Byte index of the cursor where size change starts
* @param offset_end Byte index of the cursor where size change ends
* @param family The font family
*/
void add_attribute_font_family(attribute_list& list, unsigned offset_start, unsigned offset_end, font::family_class family);
} // namespace font

View File

@ -17,6 +17,7 @@
#include "font/text.hpp"
#include "font/attributes.hpp"
#include "font/font_config.hpp"
#include "font/pango/escape.hpp"
@ -32,7 +33,6 @@
#include "preferences/preferences.hpp"
#include "video.hpp"
#include <cassert>
#include <cstring>
#include <stdexcept>
@ -66,9 +66,6 @@ pango_text::pango_text()
, pixel_scale_(1)
, surface_buffer_()
{
// Initialize global list
global_attribute_list_ = pango_attr_list_new();
// With 72 dpi the sizes are the same as with SDL_TTF so hardcoded.
pango_cairo_context_set_resolution(context_.get(), 72.0);
@ -305,138 +302,20 @@ int pango_text::xy_to_index(const point& position) const
return index;
}
void pango_text::add_attribute_size(const unsigned start_offset, const unsigned end_offset, int size)
void pango_text::clear_attributes()
{
size = prefs::get().font_scaled(size) * pixel_scale_;
pango_layout_set_attributes(layout_.get(), nullptr);
}
if (start_offset != end_offset) {
PangoAttribute *attr = pango_attr_size_new_absolute(PANGO_SCALE * size);
attr->start_index = start_offset;
attr->end_index = end_offset;
DBG_GUI_D << "attribute: size";
DBG_GUI_D << "attribute start: " << start_offset << " end : " << end_offset;
// Insert all attributes
pango_attr_list_insert(global_attribute_list_, attr);
void pango_text::apply_attributes(const font::attribute_list& attrs)
{
if(PangoAttrList* current_attrs = pango_layout_get_attributes(layout_.get())) {
attrs.splice_into(current_attrs);
} else {
attrs.apply_to(layout_.get());
}
}
void pango_text::add_attribute_weight(const unsigned start_offset, const unsigned end_offset, PangoWeight weight)
{
if (start_offset != end_offset) {
PangoAttribute *attr = pango_attr_weight_new(weight);
attr->start_index = start_offset;
attr->end_index = end_offset;
DBG_GUI_D << "attribute: weight";
DBG_GUI_D << "attribute start: " << start_offset << " end : " << end_offset;
// Insert all attributes
pango_attr_list_insert(global_attribute_list_, attr);
}
}
void pango_text::add_attribute_style(const unsigned start_offset, const unsigned end_offset, PangoStyle style)
{
if (start_offset != end_offset) {
PangoAttribute *attr = pango_attr_style_new(style);
attr->start_index = start_offset;
attr->end_index = end_offset;
DBG_GUI_D << "attribute: style";
DBG_GUI_D << "attribute start: " << start_offset << " end : " << end_offset;
// Insert all attributes
pango_attr_list_insert(global_attribute_list_, attr);
}
}
void pango_text::add_attribute_underline(const unsigned start_offset, const unsigned end_offset, PangoUnderline underline)
{
if (start_offset != end_offset) {
PangoAttribute *attr = pango_attr_underline_new(underline);
attr->start_index = start_offset;
attr->end_index = end_offset;
DBG_GUI_D << "attribute: underline";
DBG_GUI_D << "attribute start: " << start_offset << " end : " << end_offset;
// Insert all attributes
pango_attr_list_insert(global_attribute_list_, attr);
}
}
namespace
{
std::tuple<uint16_t, uint16_t, uint16_t> color_to_uint16(const color_t& color)
{
return {
color.r / 255.0 * std::numeric_limits<uint16_t>::max(),
color.g / 255.0 * std::numeric_limits<uint16_t>::max(),
color.b / 255.0 * std::numeric_limits<uint16_t>::max()
};
}
} // end anon namespace
void pango_text::add_attribute_fg_color(const unsigned start_offset, const unsigned end_offset, const color_t& color)
{
if (start_offset != end_offset) {
auto [col_r, col_g, col_b] = color_to_uint16(color);
PangoAttribute *attr = pango_attr_foreground_new(col_r, col_g, col_b);
attr->start_index = start_offset;
attr->end_index = end_offset;
DBG_GUI_D << "attribute: fg color";
DBG_GUI_D << "attribute start: " << start_offset << " end : " << end_offset;
DBG_GUI_D << "color: " << col_r << "," << col_g << "," << col_b;
// Insert all attributes
pango_attr_list_insert(global_attribute_list_, attr);
}
}
void pango_text::add_attribute_font_family(const unsigned start_offset, const unsigned end_offset, font::family_class family)
{
if (start_offset != end_offset) {
const t_string& family_name = get_font_families(family);
PangoAttribute *attr = pango_attr_family_new(family_name.c_str());
attr->start_index = start_offset;
attr->end_index = end_offset;
DBG_GUI_D << "attribute: font family";
DBG_GUI_D << "attribute start: " << start_offset << " end : " << end_offset;
DBG_GUI_D << "font family: " << family;
// Insert all attributes
pango_attr_list_insert(global_attribute_list_, attr);
}
}
void pango_text::add_attribute_bg_color(const unsigned start_offset, const unsigned end_offset, const color_t& color)
{
// Highlight
int col_r = color.r / 255.0 * 65535.0;
int col_g = color.g / 255.0 * 65535.0;
int col_b = color.b / 255.0 * 65535.0;
DBG_GUI_D << "highlight start: " << start_offset << "end : " << end_offset;
DBG_GUI_D << "highlight color: " << col_r << "," << col_g << "," << col_b;
PangoAttribute *attr = pango_attr_background_new(col_r, col_g, col_b);
attr->start_index = start_offset;
attr->end_index = end_offset;
// Insert all attributes
pango_attr_list_change(global_attribute_list_, attr);
}
void pango_text::clear_attribute_list() {
global_attribute_list_ = pango_attr_list_new();
pango_layout_set_attributes(layout_.get(), global_attribute_list_);
}
bool pango_text::set_text(const std::string& text, const bool markedup)
{
if(markedup != markedup_text_ || text != text_) {
@ -461,11 +340,6 @@ bool pango_text::set_text(const std::string& text, const bool markedup)
pango_layout_set_text(layout_.get(), narrow.c_str(), narrow.size());
}
pango_layout_set_attributes(layout_.get(), global_attribute_list_);
// Clear list. Using pango_attr_list_unref() causes segfault
global_attribute_list_ = pango_attr_list_new();
text_ = narrow;
length_ = wide.size();
markedup_text_ = markedup;
@ -951,14 +825,6 @@ bool pango_text::set_markup(std::string_view text, PangoLayout& layout)
} else {
pango_layout_set_markup(&layout, text.data(), text.size());
}
// append any manual attributes to those generated by pango_layout_set_markup
PangoAttrList* markup_list = pango_layout_get_attributes(&layout);
for (auto* l = pango_attr_list_get_attributes(global_attribute_list_); l != nullptr; l = l->next) {
PangoAttribute* attr = static_cast<PangoAttribute*>(l->data);
pango_attr_list_change(markup_list, attr);
}
global_attribute_list_ = markup_list;
}
return valid;

View File

@ -35,7 +35,9 @@
struct point;
namespace font {
namespace font
{
class attribute_list;
// add background color and also font markup.
@ -315,74 +317,8 @@ public:
pango_text& set_add_outline(bool do_add);
// The following add attribute methods are thin wrappers around the corresponding pango
// add attribute methods. For more details, refer to the Pango docs.
/**
* Add pango font weight attribute to a specific portion of text. This changes the font weight
* of the corresponding part of the text.
* @param start_offset Byte index of the cursor where font weight change starts
* @param end_offset Byte index of the cursor where font weight change ends
* @param weight Pango font weight
*/
void add_attribute_weight(const unsigned start_offset, const unsigned end_offset, PangoWeight weight);
/**
* Add pango font style attribute to a specific portion of text, used to set italic/oblique text
* @param start_offset Byte index of the cursor where font style change starts
* @param end_offset Byte index of the cursor where font style change ends
* @param style Pango font style (normal/italic/oblique)
*/
void add_attribute_style(const unsigned start_offset, const unsigned end_offset, PangoStyle style);
/**
* Add pango underline attribute to a specific portion of text. This adds an underline to the
* corresponding part of the text.
* @param start_offset Byte index of the cursor where underline starts
* @param end_offset Byte index of the cursor where underline change ends
* @param underline Pango underline style
*/
void add_attribute_underline(const unsigned start_offset, const unsigned end_offset, PangoUnderline underline);
/**
* Add pango fg color attribute to a specific portion of text. This changes the foreground
* color of the corresponding part of the text.
* @param start_offset Byte index of the cursor where color change starts
* @param end_offset Byte index of the cursor where color change ends
* @param color Foreground color
*/
void add_attribute_fg_color(const unsigned start_offset, const unsigned end_offset, const color_t& color);
/**
* Mark a specific portion of text for highlighting. Used for selection box.
* BGColor is set in set_text(), this just marks the area to be colored.
* Markup not used because the user may enter their own markup or special characters
* @param start_offset Byte index of the cursor where selection/highlight starts
* @param end_offset Byte index of the cursor where selection/highlight ends
* @param color Highlight/Background color
*/
void add_attribute_bg_color(const unsigned start_offset, const unsigned end_offset, const color_t& color);
/**
* Add pango font size attribute to a specific portion of text. This changes the font size
* of the corresponding part of the text.
* @param start_offset Byte index of the cursor where size change starts
* @param end_offset Byte index of the cursor where size change ends
* @param size Font size
*/
void add_attribute_size(const unsigned start_offset, const unsigned end_offset, int size);
/**
* Add pango font family attribute to a specific portion of text. This changes
* the font family of the corresponding part of the text.
* @param start_offset Byte index of the cursor where size change starts
* @param end_offset Byte index of the cursor where size change ends
* @param family The font family
*/
void add_attribute_font_family(const unsigned start_offset, const unsigned end_offset, font::family_class family);
/** Clears all attributes from the global attribute list */
void clear_attribute_list();
void clear_attributes();
void apply_attributes(const font::attribute_list& attrs);
private:
@ -481,12 +417,6 @@ private:
/** Length of the text. */
mutable std::size_t length_;
/**
* Global pango attribute list. All attributes in this list
* will be applied one by one to the text
*/
PangoAttrList* global_attribute_list_;
/** The pixel scale, used to render high-DPI text. */
int pixel_scale_;

View File

@ -25,6 +25,7 @@
#include "draw.hpp"
#include "draw_manager.hpp"
#include "font/attributes.hpp"
#include "font/text.hpp"
#include "formatter.hpp"
#include "gettext.hpp"
@ -438,8 +439,7 @@ void text_shape::draw(wfl::map_formula_callable& variables)
return;
}
font::pango_text& text_renderer = font::get_text_renderer();
text_renderer.clear_attribute_list();
font::attribute_list text_attributes;
//
// Highlight
@ -450,7 +450,7 @@ void text_shape::draw(wfl::map_formula_callable& variables)
for(size_t i = 0; i < std::min(starts.size(), stops.size()); i++) {
typed_formula<int> hstart(starts.at(i));
typed_formula<int> hstop(stops.at(i));
text_renderer.add_attribute_bg_color(hstart(variables), hstop(variables), highlight_color_(variables));
add_attribute_bg_color(text_attributes, hstart(variables), hstop(variables), highlight_color_(variables));
}
//
@ -467,30 +467,33 @@ void text_shape::draw(wfl::map_formula_callable& variables)
const unsigned end = attr["end"].to_int(text.size());
if (name == "color" || name == "fgcolor" || name == "foreground") {
text_renderer.add_attribute_fg_color(start, end, attr["value"].empty() ? font::NORMAL_COLOR : font::string_to_color(attr["value"]));
} else if (name == "bgcolor"||name == "background") {
text_renderer.add_attribute_bg_color(start, end, attr["value"].empty() ? font::GOOD_COLOR : font::string_to_color(attr["value"]));
} else if (name == "font_size"||name == "size") {
text_renderer.add_attribute_size(start, end, attr["value"].to_int(font::SIZE_NORMAL));
} else if (name == "font_family"||name == "face") {
text_renderer.add_attribute_font_family(start, end, font::str_to_family_class(attr["value"]));
add_attribute_fg_color(text_attributes, start, end, attr["value"].empty() ? font::NORMAL_COLOR : font::string_to_color(attr["value"]));
} else if (name == "bgcolor" || name == "background") {
add_attribute_bg_color(text_attributes, start, end, attr["value"].empty() ? font::GOOD_COLOR : font::string_to_color(attr["value"]));
} else if (name == "font_size" || name == "size") {
add_attribute_size(text_attributes, start, end, attr["value"].to_int(font::SIZE_NORMAL));
} else if (name == "font_family" || name == "face") {
add_attribute_font_family(text_attributes, start, end, font::str_to_family_class(attr["value"]));
} else if (name == "weight") {
text_renderer.add_attribute_weight(start, end, decode_text_weight(attr["value"]));
add_attribute_weight(text_attributes, start, end, decode_text_weight(attr["value"]));
} else if (name == "style") {
text_renderer.add_attribute_style(start, end, decode_text_style(attr["value"]));
add_attribute_style(text_attributes, start, end, decode_text_style(attr["value"]));
} else if (name == "bold" || name == "b") {
text_renderer.add_attribute_weight(start, end, PANGO_WEIGHT_BOLD);
add_attribute_weight(text_attributes, start, end, PANGO_WEIGHT_BOLD);
} else if (name == "italic" || name == "i") {
text_renderer.add_attribute_style(start, end, PANGO_STYLE_ITALIC);
add_attribute_style(text_attributes, start, end, PANGO_STYLE_ITALIC);
} else if (name == "underline" || name == "u") {
text_renderer.add_attribute_underline(start, end, PANGO_UNDERLINE_SINGLE);
add_attribute_underline(text_attributes, start, end, PANGO_UNDERLINE_SINGLE);
} else {
// Unsupported formatting or normal text
text_renderer.add_attribute_weight(start, end, PANGO_WEIGHT_NORMAL);
text_renderer.add_attribute_style(start, end, PANGO_STYLE_NORMAL);
add_attribute_weight(text_attributes, start, end, PANGO_WEIGHT_NORMAL);
add_attribute_style(text_attributes, start, end, PANGO_STYLE_NORMAL);
}
}
font::pango_text& text_renderer = font::get_text_renderer();
text_renderer.clear_attributes();
text_renderer
.set_link_aware(link_aware_(variables))
.set_link_color(link_color_(variables))
@ -509,6 +512,9 @@ void text_shape::draw(wfl::map_formula_callable& variables)
.set_characters_per_line(characters_per_line_)
.set_add_outline(outline_(variables));
// Do this last so it can merge with attributes from markup
text_renderer.apply_attributes(text_attributes);
wfl::map_formula_callable local_variables(variables);
const auto [tw, th] = text_renderer.get_size();

View File

@ -19,6 +19,7 @@
#include "cursor.hpp"
#include "desktop/clipboard.hpp"
#include "font/attributes.hpp"
#include "gui/core/gui_definition.hpp"
#include "gui/core/log.hpp"
#include "gui/core/timer.hpp"
@ -122,6 +123,13 @@ void text_box_base::set_maximum_length(const std::size_t maximum_length)
}
}
void text_box_base::set_highlight_area(const unsigned start_offset, const unsigned end_offset, const color_t& color)
{
font::attribute_list attrs;
add_attribute_bg_color(attrs, start_offset, end_offset, color);
text_.apply_attributes(attrs);
}
void text_box_base::set_value(const std::string& text)
{
if(text != text_.text()) {

View File

@ -121,12 +121,9 @@ public:
/**
* Wrapper function, sets the area between column start and end
* offset to be highlighted in a specific color.
* See @ref font::pango_text::add_attribute_bg_color.
* See @ref font::add_attribute_bg_color.
*/
void set_highlight_area(const unsigned start_offset, const unsigned end_offset, const color_t& color)
{
text_.add_attribute_bg_color(start_offset, end_offset, color);
}
void set_highlight_area(const unsigned start_offset, const unsigned end_offset, const color_t& color);
/***** ***** ***** setters / getters for members ***** ****** *****/