245 lines
6.1 KiB
C++
245 lines
6.1 KiB
C++
|
|
||
|
// 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;
|
||
|
}
|