Compartir vía


Compatibilidad con temas oscuros y claros en aplicaciones Win32

Windows admite temas claros y oscuros como opción de personalización en la configuración de Windows. Windows usa el modo Claro de forma predeterminada, pero los usuarios pueden elegir el modo Oscuro, que cambia gran parte de la interfaz de usuario a un color oscuro. Es posible que los usuarios prefieran esta configuración porque es más fácil de ver en entornos de poca luz, o pueden simplemente preferir una interfaz más oscura en general. Además, los colores más oscuros de la interfaz de usuario pueden reducir el uso de la batería en algunos tipos de pantallas de ordenador, como pantallas OLED.

A split image of an app in light theme on the left, and dark theme on the right.

Estamos trabajando para ampliar la compatibilidad con el modo Oscuro sin interrumpir las aplicaciones existentes y, para ello, estamos proporcionando instrucciones técnicas para actualizar una aplicación de Windows de escritorio de Win32 para admitir los modos claro y oscuro.

Modo oscuro frente al modo claro

El modo de color en la configuración (que incluye los modos claro y oscuro) es una configuración que define los colores generales de primer plano y de fondo para el sistema operativo y las aplicaciones.

Mode Descripción Ejemplo
Claro Fondo claro con un primer plano oscuro en contraste.

En el modo claro, generalmente verá texto negro u oscuro en fondos blancos o claros.
A screenshot of the Alarms & Clock app in light mode
Oscuro Fondo oscuro con un primer plano claro en contraste.

En modo oscuro, generalmente verá texto blanco o claro sobre fondos negros u oscuros.
A screenshot of the Alarms & Clocks app in Dark mode

Nota:

La razón por la que usamos "negro u oscuro" y "blanco o claro" es porque hay colores adicionales, como el color de énfasis, que pueden matizar varios colores de primer plano y de fondo. Por lo tanto, es posible que vea texto azul claro sobre un fondo azul oscuro en algunas partes de la interfaz de usuario, y todavía se consideraría una interfaz de usuario aceptable en modo oscuro.

Debido a la amplia diversidad de interfaces de usuario en diferentes aplicaciones, el modo de color y los colores de primer plano y de fondo están diseñados como instrucciones orientativas más que como una regla rígida:

  • Los elementos de primer plano, los resaltados y el texto deben estar más cerca del color de primer plano que del color de fondo.
  • Por lo general, las áreas de fondo grandes y sólidas y los fondos de texto deben estar más cerca del color de fondo que del color de primer plano.

En la práctica, esto significa que en el modo Oscuro, la mayor parte de la interfaz de usuario será oscura y en el modo Claro la mayor parte de la interfaz de usuario será clara. El concepto de fondo en Windows es el área grande de colores de una aplicación o el color de la página. El concepto de primer plano en Windows es el color del texto.

Sugerencia

Si le resulta confuso que el color de primer plano sea claro en modo Oscuro y oscuro en modo Claro, puede ayudarle si piensa que el color de primer plano es "el color del texto predeterminado".

Habilitación de la compatibilidad con los modos de cambio de color

Hay muchos enfoques para implementar la compatibilidad con el modo Oscuro en una aplicación. Algunas aplicaciones contienen dos conjuntos de interfaces de usuario (uno con un color claro y otro con un color oscuro). Algunos marcos de interfaz de usuario de Windows, como WinUI 3, detectan automáticamente el tema de un sistema y ajustan la interfaz de usuario para que siga el tema del sistema. Para ser completamente compatible con el modo Oscuro, la totalidad de la superficie de una aplicación debe seguir el tema oscuro.

Hay dos puntos principales que puede hacer en su aplicación Win32 para que sea compatible con temas claros y oscuros.

  • Saber cuándo está habilitado el modo Oscuro

    Saber cuándo está habilitado el modo Oscuro en la configuración del sistema puede ayudarle a saber cuándo cambiar la interfaz de usuario de la aplicación a una interfaz de usuario con temas en modo Oscuro.

  • Habilitar una barra de título en modo Oscuro para aplicaciones Win32

    No todas las aplicaciones Win32 admiten el modo Oscuro, por lo que Windows proporciona a las aplicaciones Win32 una barra de título claro de forma predeterminada. Si está preparado para admitir el modo Oscuro, puede pedir a Windows que dibuje la barra de título oscura en su lugar cuando el modo Oscuro esté habilitado.

