|
| 1 | +#include <chrono> |
| 2 | +#include <cstdlib> |
| 3 | +#include <functional> |
| 4 | +#include <iostream> |
| 5 | +#include <string> |
| 6 | +#include <thread> |
| 7 | +#include <utility> |
| 8 | +#include <vector> |
| 9 | + |
| 10 | +#include "nativeapi.h" |
| 11 | + |
| 12 | +using nativeapi::Color; |
| 13 | +using nativeapi::VisualEffect; |
| 14 | +using nativeapi::Window; |
| 15 | + |
| 16 | +// Walks a window through every VisualEffect, over a plain red window that gives the |
| 17 | +// effects something to show. What the API reports is checked here - the exit code is the |
| 18 | +// number of expectations that did not hold - and each step prints a "STEP" line once it is |
| 19 | +// on screen, so that whoever watches (tools/gui has a test that does) knows when to look. |
| 20 | +// |
| 21 | +// It drives the windows itself and needs no input. |
| 22 | +int main() { |
| 23 | + nativeapi::SetMainThread(); |
| 24 | + |
| 25 | + int failures = 0; |
| 26 | + auto expect = [&](const std::string& what, bool ok) { |
| 27 | + std::cout << (ok ? "PASS " : "FAIL ") << what << std::endl; |
| 28 | + if (!ok) { |
| 29 | + failures++; |
| 30 | + } |
| 31 | + }; |
| 32 | + |
| 33 | + const std::vector<std::pair<VisualEffect, std::string>> effects = { |
| 34 | + {VisualEffect::Blur, "Blur"}, {VisualEffect::Acrylic, "Acrylic"}, |
| 35 | + {VisualEffect::Hud, "Hud"}, {VisualEffect::Popover, "Popover"}, |
| 36 | + {VisualEffect::Menu, "Menu"}, {VisualEffect::Mica, "Mica"}, |
| 37 | + {VisualEffect::MicaAlt, "MicaAlt"}, |
| 38 | + }; |
| 39 | + |
| 40 | + std::shared_ptr<Window> backdrop; |
| 41 | + std::shared_ptr<Window> window; |
| 42 | + VisualEffect in_force = VisualEffect::None; |
| 43 | + |
| 44 | + std::vector<std::function<void()>> steps = {[&] { |
| 45 | + backdrop = std::make_shared<Window>(); |
| 46 | + backdrop->SetTitle("Backdrop"); |
| 47 | + backdrop->SetBackgroundColor(Color::FromRGBA(255, 0, 0)); |
| 48 | + backdrop->SetContentSize({560, 420}); |
| 49 | + backdrop->SetPosition({200, 160}); |
| 50 | + backdrop->Show(); |
| 51 | + |
| 52 | + window = std::make_shared<Window>(); |
| 53 | + window->SetTitle("Visual effect"); |
| 54 | + window->SetContentSize({400, 260}); |
| 55 | + window->SetPosition({280, 240}); |
| 56 | + // A child stays above its parent, whichever of the two is brought forward. |
| 57 | + window->SetParentWindow(backdrop); |
| 58 | + window->Show(); |
| 59 | + window->Focus(); |
| 60 | + expect("a window starts without a visual effect", |
| 61 | + window->GetVisualEffect() == VisualEffect::None); |
| 62 | + expect("no effect is supported everywhere", |
| 63 | + Window::IsVisualEffectSupported(VisualEffect::None)); |
| 64 | + std::cout << "STEP None applied" << std::endl; |
| 65 | + }}; |
| 66 | + |
| 67 | + for (const auto& [effect, name] : effects) { |
| 68 | + steps.push_back([&, effect = effect, name = name] { |
| 69 | + const bool supported = Window::IsVisualEffectSupported(effect); |
| 70 | + const bool applied = window->SetVisualEffect(effect); |
| 71 | + expect(name + ": SetVisualEffect agrees with IsVisualEffectSupported", applied == supported); |
| 72 | + if (applied) { |
| 73 | + in_force = effect; |
| 74 | + } |
| 75 | + expect(name + ": GetVisualEffect is the effect in force", |
| 76 | + window->GetVisualEffect() == in_force); |
| 77 | + std::cout << "STEP " << name << (applied ? " applied" : " refused") << std::endl; |
| 78 | + }); |
| 79 | + } |
| 80 | + |
| 81 | + steps.push_back([&] { |
| 82 | + // A background color given while an effect is active waits for the effect to go. |
| 83 | + window->SetBackgroundColor(Color::FromRGBA(0, 0, 255)); |
| 84 | + expect("the background color is kept while an effect is active", |
| 85 | + window->GetBackgroundColor().b == 255 && window->GetBackgroundColor().r == 0); |
| 86 | + std::cout << "STEP Background " << (in_force == VisualEffect::None ? "shown" : "waiting") |
| 87 | + << std::endl; |
| 88 | + }); |
| 89 | + steps.push_back([&] { |
| 90 | + expect("the effect can be removed", window->SetVisualEffect(VisualEffect::None)); |
| 91 | + expect("no effect is in force afterwards", window->GetVisualEffect() == VisualEffect::None); |
| 92 | + std::cout << "STEP Removed applied" << std::endl; |
| 93 | + }); |
| 94 | + steps.push_back([&] { |
| 95 | + std::cout << (failures == 0 ? "ALL PASS" : "FAILED") << std::endl; |
| 96 | + // Not Quit(): how a platform's loop ends must not decide the exit code. |
| 97 | + std::cout.flush(); |
| 98 | + std::_Exit(failures); |
| 99 | + }); |
| 100 | + |
| 101 | + std::thread driver([&] { |
| 102 | + for (size_t i = 0; i < steps.size(); i++) { |
| 103 | + // The windows stay as they first come up for a while: a watcher has to find them. |
| 104 | + std::this_thread::sleep_for(std::chrono::milliseconds(i == 1 ? 6000 : 2500)); |
| 105 | + nativeapi::RunOnMainThread(steps[i]); |
| 106 | + } |
| 107 | + }); |
| 108 | + driver.detach(); |
| 109 | + |
| 110 | + // On Windows destroying any window of the library ends the loop (its window |
| 111 | + // procedure posts WM_QUIT), so enter it again: the last step ends the process. |
| 112 | + while (true) { |
| 113 | + nativeapi::Application::GetInstance().Run(); |
| 114 | + } |
| 115 | +} |
0 commit comments