9.1 KiB
Telegram Desktop UI Styling
Style Definition Files
UI element styles (colors, fonts, paddings, margins, icons, etc.) are defined in .style files using a custom syntax. These files are located alongside the C++ source files they correspond to within specific UI component directories (e.g., Telegram/SourceFiles/ui/chat/chat.style).
Definitions from other .style files can be included using the using directive at the top of the file:
using "ui/basic.style";
using "ui/widgets/widgets.style";
The central definition of named colors happens in Telegram/SourceFiles/ui/colors.palette. This file allows for theme generation and loading colors from various sources.
Syntax Overview
-
Built-in Types: The syntax recognizes several base types inferred from the value assigned:
int: Integer numbers (e.g.,lineHeight: 20;)bool: Boolean values (e.g.,useShadow: true;)pixels: Pixel values, ending withpx(e.g.,borderWidth: 1px;). Generated asintin C++.color: Named colors defined incolors.palette(e.g.,background: windowBg;)icon: Defined inline using a specific syntax (see below). Generatesstyle::icon.margins: Four pixel values for margins or padding. Requiresmargins(top, right, bottom, left)syntax (e.g.,margin: margins(10px, 5px, 10px, 5px);orpadding: margins(8px, 8px, 8px, 8px);). Generatesstyle::margins(an alias forQMargins).size: Two pixel values for width and height (e.g.,iconSize: size(16px, 16px);). Generatesstyle::size.point: Two pixel values for x and y coordinates (e.g.,textPos: point(5px, 2px);). Generatesstyle::point.align: Alignment keywords (e.g.,textAlign: align(center);oriconAlign: align(left);). Generatesstyle::align.font: Font definitions (e.g.,font: font(14px semibold);). Generatesstyle::font.double: Floating point numbers (e.g.,disabledOpacity: 0.5;)
Note on Borders: Borders are typically defined using multiple fields like
border: pixels;(for width) andborderFg: color;(for color), rather than a single CSS-like property. -
Structure Definition: You can define complex data structures directly within the
.stylefile:MyButtonStyle { // Defines a structure named 'MyButtonStyle' textPadding: margins; // Field 'textPadding' expects margins type icon: icon; // Field 'icon' of type icon height: pixels; // Field 'height' of type pixels }This generates a
struct MyButtonStyle { ... };inside thenamespace style. Fields will have corresponding C++ types (style::margins,style::icon,int). -
Variable Definition & Inheritance: Variables are defined using
name: value;orgroupName { ... }. They can be of built-in types or custom structures. Structures can be initialized inline or inherit from existing variables.Icon Definition Syntax: Icons are defined inline using the
icon{...}syntax. The generator probes for.svgfiles or.pngfiles (including@2x,@3xvariants) based on the provided path stem.// Single-part icon definition: myIconSearch: icon{{ "gui/icons/search", iconColor }}; // Multi-part icon definition (layers drawn bottom-up): myComplexIcon: icon{ { "gui/icons/background", iconBgColor }, { "gui/icons/foreground", iconFgColor } }; // Icon with path modifiers (PNG only for flips, SVG only for size): myFlippedIcon: icon{{ "gui/icons/arrow-flip_horizontal", arrowColor }}; myResizedIcon: icon{{ "gui/icons/logo-128x128", logoColor }}; // Forces 128x128 for SVGOther Variable Examples:
// Simple variables buttonHeight: 30px; activeButtonColor: buttonBgActive; // Named color from colors.palette // Variable of a custom structure type, initialized inline defaultButton: MyButtonStyle { textPadding: margins(10px, 15px, 10px, 15px); // Use margins(...) syntax icon: myIconSearch; // Assign the previously defined icon variable height: buttonHeight; // Reference another variable } // Another variable inheriting from 'defaultButton' and overriding/adding fields primaryButton: MyButtonStyle(defaultButton) { icon: myComplexIcon; // Override icon with the multi-part one backgroundColor: activeButtonColor; // Add a field not in MyButtonStyle definition } // Style group (often used for specific UI elements) chatInput { // Example using separate border properties and explicit padding border: 1px; // Border width borderFg: defaultInputFieldBorder; // Border color (named color) padding: margins(5px, 10px, 5px, 10px); // Use margins(...) syntax for padding field backgroundColor: defaultChatBg; // Background color }
Code Generation
A code generation tool processes these .style files and colors.palette to create C++ objects.
- The
usingdirectives resolve dependencies between.stylefiles. - Custom structure definitions (like
MyButtonStyle) generate correspondingstruct MyButtonStyle { ... };within thenamespace style. - Style variables/groups (like
defaultButton,primaryButton,chatInput) are generated as objects/structs within thestnamespace (e.g.,st::defaultButton,st::primaryButton,st::chatInput). These generated structs contain members corresponding to the fields defined in the.stylefile. - Color objects are generated into the
stnamespace as well, based on their names incolors.palette(e.g.,st::windowBg,st::buttonBgActive). - The generated header files for styles are placed in the
Telegram/SourceFiles/styles/directory with astyle_prefix (e.g.,styles/style_widgets.hforui/widgets/widgets.style). You include them like#include "styles/style_widgets.h".
Generated C++ types correspond to the .style types: style::color, style::font, style::margins (used for both margin: and padding: fields), style::icon, style::size, style::point, style::align, and int or bool for simple types.
Style Usage in Code
Styles are applied in C++ code by referencing the generated st::... objects and their members.
// Example: Including the generated style header
#include "styles/style_widgets.h" // For styles defined in ui/widgets/widgets.style
// ... inside some UI class code ...
// Accessing members of a generated style struct
int height = st::primaryButton.height; // Accessing the 'height' field (pixels -> int)
const style::icon &icon = st::primaryButton.icon; // Accessing the 'icon' field (st::myComplexIcon)
style::margins padding = st::primaryButton.textPadding; // Accessing 'textPadding'
style::color bgColor = st::primaryButton.backgroundColor; // Accessing the color (st::activeButtonColor)
// Applying styles (conceptual examples)
myButton->setIcon(st::primaryButton.icon);
myButton->setHeight(st::primaryButton.height);
myButton->setPadding(st::primaryButton.textPadding);
myButton->setBackgroundColor(st::primaryButton.backgroundColor);
// Using styles directly in painting
void MyWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(rect(), st::chatInput.backgroundColor); // Use color from chatInput style
// Border painting requires width and color
int borderWidth = st::chatInput.border; // Access border width (pixels -> int)
style::color borderColor = st::chatInput.borderFg; // Access border color
if (borderWidth > 0) {
p.setPen(QPen(borderColor, borderWidth));
// Adjust rect for pen width if needed before drawing
p.drawRect(rect().adjusted(borderWidth / 2, borderWidth / 2, -borderWidth / 2, -borderWidth / 2));
}
// Access padding (style::margins)
style::margins inputPadding = st::chatInput.padding;
// ... use inputPadding.top(), inputPadding.left() etc. for content layout ...
}
Key Points:
- Styles are defined in
.stylefiles next to their corresponding C++ source files. using "path/to/other.style";includes definitions from other style files.- Named colors are defined centrally in
ui/colors.palette. .stylesyntax supports built-in types (likepixels,color,margins,point,size,align,font,double), custom structure definitions (Name { field: type; ... }), variable definitions (name: value;), and inheritance (child: Name(parent) { ... }).- Values must match the expected type (e.g., fields declared as
marginstype, likemargin:orpadding:, requiremargins(...)syntax). Borders are typically set via separateborder: pixels;andborderFg: color;fields. - Icons are defined inline using
name: icon{{ "path_stem", color }};orname: icon{ { "path1", c1 }, ... };syntax, with optional path modifiers. - Code generation creates
structdefinitions in thestylenamespace for custom types and objects/structs in thestnamespace for defined variables/groups. - Generated headers are in
styles/with astyle_prefix and must be included. - Access style properties via the generated
st::objects (e.g.,st::primaryButton.height,st::chatInput.backgroundColor).