Udostępnij przez


Obsługa motywów ciemnych i jasnych w aplikacjach Win32

System Windows obsługuje motywy Light i Ciemny jako opcję personalizacji w ustawieniach systemu Windows. System Windows domyślnie używa trybu Jasny, ale użytkownicy mogą wybrać tryb ciemny, który zmienia większość interfejsu użytkownika na ciemny kolor. Użytkownicy mogą preferować to ustawienie, ponieważ łatwiej jest na oczach w środowiskach o małych światłach lub po prostu wolą ciemniejszy interfejs ogólnie. Ponadto ciemniejsze kolory interfejsu użytkownika mogą zmniejszyć użycie baterii na niektórych typach wyświetlaczy komputerowych, takich jak ekrany OLED.

Podzielony obraz aplikacji w jasnym motywie po lewej stronie i ciemny motyw po prawej stronie.

Ciężko pracujemy nad poszerzeniem obsługi trybu ciemnego bez przerywania istniejących aplikacji. W tym celu udostępniamy wskazówki techniczne dotyczące aktualizowania aplikacji systemu Windows dla komputerów Win32 w celu obsługi trybów jasnych i ciemnych.

Tryb ciemny a tryb jasny

Tryb kolorów w ustawieniach (w tym tryby jasny i ciemny) to ustawienie definiujące pierwszego planu i tła w systemie operacyjnym i aplikacjach.

Tryb Opis Przykład
Światło Jasne tło z kontrastowym ciemnym pierwszym planem.

W trybie jasnym na ogół zobaczysz czarny lub ciemny tekst na białym lub jasnym tle.
Zrzut ekranu przedstawiający aplikację Alarmy i zegar w trybie świetlnym
ciemny Ciemne tło z kontrastowym jasnym pierwszym planem.

W trybie ciemnym na ogół zobaczysz biały lub jasny tekst na czarnych lub ciemnych tłach.
Zrzut ekranu aplikacji Alarmy i zegary w trybie ciemnym

Uwaga / Notatka

Powodem, dla którego używamy "czarny lub ciemny" i "biały lub jasny" jest to, że istnieją dodatkowe kolory, takie jak kolor akcentu, który może zmieniać odcień różnych kolorów pierwszego planu i tła. W rzeczywistości możesz zobaczyć jasnoniebieski tekst na ciemnoniebieskim tle w niektórych częściach interfejsu użytkownika i to będzie nadal uważane za akceptowalny interfejs użytkownika trybu ciemnego.

Ze względu na szeroką różnorodność interfejsu użytkownika w różnych aplikacjach, tryb kolorów i kolory pierwszego planu i tła są przeznaczone jako bardziej wskazówki kierunkowe niż twarda reguła:

  • Elementy pierwszego planu, wyróżnienia i tekst powinny być bliżej koloru pierwszego planu niż kolor tła.
  • Duże, jednolite obszary tła oraz tła tekstu powinny zazwyczaj być bardziej zbliżone do koloru tła niż do koloru pierwszego planu.

W praktyce oznacza to, że w trybie ciemnym większość interfejsu użytkownika będzie ciemna, a w trybie jasnym większość interfejsu użytkownika będzie jasna. Pojęcie tła w systemie Windows to duży obszar kolorów w aplikacji lub kolor strony. Koncepcja pierwszego planu w systemie Windows jest kolorem tekstu.

Wskazówka

Jeśli okaże się mylące, że kolor pierwszego planu jest jasny w trybie ciemnym i ciemny w trybie jasnym, warto pomyśleć o kolorze pierwszego planu jako „domyślny kolor tekstu”.

Włączanie obsługi przełączania trybów kolorów

Istnieje wiele podejść do implementowania obsługi trybu ciemnego w aplikacji. Niektóre aplikacje zawierają dwa zestawy interfejsów użytkownika (jeden z jasnym kolorem i jeden z ciemnym kolorem). Niektóre struktury interfejsu użytkownika systemu Windows, takie jak WinUI 3, automatycznie wykrywają motyw systemu i dostosowują interfejs użytkownika do motywu systemowego. Aby w pełni obsługiwać tryb ciemny, cała powierzchnia aplikacji musi być zgodna z ciemnym motywem.

