AyuGramDesktop/.cursor/styling.md
2025-05-01 11:13:22 +04:00

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

  1. 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 with px (e.g., borderWidth: 1px;). Generated as int in C++.
    • color: Named colors defined in colors.palette (e.g., background: windowBg;)
    • icon: Defined inline using a specific syntax (see below). Generates style::icon.
    • margins: Four pixel values for margins or padding. Requires margins(top, right, bottom, left) syntax (e.g., margin: margins(10px, 5px, 10px, 5px); or padding: margins(8px, 8px, 8px, 8px);). Generates style::margins (an alias for QMargins).
    • size: Two pixel values for width and height (e.g., iconSize: size(16px, 16px);). Generates style::size.
    • point: Two pixel values for x and y coordinates (e.g., textPos: point(5px, 2px);). Generates style::point.
    • align: Alignment keywords (e.g., textAlign: align(center); or iconAlign: align(left);). Generates style::align.
    • font: Font definitions (e.g., font: font(14px semibold);). Generates style::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) and borderFg: color; (for color), rather than a single CSS-like property.

  2. Structure Definition: You can define complex data structures directly within the .style file:

    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 the namespace style. Fields will have corresponding C++ types (style::margins, style::icon, int).

  3. Variable Definition & Inheritance: Variables are defined using name: value; or groupName { ... }. 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 .svg files or .png files (including @2x, @3x variants) 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 SVG
    

    Other 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 using directives resolve dependencies between .style files.
  • Custom structure definitions (like MyButtonStyle) generate corresponding struct MyButtonStyle { ... }; within the namespace style.
  • Style variables/groups (like defaultButton, primaryButton, chatInput) are generated as objects/structs within the st namespace (e.g., st::defaultButton, st::primaryButton, st::chatInput). These generated structs contain members corresponding to the fields defined in the .style file.
  • Color objects are generated into the st namespace as well, based on their names in colors.palette (e.g., st::windowBg, st::buttonBgActive).
  • The generated header files for styles are placed in the Telegram/SourceFiles/styles/ directory with a style_ prefix (e.g., styles/style_widgets.h for ui/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 .style files 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.
  • .style syntax supports built-in types (like pixels, 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 margins type, like margin: or padding:, require margins(...) syntax). Borders are typically set via separate border: pixels; and borderFg: color; fields.
  • Icons are defined inline using name: icon{{ "path_stem", color }}; or name: icon{ { "path1", c1 }, ... }; syntax, with optional path modifiers.
  • Code generation creates struct definitions in the style namespace for custom types and objects/structs in the st namespace for defined variables/groups.
  • Generated headers are in styles/ with a style_ prefix and must be included.
  • Access style properties via the generated st:: objects (e.g., st::primaryButton.height, st::chatInput.backgroundColor).