Nota

En este artículo se proporcionan ejemplos de formas de detectar cambios en el tema del sistema y solicitar una barra de título clara u oscura para la ventana de la aplicación Win32. No cubre los detalles de cómo volver a pintar y representar la interfaz de usuario de la aplicación mediante un conjunto de colores en modo Oscuro.

Saber cuándo está habilitado el modo Oscuro

El primer paso es realizar un seguimiento de la propia configuración del modo de color. Esto le permitirá ajustar el código de representación y pintura de la aplicación para usar un conjunto de colores en modo Oscuro. Para hacerlo, la aplicación debe leer la configuración de color en el inicio y saber cuándo cambia durante una sesión de la aplicación.

Para ello en una aplicación Win32, use Windows::UI::Color y detecte si un color se puede clasificar como claro u oscuro. Para usar Windows::UI::Color, debe importar (en pch.h) el encabezado Windows.UI.ViewManagement de winrt.

#include <winrt/Windows.UI.ViewManagement.h>

Incluya también ese espacio de nombres en main.cpp.

using namespace Windows::UI::ViewManagement;

En main.cpp, use esta función para detectar si un color se puede clasificar como claro.

inline bool IsColorLight(Windows::UI::Color& clr)
{
    return (((5 * clr.G) + (2 * clr.R) + clr.B) > (8 * 128));
}

Esta función realiza un cálculo rápido del brillo percibido de un color y tiene en cuenta las formas en que los diferentes canales de un valor de color RGB contribuyen al brillo que percibe el ojo humano. Usa matemáticas de todo enteros para acelerar las CPU típicas.

Nota

Este no es un modelo para el análisis real del brillo del color. Está bien para cálculos rápidos que requieren determinar si un color se puede clasificar como claro u oscuro. Los colores del tema a menudo pueden ser claros, pero no blanco puro, u oscuros, pero no negro puro.

Ahora que tiene una función para comprobar si un color es claro, puede usar esa función para detectar si el modo Oscuro está habilitado.

El modo Oscuro se define como un fondo oscuro con un primer plano claro en contraste. Dado que IsColorLight comprueba si un color se considera claro, puede usar esa función para ver si el primer plano es claro. Si el primer plano es claro, se habilita el modo Oscuro.

Para ello, debe obtener el tipo de color de la interfaz de usuario del primer plano de la configuración del sistema. Use este código en main.cpp.

auto settings = UISettings();
    
auto foreground = settings.GetColorValue(UIColorType::Foreground);

UISettings obtiene toda la configuración de la interfaz de usuario, incluido el color. Llame a UISettings.GetColorValue(UIColorType::Foreground) para obtener el valor de color de primer plano de la configuración de la interfaz de usuario.

Ahora puede ejecutar una comprobación para ver si el primer plano se considera claro (en main.cpp).

bool isDarkMode = static_cast<bool>(IsColorLight(foreground));

wprintf(L"\nisDarkMode: %u\n", isDarkMode);
  • Si el primer plano es claro, isDarkMode se evaluará como 1 (true) lo que significa que el modo Oscuro está habilitado.
  • Si el primer plano es claro, isDarkMode se evaluará como 0 (false) lo que significa que el modo Oscuro no está habilitado.

Para realizar un seguimiento automático de cuándo cambia la configuración del modo Oscuro durante una sesión de la aplicación, puede encapsular comprobaciones similares a esta.

auto revoker = settings.ColorValuesChanged([settings](auto&&...)
{
    auto foregroundRevoker = settings.GetColorValue(UIColorType::Foreground);
    bool isDarkModeRevoker = static_cast<bool>(IsColorLight(foregroundRevoker));
    wprintf(L"isDarkModeRevoker: %d\n", isDarkModeRevoker);
});

El código completo debe ser similar al siguiente:

inline bool IsColorLight(Windows::UI::Color& clr)
{
    return (((5 * clr.G) + (2 * clr.R) + clr.B) > (8 * 128));
}