Istnieją dwie główne rzeczy, które można wykonać w aplikacji Win32, aby obsługiwać motywy jasne i ciemne.

  • Rozpoznaj, kiedy tryb ciemny jest włączony

    Znajomość, kiedy tryb ciemny jest włączony w ustawieniach systemowych, może pomóc Ci znać, kiedy przełączyć interfejs użytkownika aplikacji na interfejs użytkownika z motywem ciemnym.

  • Włączanie paska tytułu trybu ciemnego dla aplikacji Win32

    Nie wszystkie aplikacje Win32 obsługują tryb ciemny, dlatego system Windows domyślnie udostępnia aplikacjom Win32 jasny pasek tytułu. Jeśli chcesz obsługiwać tryb ciemny, możesz poprosić system Windows o rysowanie ciemnego paska tytułu zamiast tego po włączeniu trybu ciemnego.

Uwaga / Notatka

Ten artykuł zawiera przykłady sposobów wykrywania zmian motywu systemowego oraz żądania jasnego lub ciemnego paska tytułu dla okna aplikacji Win32. Nie obejmuje on szczegółowych informacji na temat sposobu przemalowania i renderowania interfejsu użytkownika aplikacji przy użyciu zestawu kolorów trybu ciemnego.

Dowiedz się, kiedy jest włączony tryb ciemny

Pierwszym krokiem jest utrzymanie kontroli nad ustawieniami trybu kolorów. Umożliwi to dostosowanie kodu malowania i renderowania aplikacji w celu użycia zestawu kolorów trybu ciemnego. W ten sposób aplikacja musi odczytać ustawienie koloru podczas uruchamiania i wiedzieć, kiedy ustawienie koloru zmienia się podczas sesji aplikacji.

Aby to zrobić w aplikacji Win32, użyj Windows::UI::Color i wykryć, czy kolor można sklasyfikować jako jasny lub ciemny. Aby użyć Windows::UI::Color, należy zaimportować (w pch.h) nagłówek Windows.UI.ViewManagement z winrt.

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

Uwzględnij również tę przestrzeń nazw w main.cpp.

using namespace Windows::UI::ViewManagement;

W main.cppużyj tej funkcji, aby wykryć, czy kolor można sklasyfikować jako jasny.

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

Ta funkcja wykonuje szybkie obliczenie postrzeganej jasności koloru i bierze pod uwagę sposoby, w jaki różne kanały w wartości koloru RGB przyczyniają się do tego, jak jasny wygląda na ludzkie oko. Używa arytmetyki całkowitoliczbowej dla szybkości na typowych procesorach.

Uwaga / Notatka

Nie jest to model do rzeczywistej analizy jasności kolorów. Jest to dobre w przypadku szybkich obliczeń, które wymagają określenia, czy kolor można sklasyfikować jako jasny lub ciemny. Kolory motywu często mogą być jasne, ale nie czyste białe lub ciemne, ale nie czyste czarne.

Teraz, gdy masz funkcję do sprawdzania, czy kolor jest jasny, możesz użyć tej funkcji, aby wykryć, czy tryb ciemny jest włączony.

Tryb ciemny jest definiowany jako ciemne tło z kontrastowym jasnym pierwszym planem. Ponieważ IsColorLight sprawdza, czy kolor jest uznawany za jasny, możesz użyć tej funkcji, aby sprawdzić, czy pierwszy plan jest jasny. Jeśli pierwszy plan jest jasny, tryb ciemny jest włączony.

Aby to zrobić, pobierz typ koloru UI pierwszego planu z ustawień systemowych. Użyj tego kodu w main.cpp.

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

UISettings pobiera wszystkie ustawienia interfejsu użytkownika, w tym kolor. Wywołaj UISettings.GetColorValue(UIColorType::Foreground), aby uzyskać wartość koloru pierwszego planu z ustawień interfejsu użytkownika.

Teraz możesz przeprowadzić test, aby sprawdzić, czy kolor pierwszoplanowy jest uznawany za jasny (w main.cpp).

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

wprintf(L"\nisDarkMode: %u\n", isDarkMode);
  • Jeśli pierwszy plan jest jasny, isDarkMode oceni wartość 1 (true), co oznacza, że włączony jest tryb ciemny.
  • Jeśli pierwszy plan jest ciemny, isDarkMode oceni wartość 0 (false), co oznacza, że tryb ciemny nie jest włączony.

