gtkmm :使用 cairo 绘制文本

gtkmm : Drawing text with cairo

我想在使用 Gtkmm 的应用程序中使用 Cairo 绘制一个简单的文本。我想直接给出字体样式(可以是 Pango::FontDescriptionPango::Context 等等......)当 Gtk::FontButton 被点击时(换句话说当signal_font_set 发出信号)。在下面的示例中,我有一个 Gtk::HeaderBar,其中包含一个 Gtk::FontButton,在单击时将 Glib::RefPtr<<Pango::Context>> 发送到绘图 class。我想要这样的东西:

MyWindow.cpp:

#include <iostream>
#include "MyWindow.h"

MyWindow::MyWindow() {
    
    set_default_size(1000, 1000);
    set_position(Gtk::WIN_POS_CENTER);

    header.set_show_close_button(true);
    header.pack_start(fontButton);;

    set_titlebar(header);

    fontButton.signal_font_set().connect([&] {
        drawingArea.select_font(fontButton.get_pango_context());
    });

    scrolledWindow.add(drawingArea);
    add(scrolledWindow);
    show_all();
}

MyDrawing.h:

#ifndef DRAWING_H
#define DRAWING_H

#include <gtkmm.h>
#include <cairo/cairo.h>

class MyDrawing : public Gtk::Layout
{

public:

     MyDrawing();
    ~MyDrawing() override;

    void select_font(Glib::RefPtr<Pango::Context> p_pangoContext);

private:
    bool draw_text(const Cairo::RefPtr<::Cairo::Context>& p_context);

    Glib::RefPtr<Pango::Context> m_pangoContext;
};

#endif // DRAWING_H

MyDrawing.cpp:

#include <iostream>
#include <utility>
#include "MyDrawing.h"
#include <cairomm/context.h>
#include <cairomm/surface.h>


MyDrawing::MyDrawing() {

    this->signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_text));
}

MyDrawing::~MyDrawing() = default;

bool MyDrawing::draw_text(const Cairo::RefPtr<::Cairo::Context> &p_context) {

    auto layout = create_pango_layout("hello ");
    if(m_pangoContext) {
        layout->set_font_description(m_pangoContext->get_font_description());
    }
    else {
        Pango::FontDescription fontDescription;
        layout->set_font_description(fontDescription);
    }
    p_context->save();
    p_context->set_font_size(30);
    p_context->set_source_rgb(0.1, 0.1, 0.1);
    p_context->move_to(40, 40);
    layout->show_in_cairo_context(p_context);
    p_context->restore();
    return true;
}

void MyDrawing::select_font(Glib::RefPtr<Pango::Context> p_pangoContext) {
    this->m_pangoContext = std::move(p_pangoContext);
    queue_draw();
}

当我手动设置 Pango::FontDescription 时,如 :

Pango::FontDescription fontDescription;
fontDescription.set_weight(Pango::WEIGHT_BOLD);
fontDescription.set_style(Pango::STYLE_ITALIC);
layout->set_font_description(fontDescription);

有效:

这是一种可行的方法:您可以从 Gtk::FontButton::get_font_name 获取字体名称,然后从中创建 Pango::FontDescription,而不是使用 Pango::Context。这是一个最小的例子来说明这一点:

#include <iostream>
#include <gtkmm.h>

class MyDrawing : public Gtk::Layout
{

public:

    MyDrawing();

    void set_font(const std::string& p_selectedFont);

private:

    bool draw_text(const Cairo::RefPtr<::Cairo::Context>& p_context);

    std::string                 m_selectedFont;
    Glib::RefPtr<Pango::Layout> m_pangoLayout;
    
};

MyDrawing::MyDrawing()
{
    signal_draw().connect(sigc::mem_fun(*this, &MyDrawing::draw_text));

    m_pangoLayout = Pango::Layout::create(get_pango_context());
    m_pangoLayout->set_text(
        "This text's appearance should change\n"
        "when font description changes."
    );
}

void MyDrawing::set_font(const std::string& p_selectedFont)
{
    // Record the font selected by the user (as a string... Ugh!):
    m_selectedFont = p_selectedFont;
    std::cout << "Selected font: " << m_selectedFont << std::endl;

    // Request a redraw:
    queue_draw();
}

bool MyDrawing::draw_text(const Cairo::RefPtr<::Cairo::Context>& p_context)
{
    // Create a font description from what was selected by the user earlier,
    // and set it:
    const Pango::FontDescription description{m_selectedFont};
    m_pangoLayout->set_font_description(description);

    p_context->save();
    p_context->set_font_size(30);
    p_context->set_source_rgb(0.1, 0.1, 0.1);
    p_context->move_to(40, 40);
    m_pangoLayout->show_in_cairo_context(p_context);
    p_context->restore();

    return true;
}

class MyWindow : public Gtk::ApplicationWindow
{

public:

    MyWindow();

private:

    Gtk::HeaderBar  m_headerBar;
    Gtk::FontButton m_fontButton;
    MyDrawing       m_drawingArea;

};

MyWindow::MyWindow()
{
    m_headerBar.set_show_close_button(true);
    m_headerBar.pack_start(m_fontButton);;

    set_titlebar(m_headerBar);

    m_fontButton.signal_font_set().connect(
        [this]()
        {
            m_drawingArea.set_font(m_fontButton.get_font_name());
        }
    );

    add(m_drawingArea);
    show_all();
}

int main(int argc, char *argv[])
{
    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
  
    MyWindow window;
    window.show_all();
  
    return app->run(window);
}

我不是Pango专家,所以我不知道使用你的初始策略是否有更好的方法。此外,表示字体名称的字符串似乎需要遵循一些约定。来自 Pango::FontDescription::FontDescription(const Glib::ustring& font_name) (Gtkmm 3.24) 的文档:

font_name must have the form "[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]", where FAMILY-LIST is a comma separated list of families optionally terminated by a comma, STYLE_OPTIONS is a whitespace separated list of words where each WORD describes one of style, variant, weight, or stretch, and SIZE is an decimal number (size in points). Any one of the options may be absent. If FAMILY-LIST is absent, then the family_name field of the resulting font description will be initialized to 0. If STYLE-OPTIONS is missing, then all style options will be set to the default values. If SIZE is missing, the size in the resulting font description will be set to 0.

因此您可能需要自己进行一些测试,以确保这适用于所有字体。就我而言,我测试过的所有 10-15 种字体似乎都可以正常工作。