aboutsummaryrefslogtreecommitdiff
path: root/src/application.cpp
blob: 7be56f56aa9c378d6b8c568f73f82270dc1fa595 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include "application.h"

#include "exception.h"
#include "timer.h"
#include "ui/window.h"
#include "ui/cursor.h"
#include "graph/graph.h"
#include "ui/animations/animation.h"

namespace cru {
    constexpr auto god_window_class_name = L"GodWindowClass";
    constexpr int invoke_later_message_id = WM_USER + 2000;


    LRESULT GodWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        const auto app = Application::GetInstance();

        if (app)
        {
            const auto result = app->GetGodWindow()->HandleGodWindowMessage(hWnd, uMsg, wParam, lParam);
            if (result.has_value())
                return result.value();
            else
                return DefWindowProc(hWnd, uMsg, wParam, lParam);
        }
        else
            return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

    GodWindow::GodWindow(Application* application) : application_(application)
    {
        const auto h_instance = application->GetInstanceHandle();

        god_window_class_ = std::make_unique<ui::WindowClass>(god_window_class_name, GodWndProc, h_instance);

        hwnd_ = CreateWindowEx(0,
            god_window_class_name,
            L"", 0,
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            nullptr, nullptr, h_instance, nullptr
        );

        if (hwnd_ == nullptr)
            throw std::runtime_error("Failed to create window.");
    }

    GodWindow::~GodWindow()
    {
        ::DestroyWindow(hwnd_);
    }

    std::optional<LRESULT> GodWindow::HandleGodWindowMessage(HWND hwnd, int msg, WPARAM w_param, LPARAM l_param)
    {
        switch (msg)
        {
        case invoke_later_message_id:
        {
            const auto p_action = reinterpret_cast<std::function<void()>*>(w_param);
            (*p_action)();
            delete p_action;
            return 0;
        }
        case WM_TIMER:
        {
            const auto id = static_cast<UINT_PTR>(w_param);
            const auto action = application_->GetTimerManager()->GetAction(id);
            if (action.has_value())
            {
                (action.value().second)();
                if (!action.value().first)
                    Application::GetInstance()->GetTimerManager()->KillTimer(id);
                return 0;
            }
            break;
        }
        default:
            return std::nullopt;
        }
        return std::nullopt;
    }



    Application* Application::instance_ = nullptr;

    Application * Application::GetInstance() {
        return instance_;
    }

    namespace
    {
        CaretInfo GetSystemCaretInfo()
        {
            CaretInfo caret_info;
            caret_info.caret_blink_duration = std::chrono::milliseconds(::GetCaretBlinkTime());
            DWORD caret_width;
            if (!::SystemParametersInfoW(SPI_GETCARETWIDTH, 0 , &caret_width, 0))
                throw Win32Error(::GetLastError(), "Failed to get system caret width.");
            caret_info.half_caret_width = caret_width / 2.0f;
            return caret_info;
        }

        void LoadSystemCursor(HINSTANCE h_instance)
        {
            ui::cursors[ui::cursor_arrow_key] = std::make_shared<ui::Cursor>(::LoadCursorW(h_instance, MAKEINTRESOURCEW(IDC_ARROW)), false);
            ui::cursors[ui::cursor_hand_key] = std::make_shared<ui::Cursor>(::LoadCursorW(h_instance, MAKEINTRESOURCEW(IDC_HAND)), false);
            ui::cursors[ui::cursor_i_beam_key] = std::make_shared<ui::Cursor>(::LoadCursorW(h_instance, MAKEINTRESOURCEW(IDC_IBEAM)), false);
        }
    }

    Application::Application(HINSTANCE h_instance)
        : h_instance_(h_instance) {

        if (instance_)
            throw std::runtime_error("A application instance already exists.");

        instance_ = this;

        window_manager_ = std::make_unique<ui::WindowManager>();
        graph_manager_ = std::make_unique<graph::GraphManager>();
        timer_manager_ = std::make_unique<TimerManager>();
        animation_manager_ = std::make_unique<ui::animations::details::AnimationManager>();

        god_window_ = std::make_unique<GodWindow>(this);

#ifdef CRU_DEBUG_LAYOUT
        debug_layout_resource_.out_border_brush = graph::CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Crimson));
        debug_layout_resource_.margin_brush = graph::CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::LightCoral, 0.25f));
        debug_layout_resource_.padding_brush = graph::CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::SkyBlue, 0.25f));
#endif

        caret_info_ = GetSystemCaretInfo();

        LoadSystemCursor(h_instance);
    }

    Application::~Application()
    {
        ui::cursors.clear();

        animation_manager_.reset();
        instance_ = nullptr;
    }

    int Application::Run()
    {
        MSG msg;

        while (GetMessage(&msg, nullptr, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return static_cast<int>(msg.wParam);
    }

    void Application::Quit(const int quit_code) {
        ::PostQuitMessage(quit_code);
    }

    void InvokeLater(const std::function<void()>& action) {
        //copy the action to a safe place
        auto p_action_copy = new std::function<void()>(action);

        if (PostMessageW(Application::GetInstance()->GetGodWindow()->GetHandle(), invoke_later_message_id, reinterpret_cast<WPARAM>(p_action_copy), 0) == 0)
            throw Win32Error(::GetLastError(), "InvokeLater failed to post message.");
    }
}