Aby automatycznie śledzić zmiany ustawień trybu ciemnego podczas sesji aplikacji, możesz opakowować testy w ten sposób.

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);
});

Pełny kod powinien wyglądać następująco.

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);
    }
}

Po uruchomieniu tego kodu:

Jeśli tryb ciemny jest włączony, isDarkMode oceni wartość 1.

Zrzut ekranu aplikacji w trybie ciemnym.

Zmiana ustawienia z trybu ciemnego na tryb jasny spowoduje, że isDarkModeRevoker przyjmie wartość 0.

Zrzut ekranu aplikacji w trybie jasnym.

Włączanie paska tytułu trybu ciemnego dla aplikacji Win32

System Windows nie wie, czy aplikacja może obsługiwać tryb ciemny, więc zakłada, że nie może ze względu na zgodność z poprzednimi wersjami. Niektóre struktury programistyczne systemu Windows, takie jak zestaw SDK aplikacji systemu Windows, obsługują tryb ciemny natywnie i zmieniają niektóre elementy interfejsu użytkownika bez dodatkowego kodu. Aplikacje Win32 często nie obsługują trybu ciemnego, więc system Windows domyślnie udostępnia aplikacjom Win32 jasny pasek tytułu.

Jednak w przypadku każdej aplikacji korzystającej ze standardowego paska tytułu systemu Windows można włączyć ciemną wersję paska tytułu, gdy system jest w trybie ciemnym. Aby włączyć ciemny pasek tytułu, wywołaj funkcję Desktop Windows Manager (DWM) o nazwie DwmSetWindowAttribute w oknie najwyższego poziomu przy użyciu atrybutu okna DWMWA_USE_IMMERSIVE_DARK_MODE. (DwM renderuje atrybuty dla okna).

W poniższych przykładach założono, że masz okno ze standardowym paskiem tytułu, takim jak ten utworzony przez ten kod.

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;
}

Najpierw musisz zaimportować interfejs API DWM, podobnie jak w tym przypadku.

#include <dwmapi.h>

Następnie zdefiniuj makra DWMWA_USE_IMMERSIVE_DARK_MODE powyżej funkcji InitInstance.

#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
…

Na koniec możesz użyć interfejsu API DWM, aby ustawić pasek tytułu w celu użycia ciemnego koloru. W tym miejscu utworzysz BOOL o nazwie value i ustawisz ją na TRUE. Ta BOOL służy do wyzwalania tego ustawienia atrybutu systemu Windows. Następnie użyjesz DwmSetWindowAttribute, aby zmienić atrybut okna tak, aby używał kolorów trybu ciemnego.

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

Oto więcej wyjaśnień dotyczących tego, co to wywołanie robi.

Blok składni dla DwmSetWindowAttribute wygląda następująco.

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

Po przekazaniu hWnd (uchwytu do okna, które chcesz zmienić) jako pierwszego parametru, musisz przekazać DWMWA_USE_IMMERSIVE_DARK_MODE jako parametr dwAttribute. Jest to stała w ramach interfejsu API DWM, która umożliwia rysowanie ramki systemu Windows w kolorach trybu ciemnego, gdy włączone jest ustawienie systemowe trybu ciemnego. Jeśli przełączysz się na tryb jasny, musisz zmienić DWMWA_USE_IMMERSIVE_DARK_MODE z 20 na 0, aby pasek tytułu był rysowany w kolorach trybu jasnego.

Parametr pvAttribute wskazuje na wartość typu BOOL, co wyjaśnia, dlaczego wcześniej utworzyłeś wartość BOOL. Aby uhonorować tryb ciemny dla okna, pvAttribute musi być TRUE. Jeśli pvAttribute jest FALSE, w oknie zostanie użyty tryb światła.

Na koniec cbAttribute musi mieć rozmiar atrybutu ustawianego w pvAttribute. Aby łatwo to zrobić, przekazujemy sizeof(value).

Twój kod do rysowania ciemnego paska tytułu okna powinien wyglądać następująco.

#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;
}

Po uruchomieniu tego kodu pasek tytułu aplikacji powinien być ciemny:

Zrzut ekranu aplikacji z ciemnym paskiem tytułu.

Zobacz także