diff options
Diffstat (limited to '')
-rw-r--r-- | src/display/control/canvas-item-text.cpp | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/src/display/control/canvas-item-text.cpp b/src/display/control/canvas-item-text.cpp new file mode 100644 index 0000000..5049543 --- /dev/null +++ b/src/display/control/canvas-item-text.cpp @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * A class to represent a control textrilateral. Used to highlight selected text. + */ + +/* + * Author: + * Tavmjong Bah + * + * Copyright (C) 2020 Tavmjong Bah + * + * Rewrite of SPCtrlTextr + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "canvas-item-text.h" + +#include <cmath> +#include <utility> // std::move +#include <glibmm/i18n.h> + +#include "color.h" // SP_RGBA_x_F + +#include "ui/util.h" + +namespace Inkscape { + +/** + * Create a null control text. + */ +CanvasItemText::CanvasItemText(CanvasItemGroup *group) + : CanvasItem(group) +{ + _name = "CanvasItemText"; + _fill = 0x33337fff; // Override CanvasItem default. +} + +/** + * Create a control text. Point are in document coordinates. + */ +CanvasItemText::CanvasItemText(CanvasItemGroup *group, Geom::Point const &p, Glib::ustring text, bool scaled) + : CanvasItem(group) + , _p(p) + , _text(std::move(text)) + , _scaled(scaled) +{ + _name = "CanvasItemText"; + _fill = 0x33337fff; // Override CanvasItem default. + + request_update(); +} + +/** + * Set a text position. Position is in document coordinates. + */ +void CanvasItemText::set_coord(Geom::Point const &p) +{ + defer([=] { + if (_p == p) return; + _p = p; + request_update(); + }); +} + +/** + * Set a text position. Position is in document coordinates. + */ +void CanvasItemText::set_bg_radius(double rad) +{ + defer([=] { + if (_bg_rad == rad) return; + _bg_rad = rad; + request_update(); + }); +} + +/** + * Returns true if point p (in canvas units) is within tolerance (canvas units) distance of text. + */ +bool CanvasItemText::contains(Geom::Point const &p, double tolerance) +{ + return false; // We never select text. +} + +/** + * Update and redraw control text. + */ +void CanvasItemText::_update(bool) +{ + // Queue redraw of old area (erase previous content). + request_redraw(); + + // Point needs to be scaled manually if not cairo scaling + Geom::Point p = _scaled ? _p : _p * affine(); + + // Measure text size + _text_box = load_text_extents(); + + // Offset relative to requested point + double offset_x = -(_anchor_position.x() * _text_box.width()); + double offset_y = -(_anchor_position.y() * _text_box.height()); + offset_x += p.x() + _adjust_offset.x(); + offset_y += p.y() + _adjust_offset.y(); + _text_box *= Geom::Translate(Geom::Point(offset_x, offset_y).floor()); + + // Pixel alignment of background. Avoid aliasing artifacts on redraw. + _text_box = _text_box.roundOutwards(); + + // Don't apply affine here, to keep text at the same size in screen coords. + _bounds = _text_box; + if (_scaled && _bounds) { + *_bounds *= affine(); + _bounds = _bounds->roundOutwards(); + } + + // Queue redraw of new area + request_redraw(); +} + +/** + * Render text to screen via Cairo. + */ +void CanvasItemText::_render(Inkscape::CanvasItemBuffer &buf) const +{ + buf.cr->save(); + + // Screen to desktop coords. + buf.cr->translate(-buf.rect.left(), -buf.rect.top()); + + if (_scaled) { + // Convert from canvas space to document space + buf.cr->transform(geom_to_cairo(affine())); + } + + double x = _text_box.min().x(); + double y = _text_box.min().y(); + double w = _text_box.width(); + double h = _text_box.height(); + + // Background + if (_use_background) { + if (_bg_rad == 0.0) { + buf.cr->rectangle(x, y, w, h); + } else { + double radius = _bg_rad * (std::min(w ,h) / 2); + buf.cr->arc(x + w - radius, y + radius, radius, -M_PI_2, 0); + buf.cr->arc(x + w - radius, y + h - radius, radius, 0, M_PI_2); + buf.cr->arc(x + radius, y + h - radius, radius, M_PI_2, M_PI); + buf.cr->arc(x + radius, y + radius, radius, M_PI, 3*M_PI_2); + } + buf.cr->set_line_width(2); + buf.cr->set_source_rgba(SP_RGBA32_R_F(_background), SP_RGBA32_G_F(_background), + SP_RGBA32_B_F(_background), SP_RGBA32_A_F(_background)); + buf.cr->fill(); + } + + // Center the text inside the draw background box + auto bx = x + w / 2.0; + auto by = y + h / 2.0 + 1; + buf.cr->move_to(int(bx - _text_size.x_bearing - _text_size.width/2.0), + int(by - _text_size.y_bearing - _text_extent.height/2.0)); + + buf.cr->select_font_face(_fontname, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL); + buf.cr->set_font_size(_fontsize); + buf.cr->text_path(_text); + buf.cr->set_source_rgba(SP_RGBA32_R_F(_fill), SP_RGBA32_G_F(_fill), + SP_RGBA32_B_F(_fill), SP_RGBA32_A_F(_fill)); + buf.cr->fill(); + buf.cr->restore(); +} + +void CanvasItemText::set_text(Glib::ustring text) +{ + defer([=, text = std::move(text)] () mutable { + if (_text == text) return; + _text = std::move(text); + request_update(); // Might be larger than before! + }); +} + +void CanvasItemText::set_fontsize(double fontsize) +{ + defer([=] { + if (_fontsize == fontsize) return; + _fontsize = fontsize; + request_update(); // Might be larger than before! + }); +} + +/** + * Load the sizes of the text extent using the given font. + */ +Geom::Rect CanvasItemText::load_text_extents() +{ + auto surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, 1, 1); + auto context = Cairo::Context::create(surface); + context->select_font_face(_fontname, Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL); + context->set_font_size(_fontsize); + context->get_text_extents(_text, _text_size); + + if (_fixed_line) { + // TRANSLATORS: This is a set of letters to test for font ascender and descenders. + context->get_text_extents(_("lg1p$"), _text_extent); + } else { + _text_extent = _text_size; + } + + return Geom::Rect::from_xywh(0, 0, + _text_size.x_advance + _border * 2, + _text_extent.height + _border * 2); +} + +void CanvasItemText::set_background(uint32_t background) +{ + defer([=] { + if (_background != background) { + _background = background; + request_redraw(); + } + _use_background = true; + }); +} + +/** + * Set the anchor point, x and y between 0.0 and 1.0. + */ +void CanvasItemText::set_anchor(Geom::Point const &anchor_pt) +{ + defer([=] { + if (_anchor_position == anchor_pt) return; + _anchor_position = anchor_pt; + request_update(); + }); +} + +void CanvasItemText::set_adjust(Geom::Point const &adjust_pt) +{ + defer([=] { + if (_adjust_offset == adjust_pt) return; + _adjust_offset = adjust_pt; + request_update(); + }); +} + +void CanvasItemText::set_fixed_line(bool fixed_line) +{ + defer([=] { + if (_fixed_line == fixed_line) return; + _fixed_line = fixed_line; + request_update(); + }); +} + +void CanvasItemText::set_border(double border) +{ + defer([=] { + if (_border == border) return; + _border = border; + request_update(); + }); +} + +} // namespace Inkscape + +/* FROM: http://lists.cairographics.org/archives/cairo-bugs/2009-March/003014.html + - Glyph surfaces: In most font rendering systems, glyph surfaces + have an origin at (0,0) and a bounding box that is typically + represented as (x_bearing,y_bearing,width,height). Depending on + which way y progresses in the system, y_bearing may typically be + negative (for systems similar to cairo, with origin at top left), + or be positive (in systems like PDF with origin at bottom left). + No matter which is the case, it is important to note that + (x_bearing,y_bearing) is the coordinates of top-left of the glyph + relative to the glyph origin. That is, for example: + + Scaled-glyph space: + + (x_bearing,y_bearing) <-- negative numbers + +----------------+ + | . | + | . | + |......(0,0) <---|-- glyph origin + | | + | | + +----------------+ + (width+x_bearing,height+y_bearing) + + Note the similarity of the origin to the device space. That is + exactly how we use the device_offset to represent scaled glyphs: + to use the device-space origin as the glyph origin. +*/ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |