Implementations of the Action-Model-View pattern:
- with PDCurses
- with Win32
Implementations of the Action-Model-View pattern:
| #include <curses.h> | |
| #define curses | |
| #include <memory> | |
| struct action | |
| { | |
| int key; | |
| }; | |
| struct application | |
| { | |
| int y = 0; | |
| int x = 0; | |
| }; | |
| std::unique_ptr<application> update(application app, action a) | |
| { | |
| switch (a.key) | |
| { | |
| case 'q': | |
| return nullptr; | |
| case KEY_UP: | |
| app.y--; | |
| break; | |
| case KEY_DOWN: | |
| app.y++; | |
| break; | |
| case KEY_LEFT: | |
| app.x--; | |
| break; | |
| case KEY_RIGHT: | |
| app.x++; | |
| break; | |
| } | |
| return std::make_unique<application>(std::move(app)); | |
| } | |
| void draw(WINDOW* window, const application& app) | |
| { | |
| curses::wmove(window, app.y, app.x); | |
| } | |
| int main() | |
| { | |
| curses::initscr(); | |
| curses::curs_set(2); | |
| curses::noecho(); | |
| curses::keypad(curses::stdscr, TRUE); | |
| auto state = application{}; | |
| while (auto new_state = update(state, {curses::wgetch(curses::stdscr)})) | |
| { | |
| state = *new_state; | |
| draw(curses::stdscr, state); | |
| } | |
| curses::curs_set(1); | |
| curses::endwin(); | |
| return 0; | |
| } |
| #include <windows.h> | |
| #include <new> | |
| #include <tuple> | |
| struct action | |
| { | |
| HWND hWnd; | |
| UINT message; | |
| WPARAM wParam; | |
| LPARAM lParam; | |
| }; | |
| struct application | |
| { | |
| int y = 0; | |
| int x = 0; | |
| }; | |
| bool operator==(const application& lhs, const application& rhs) | |
| { | |
| return std::tie(lhs.x, lhs.y) == std::tie(rhs.x, rhs.y); | |
| } | |
| bool operator!=(const application& lhs, const application& rhs) | |
| { | |
| return !(lhs == rhs); | |
| } | |
| application update(application app, action a) | |
| { | |
| switch (a.message) | |
| { | |
| case WM_KEYDOWN: | |
| switch (a.wParam) | |
| { | |
| case VK_UP: | |
| app.y -= 10; | |
| break; | |
| case VK_DOWN: | |
| app.y += 10; | |
| break; | |
| case VK_LEFT: | |
| app.x -= 10; | |
| break; | |
| case VK_RIGHT: | |
| app.x += 10; | |
| break; | |
| } | |
| break; | |
| } | |
| return app; | |
| } | |
| void draw(HWND hWnd, const application& app) | |
| { | |
| PAINTSTRUCT ps; | |
| HDC hdc = ::BeginPaint(hWnd, &ps); | |
| ::TextOut(hdc, app.x, app.y, "X", 1); | |
| ::EndPaint(hWnd, &ps); | |
| } | |
| LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | |
| { | |
| auto pState = reinterpret_cast<application*>(::GetWindowLongPtr(hWnd, 0)); | |
| switch (message) | |
| { | |
| case WM_PAINT: | |
| if (!pState) | |
| { | |
| return FALSE; | |
| } | |
| draw(hWnd, *pState); | |
| return 0; | |
| case WM_DESTROY: | |
| ::PostQuitMessage(0); | |
| return 0; | |
| } | |
| if (pState) | |
| { | |
| auto newState = update(*pState, {hWnd, message, wParam, lParam}); | |
| if (*pState != newState) | |
| { | |
| ::InvalidateRect(hWnd, nullptr, TRUE); // trigger re-paint | |
| } | |
| *pState = newState; | |
| } | |
| return ::DefWindowProc(hWnd, message, wParam, lParam); | |
| } | |
| int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) | |
| { | |
| WNDCLASSEX wcex{}; | |
| wcex.cbSize = sizeof(WNDCLASSEX); | |
| wcex.style = CS_HREDRAW | CS_VREDRAW; | |
| wcex.lpfnWndProc = WndProc; | |
| wcex.cbWndExtra = sizeof(application*); | |
| wcex.hInstance = hInstance; | |
| wcex.hCursor = ::LoadCursor(nullptr, IDC_ARROW); | |
| wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); | |
| wcex.lpszClassName = "AMOV"; | |
| if (!::RegisterClassEx(&wcex)) | |
| { | |
| ::MessageBox(nullptr, "Call to RegisterClassEx failed!", nullptr, 0); | |
| return 1; | |
| } | |
| HWND hWnd = | |
| ::CreateWindow("AMOV", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 250, | |
| 250, nullptr, nullptr, hInstance, nullptr); | |
| if (!hWnd) | |
| { | |
| ::MessageBox(nullptr, "Call to CreateWindow failed!", nullptr, 0); | |
| return 1; | |
| } | |
| application state; | |
| ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(&state)); | |
| ::ShowWindow(hWnd, nCmdShow); | |
| ::UpdateWindow(hWnd); | |
| MSG msg; | |
| while (::GetMessage(&msg, nullptr, 0, 0) > 0) | |
| { | |
| ::TranslateMessage(&msg); | |
| ::DispatchMessage(&msg); | |
| } | |
| return (int)msg.wParam; | |
| } |