development version (master branch)
Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
UppHub
Status & Roadmap
FAQ
Authors & License
Forums
Funding U++
Search on this site











SourceForge.net Logo

SourceForge.net Logo

GitHub Logo

Discord Logo

Supporting UHD displays and Dark theme


Table of contents

 

1. GUI mode detection

2. Scaling GUI for actual GUI font and UHD resolution

3. Color adjustment

4. Iml files

 


GUI mode detection

UHD mode is activated when standard GUI font is larger than 24 pixels. Dark theme mode is activated if IsDark(SColorPaper()), which means that grayscale value of default background is less than 80. Note that both modes create 4 combinations in total - standard resolution with light theme, standard resolution with dark theme, UHD resolution with light theme, UHD resolution with dark theme.

IsUHDMode() and IsDarkTheme() functions return respective current GUI status.


Scaling GUI for actual GUI font and UHD resolution

U++ coordinates in drawing operations are always in real pixels for screen targets. U++ provides various functions to adjust GUI elements metrics to host platform font size and UHD mode. Some of these function use 'font-ratio', which is the ratio of metrics of current default GUI font to 'design-font' (which is more or less defined as font where text "OK Cancel Exit Retry" has Size(99, 13), font ratio is then current GUI font Size vs this predefined size).

int Zx(int cx);

double Zxf(double cx);

Scales the value horizontally based on font ratio.

int Zy(int cy);

Scales the value vertically based on font ratio.

Size Zsz(int cx, int cy);

Size Zsz(Size sz);

Scales size based on font ratio.

Font FontZ(int face, int height = 0);

Font StdFontZ(int height = 0);

Font SansSerifZ(int height = 0);

Font SerifZ(int height = 0);

Font MonospaceZ(int height = 0);

Font RomanZ(int height = 0);

Font ArialZ(int height = 0);

Font CourierZ(int height = 0);

Creates the font while scaling its height based on current font ratio. If height is 0, the height is set to current GUI font height.

int DPI(int a);

double DPI(double a);

Size DPI(Size sz);

Size DPI(int cx, int cy);

If UHD mode is active, returns the input argument multiplied by 2, otherwise returns it unchanged.

Image DPI(const Image& a, const Image& b);

Returns b if UHD is active, a otherwise.

Usually DPI functions are used if the value is Image related, 'Z' functions if it is text size related.


Color adjustment

If application is specifying any colors, these colors need to be adjusted for dark theme. This can be often done by using predefined colors. Sometimes only the light theme color is available that needs to be converted to the dark theme - this can be done using DarkTheme function. Alternatively AdjustIfDark converts the color with DarkTheme only if dark theme mode is currently active.


Iml files

Iml files most often contain images that are used in GUI interface. Obviously, these images must be usually different for any of 4 GUI modes.

.iml should always contain images for either standard or UHD resolution and the light theme. These images are used to define the set of icons.

U++ then uses smart algorithms to convert such images for the current GUI mode. These work acceptably well in most cases.

Developer might decide to provide dedicated variants for any image for any target mode which will be used instead of converted basic icon. Such variant can be either placed into the same .iml file or into separate .iml file.

The complete control of the process is available in image details in the icon designer:

 

 

 

Fixed

Image is never scaled or darkened to match current mode.

Fixed colors

Image is never darkened to match current mode. Set this if the image looks better in the dark mode without actually converting it.

Fixed size

Image is never scaled to match current mode.

UHD variant

Image is variant for UHD mode.

Dark variant

Image is variant for Dark theme.

Supersampled 3x

Image is downsampled 3x before use - this simplifies design of antialiased images as images are drawn and stored as unaliased.

Export...

This is unrelated to UHD / Dark theme mode, but if this is checked, the Image is exported as .ico and .png files. This is intended for application icon (e.g. shown in host shell).

 

Finally, it is also possible to disable automatic conversion for the whole .iml file by #defining FIXED_COLORS and/or FIXED_SIZE macros:

 

#define IMAGECLASS MyImg

#define IMAGEFILE <MyApp/MyImg.iml>

#define FIXED_COLORS

#define FIXED_SIZE

 

#include <Draw/iml_source.h>

 


Reacting to theme changes

Before 2025.1 U++ release, the application skin was loaded from host platform just once at the start of execution and if user changed host platform theme, this was not reflected in the running application. Also changing to user defined or some predefined skin required restart of application to work properly.

Since 2025.1, application can be made to react to host platform changes and can change skins without restart with a call to Ctrl::SkinChangeSensitive() (e.g. in GUI_APP_MAIN). However, this poses development challenges with switching between light theme and dark theme.

While application can easily react in Paint to current mode, situation is much more complicated when colors or images are used as attributes of widgets or widget contents. For example ArrayCtrl::EvenRowColor can be called by developer to define some color that is appropriate for light theme, theme but when the theme is switched can be no longer viable.

U++ provides several tools to handle this problem:

All .iml images are now considered "special logical constants" whose appearance changes according to current theme (dark / light) - this applies to all copies as well.

Predefined colors constants are also "special logical constants" that are interpreted differently after theme switch.

Special logical type of Color - SColor - that is defined by function that is reevaluated after theme switch, e.g.

static SColor light_highlight([] { return Blend(SColorHighlight(), SColorPaper()) });

 

Another special logical type of Color - AColor - which should be defined as light theme color value, but when in dark theme, adjusts its value to the dark theme (with DarkThemeCached function)

RichText / QTF can use DarkThemeCached to adjust colors, which is automatically done e.g. in Labels or RichTextCtrl when rendered on dark backgrounds

If everything else fails, Ctrl::Skin virtual function is always called on widget opening and on theme changes giving developer chance to alter colors are required

To simplify testing, in debug mode Ctrl + Num[*] toggles quickly between light and dark theme - that way developer can check whether colors adjust correctly to the situation.

Demonstration of these features is in reference/ThemeChangeSensitive example.

Do you want to contribute?