Using fonts¶
Columns UI features centralised configuration of fonts on its Fonts preferences page. This allows fonts for different parts of the UI to be configured in one place.
Third-party panel components can add their own entries to the Fonts preferences page and they can query the current configuration.
Adding a new entry¶
To add a new element to the Columns UI Fonts preferences page, you need to
implement a cui::fonts::client service and register it using the
cui::fonts::client::factory factory.
For an example of this, see the Console panel source.
A single component can add multiple entries by implementing
cui::fonts::client multiple times, each with a different client GUID.
Querying fonts¶
GDI¶
cui::fonts::create_hfont_with_fallback() can be used to create an
HFONT for a particular font client, while
cui::fonts::get_log_font_with_fallback() and can be used to get the
LOGFONT structure for a particular font client. There are also various other
utility functions for other scenarios in the cui::fonts namespace.
DirectWrite¶
Panels using DirectWrite can use cui::fonts::get_font() to retrieve a
cui::fonts::font instance. The
cui::fonts::font::create_text_format() member function can then be
called to create an IDWriteTextFormat object.
You should also apply the rendering options returned by
cui::fonts::font::rendering_options() when rendering text using the text
format.
Note that cui::fonts::get_font() requires Columns UI 3.0.0 or later.
If a compatible version of Columns UI isn’t installed,
cui::fonts::get_font() will return an empty std::optional. Fallback
logic should be implemented for this scenario.
Common fonts¶
It’s also possible to pass the ID of a common font (i.e.
cui::fonts::items_font_id or cui::fonts::labels_font_id) rather
than the ID of a font client to the functions mentioned in the previous two
sections. However, given the ease of implementing a font client to add a custom
entry, it’s not generally recommended to use the common fonts directly.
Rendering text using DirectWrite¶
Rendering using Direct2D¶
Below is a simplified guide to using the font API with Direct2D. Error handling is omitted, as is general Direct2D set-up and rendering code.
In your own code, using the error handling helpers from the Windows Implementation Libraries is recommended.
Refer to the Direct2D documentation for more general information on how to use Direct2D (for example, the Create a simple Direct2D application article).
1. Create a text format and query rendering options¶
When your panel window is created, and when the
cui::fonts::client::on_font_changed() method of your font client is
called, create or recreate a DirectWrite text format and query the rendering
options:
struct TextFormatWrapper
{
pfc::com_ptr_t<IDWriteTextFormat> text_format;
cui::fonts::rendering_options::ptr rendering_options;
};
TextFormatWrapper create_text_format()
{
TextFormatWrapper text_format_wrapper;
const auto font = cui::fonts::get_font(my_font_id);
if (font) {
(void)font->create_text_format(text_format_wrapper.text_format.receive_ptr());
text_format_wrapper.rendering_options = font->rendering_options();
}
if (!text_format_wrapper.text_format.is_valid()) {
// Implement fallback path here – for example, using
// cui::fonts::get_log_font_with_fallback()
// and IDWriteGdiInterop
}
text_format_wrapper.text_format->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
text_format_wrapper.text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
return text_format_wrapper;
}
2. Create a text layout¶
Whenever the text being rendered changes, create a new DirectWrite text layout:
pfc::com_ptr_t<IDWriteTextLayout> create_text_layout(IDWriteFactory* factory,
const TextFormatWrapper& text_format_wrapper)
{
HRESULT hr{};
pfc::com_ptr_t<IDWriteTextLayout> text_layout;
if (text_format_wrapper.rendering_options->use_gdi_compatible_layout()) {
const auto use_gdi_natural = text_format_wrapper.rendering_options->use_gdi_natural();
hr = factory->CreateGdiCompatibleTextLayout(
/* arguments omitted */, use_gdi_natural, text_layout.receive_ptr());
} else {
hr = factory->CreateTextLayout(/* arguments omitted */, text_layout.receive_ptr());
}
if (FAILED(hr)) {
// Handle error
}
return text_layout;
}
3. Render text using Direct2D¶
Within your Direct2D rendering logic, render the text layout with the correct rendering parameters:
void render_text_layout(IDWriteFactory* factory, ID2D1RenderTarget* render_target, IDWriteTextLayout* layout, HWND wnd,
const cui::fonts::rendering_options::ptr& rendering_options)
{
HRESULT hr{};
const auto monitor = cui::dwrite_utils::get_monitor_for_window(wnd);
pfc::com_ptr_t<IDWriteRenderingParams> rendering_params;
hr = rendering_options->create_rendering_params(factory, monitor, rendering_params.receive_ptr());
if (FAILED(hr)) {
// Handle error
}
render_target->SetTextRenderingParams(rendering_params.get_ptr());
render_target->SetTextAntialiasMode(rendering_options->d2d_text_antialiasing_mode());
// Note: D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT is only supported on Windows 8.1 and newer.
// Do not set the flag on older versions of Windows, as it will cause rendering to fail.
// Check for support of D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT by e.g. querying the render
// target for the ID2D1DeviceContext1 interface.
const auto draw_text_opts = rendering_options->use_colour_glyphs() ? D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
: D2D1_DRAW_TEXT_OPTIONS_NONE;
render_target->DrawTextLayout(/* origin */, layout, /* D2D brush */, draw_text_opts);
}
Rendering directly to a GDI device context¶
If you wish to render text using DirectWrite directly to a GDI device context (without using Direct2D), you’ll need to implement a custom IDWriteTextRenderer object.
See Render to a GDI surface and the GdiInterop sample for examples of this.