int main()
{
    init_apartment();

    auto settings = UISettings();
    auto foreground = settings.GetColorValue(UIColorType::Foreground);

    bool isDarkMode = static_cast<bool>(IsColorLight(foreground));
    wprintf(L"\nisDarkMode: %u\n", isDarkMode);

    auto revoker = settings.ColorValuesChanged([settings](auto&&...)
        {
            auto foregroundRevoker = settings.GetColorValue(UIColorType::Foreground);
            bool isDarkModeRevoker = static_cast<bool>(IsColorLight(foregroundRevoker));
            wprintf(L"isDarkModeRevoker: %d\n", isDarkModeRevoker);
        });
    
    static bool s_go = true;
    while (s_go)
    {
        Sleep(50);
    }
}

Cuando se ejecuta este código:

Si el modo Oscuro está habilitado, isDarkMode se evaluará como 1.

A screenshot of an app in dark mode.

Al cambiar la configuración del modo Oscuro al modo Claro, isDarkModeRevoker se evaluará en 0.

A screenshot of an app in light mode.

Habilitación de una barra de título en modo Oscuro para aplicaciones Win32

Windows no sabe si una aplicación puede admitir el modo Oscuro, por lo que supone que no puede por motivos de compatibilidad con versiones anteriores. Algunos marcos de desarrollo de Windows, como el SDK de aplicaciones de Windows, admiten el modo Oscuro de forma nativa y cambian determinados elementos de la interfaz de usuario sin código adicional. Las aplicaciones Win32 no suelen admitir el modo Oscuro, por lo que Windows proporciona a las aplicaciones Win32 una barra de título claro de forma predeterminada.

Sin embargo, para cualquier aplicación que use la barra de título estándar de Windows, puede habilitar su versión oscura cuando el sistema esté en modo Oscuro. Para habilitar la barra de título oscura, llame a una función de Desktop Windows Manager (DWM) llamada DwmSetWindowAttribute en la ventana de nivel superior, con el atributo de ventana DWMWA_USE_IMMERSIVE_DARK_MODE. (DWM representa los atributos de una ventana).

En los ejemplos siguientes se supone que tiene una ventana con una barra de título estándar, como la creada por este código.

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, 
     CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

En primer lugar, debe importar la API de DWM, de este modo.

#include <dwmapi.h>

A continuación, defina las macros DWMWA_USE_IMMERSIVE_DARK_MODE encima de la función InitInstance.

#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
…

Por último, puede usar la API de DWM para establecer la barra de título para que use un color oscuro. Aquí, creará un elemento BOOL llamado value y lo establecerá en TRUE. Este elemento BOOL se usa para desencadenar esta configuración de atributo de Windows. A continuación, se usa DwmSetWindowAttribute para cambiar el atributo de ventana para que use colores en modo Oscuro.

BOOL value = TRUE;
::DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));

A continuación, se da una explicación más detallada de lo que hace esta llamada.

El bloque de sintaxis para DwmSetWindowAttribute tiene el aspecto siguiente.

HRESULT DwmSetWindowAttribute(
       HWND    hwnd,
       DWORD   dwAttribute,
  [in] LPCVOID pvAttribute,
       DWORD   cbAttribute
);

Después de pasar hWnd (el identificador para la ventana que quiere cambiar) como primer parámetro, debe pasar DWMWA_USE_IMMERSIVE_DARK_MODE como parámetro dwAttribute. Se trata de una constante en la API de DWM que permite dibujar el marco de Windows en colores en modo Oscuro cuando la configuración del sistema en modo Oscuro está habilitada. Si cambia al modo Claro, tendrá que cambiar DWMWA_USE_IMMERSIVE_DARK_MODE de 20 a 0 para que la barra de título se dibuje en colores del modo Claro.

El parámetro pvAttribute apunta a un valor de tipo BOOL (que es por lo que creó el valor BOOL antes). Es necesario que pvAttribute sea TRUE para respetar el modo Oscuro de la ventana. Si pvAttribute es FALSE, la ventana usará el modo Claro.

Por último, cbAttribute debe tener el tamaño del atributo que se establece en pvAttribute. Para hacerlo fácilmente, pasamos sizeof(value).

El código para dibujar una barra de título de ventanas oscuras debe tener este aspecto.

#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   BOOL value = TRUE;
   ::DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

Cuando se ejecuta este código, la barra de título de la aplicación debe ser oscura:

A screenshot of an app with a dark title bar.

Consulte también