summaryrefslogtreecommitdiff
path: root/Rx/v2/examples/win_text/main.cpp
blob: 709cc2fefa53c75862f6c422b934a6386c4ac242 (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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

// win_text.cpp : Defines the entry point for the application.
//
//

#define STRICT
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

#include <windows.h>
#include <windowsx.h>
#include <ole2.h>
#include <commctrl.h>
#include <shlwapi.h>
#include <shlobj.h>
#include <shellapi.h>

#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Ole32.lib")

#include <new>
#include <utility>
#include <memory>
#include <type_traits>
#include <tuple>
#include <list>

#include "rxcpp/rx.hpp"
// create alias' to simplify code
// these are owned by the user so that
// conflicts can be managed by the user.
namespace rx=rxcpp;
namespace rxsub=rxcpp::subjects;
namespace rxu=rxcpp::util;

// At this time, RxCpp will fail to compile if the contents
// of the std namespace are merged into the global namespace
// DO NOT USE: 'using namespace std;'

#include "unwinder.h"

#include "windows_user.h"
namespace wu = windows_user;

#include "rx_windows_user.h"
namespace rxwu = rxcpp::windows_user;

struct RootWindow : public rxwu::rx_messages, public rxwu::enable_send_call<RootWindow, WM_USER+1>
{
    // window class
    using window_class = wu::window_class<RootWindow>;
    static LPCWSTR class_name() {return L"Scratch";}
    static void change_class(WNDCLASSEX&) {}

    // createstruct parameter type
    using param_type = std::wstring;

    // public methods

    // static methods use a window message per call

    static LRESULT set_title(HWND w, const std::wstring& t) {
        return send_call(w, [&](RootWindow& r){
            r.set_title(t);
            return 0;
        });
    }
    static std::wstring get_title(HWND w) {
        std::wstring t;
        send_call(w, [&](RootWindow& r){
            t = r.get_title();
            return 0;
        });
        return t;
    }

    // instance methods are accessed using static send_call(hwnd, [](RootWindow& r){. . .});
    // send_call uses one window message, the lambda can call many instance methods.

    void set_title(const std::wstring& t) {
        title = t;
    }
    const std::wstring& get_title() {
        return title;
    }

    // lifetime

    // called during WM_NCDESTROY
    ~RootWindow() {
        PostQuitMessage(0);
    }

    // called during WM_NCCREATE
    RootWindow(HWND w, LPCREATESTRUCT, param_type* title)
        : window(w)
        , title(title ? *title : L"RootWindow")
        , position{40, 10} {
        // listen for the following messages
        OnPaint();
        OnPrintClient();
        OnKeyDown();
        OnMovesWhileLButtonDown();
    }

private:
    // implementation

    HWND window;
    std::wstring title;
    POINTS position;

    void PaintContent(PAINTSTRUCT& ps) {
        RECT rect;
        GetClientRect (window, &rect) ;
        SetTextColor(ps.hdc, 0x00000000);
        SetBkMode(ps.hdc,TRANSPARENT);
        rect.left=position.x;
        rect.top=position.y;
        DrawText( ps.hdc, title.c_str(), -1, &rect, DT_SINGLELINE | DT_NOCLIP  ) ;
    }

    void OnKeyDown() {
        messages<WM_KEYDOWN>().
        subscribe([this](auto m) {
            m.handled(); // skip DefWindowProc

            MessageBox(window, L"KeyDown", L"RootWindow", MB_OK);
            // NOTE: MessageBox pumps messages, but this subscription only
            // receives messages if it is suspended by 'for await', so any
            // WM_KEYDOWN arriving while the message box is up is not delivered.
            // the other subscriptions will receive messages.
        });
    }

    void OnMovesWhileLButtonDown() {

        auto moves_while_lbutton_down = messages<WM_LBUTTONDOWN>().
            map(
                [this](auto m) {
                    m.handled(); // skip DefWindowProc

                    return this->messages<WM_MOUSEMOVE>().
                        take_until(this->messages<WM_LBUTTONUP>());
                }).
            merge();

        moves_while_lbutton_down.
        subscribe([this](auto m) {
            m.handled(); // skip DefWindowProc

            position = MAKEPOINTS(m.lParam);
            InvalidateRect(window, nullptr, true);
        });
    }

    void OnPaint() {
        messages<WM_PAINT>(). 
        subscribe([this](auto m) {
            m.handled(); // skip DefWindowProc

            PAINTSTRUCT ps;
            BeginPaint(window, &ps);
            PaintContent(ps);
            EndPaint(window, &ps);
        });
    }

    void OnPrintClient() {
        messages<WM_PRINTCLIENT, HDC>().
        subscribe([this](auto m) {
            m.handled(); // skip DefWindowProc

            PAINTSTRUCT ps;
            ps.hdc = m.wParam;
            GetClientRect(window, &ps.rcPaint);
            PaintContent(ps);
        });
    }
};

int PASCAL
wWinMain(HINSTANCE hinst, HINSTANCE, LPWSTR, int nShowCmd)
{
    HRESULT hr = S_OK;

    hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        return FALSE;
    }
    ON_UNWIND_AUTO([&]{CoUninitialize();});

    InitCommonControls();

    RootWindow::window_class::Register();

    LONG winerror = ERROR_SUCCESS;

    std::wstring title{L"Scratch App - RootWindow"};

    // normal create window call, just takes the class name and optional create parameters
    HWND window = CreateWindow(
        RootWindow::window_class::Name(), title.c_str(),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL,
        hinst,
        &title);
    if (!window) {winerror = GetLastError();}

    if (!!winerror || !window)
    {
        return winerror;
    }

    ShowWindow(window, nShowCmd);

    // interact with window safely on the UI thread from another thread
    auto settitle = std::async([window](){

        // by static method (two SendMessage)
        RootWindow::set_title(window, L"SET_TITLE! " + RootWindow::get_title(window));

        // or multiple instance methods (one SendMessage)
        RootWindow::send_call(window, [](RootWindow& r){
            r.set_title(L"SEND_CALL! " + r.get_title());
            return 0;
        });
    });

    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    settitle.get();

    return 0;
}