mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
C++ test with an example of using the Windows API directly to create a slint platform
This commit is contained in:
parent
6803f05170
commit
f9e5559eeb
8 changed files with 430 additions and 0 deletions
|
@ -55,4 +55,5 @@ if(SLINT_FEATURE_EXPERIMENTAL)
|
|||
if(Qt6_FOUND)
|
||||
add_subdirectory(manual/platform_qt)
|
||||
endif(Qt6_FOUND)
|
||||
add_subdirectory(manual/platform_native)
|
||||
endif(SLINT_FEATURE_EXPERIMENTAL)
|
||||
|
|
9
api/cpp/tests/manual/platform_native/CMakeLists.txt
Normal file
9
api/cpp/tests/manual/platform_native/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
if (WIN32)
|
||||
add_executable(platform_native WIN32 main.cpp appview.cpp)
|
||||
target_link_libraries(platform_native PRIVATE Slint::Slint)
|
||||
slint_target_sources(platform_native appwindow.slint)
|
||||
endif(WIN32)
|
||||
|
7
api/cpp/tests/manual/platform_native/README.md
Normal file
7
api/cpp/tests/manual/platform_native/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
This shows how one can use the Slint C++ platform API to integrate into any Windows application
|
||||
|
||||
- main.cpp is basically a shell of an application written using the native WIN32 api.
|
||||
- appview.h is an interface that is used by the application to show a Slint Window.
|
||||
the implementation of this interface could even be in a plugin.
|
||||
- appview.cpp is the implementation of this interface and instantiate the UI made with Slint
|
||||
- windowadapter_win.h contains the glue code used to implement a Slint platform using native WIN32 API
|
43
api/cpp/tests/manual/platform_native/appview.cpp
Normal file
43
api/cpp/tests/manual/platform_native/appview.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
#ifndef UNICODE
|
||||
# define UNICODE
|
||||
#endif
|
||||
|
||||
#include "appwindow.h"
|
||||
#include <slint_platform.h>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
# include "windowadapter_win.h"
|
||||
#endif
|
||||
|
||||
namespace slint_platform = slint::experimental::platform;
|
||||
|
||||
struct MyPlatform : public slint_platform::Platform
|
||||
{
|
||||
mutable std::unique_ptr<MyWindowAdapter> the_window;
|
||||
std::unique_ptr<slint_platform::AbstractWindowAdapter> create_window_adapter() const override
|
||||
{
|
||||
return std::move(the_window);
|
||||
}
|
||||
};
|
||||
|
||||
AppView::AppView() { }
|
||||
|
||||
void AppView::setGeometry(int x, int y, int width, int height)
|
||||
{
|
||||
myWindow->setGeometry(x, y, width, height);
|
||||
}
|
||||
|
||||
void AppView::attachToWindow(WINDOW_HANDLE winId)
|
||||
{
|
||||
auto p = std::make_unique<MyPlatform>();
|
||||
p->the_window = std::make_unique<MyWindowAdapter>(winId);
|
||||
myWindow = p->the_window.get();
|
||||
slint_platform::Platform::register_platform(std::move(p));
|
||||
|
||||
// AppWindow is the auto-generated slint code
|
||||
static auto app = AppWindow::create();
|
||||
app->show();
|
||||
}
|
22
api/cpp/tests/manual/platform_native/appview.h
Normal file
22
api/cpp/tests/manual/platform_native/appview.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
# include <windows.h>
|
||||
typedef HWND WINDOW_HANDLE;
|
||||
#endif
|
||||
|
||||
struct MyWindowAdapter;
|
||||
|
||||
class AppView
|
||||
{
|
||||
MyWindowAdapter *myWindow = nullptr;
|
||||
|
||||
public:
|
||||
AppView();
|
||||
|
||||
void attachToWindow(WINDOW_HANDLE winId);
|
||||
void setGeometry(int x, int y, int width, int height);
|
||||
};
|
20
api/cpp/tests/manual/platform_native/appwindow.slint
Normal file
20
api/cpp/tests/manual/platform_native/appwindow.slint
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
import {Button, AboutSlint} from "std-widgets.slint";
|
||||
|
||||
export component AppWindow inherits Window {
|
||||
preferred-width: 600px;
|
||||
preferred-height: 300px;
|
||||
property <int> count;
|
||||
|
||||
VerticalLayout {
|
||||
AboutSlint { }
|
||||
Button {
|
||||
text: "Press me";
|
||||
clicked => { count += 1; }
|
||||
}
|
||||
Text { text: count; }
|
||||
Rectangle { }
|
||||
}
|
||||
}
|
92
api/cpp/tests/manual/platform_native/main.cpp
Normal file
92
api/cpp/tests/manual/platform_native/main.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
#include <windows.h>
|
||||
#include "appview.h"
|
||||
#include <memory>
|
||||
|
||||
#define THE_BUTTON_ID 101
|
||||
|
||||
static std::unique_ptr<AppView> app;
|
||||
|
||||
extern "C" static LRESULT WindowProc(HWND h, UINT msg, WPARAM wp, LPARAM lp)
|
||||
{
|
||||
switch (msg) {
|
||||
/* Add a win32 push button and do something when it's clicked. */
|
||||
case WM_CREATE: {
|
||||
HWND hbutton = CreateWindow(
|
||||
"BUTTON", "Hey There", /* class and title */
|
||||
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, /* style */
|
||||
0, 0, 100, 30, /* position */
|
||||
h, /* parent */
|
||||
(HMENU)THE_BUTTON_ID, /* unique (within the application) integer identifier */
|
||||
GetModuleHandle(0), 0 /* GetModuleHandle(0) gets the hinst */
|
||||
);
|
||||
app = std::make_unique<AppView>();
|
||||
app->attachToWindow(h);
|
||||
} break;
|
||||
|
||||
case WM_SIZE: {
|
||||
UINT width = LOWORD(lp);
|
||||
UINT height = HIWORD(lp);
|
||||
if (app)
|
||||
app->setGeometry(0, 40, width, height - 40);
|
||||
} break;
|
||||
|
||||
case WM_COMMAND: {
|
||||
switch (LOWORD(wp)) {
|
||||
case THE_BUTTON_ID:
|
||||
app = nullptr;
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
} break;
|
||||
|
||||
case WM_CLOSE:
|
||||
app = nullptr;
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(h, msg, wp, lp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int show)
|
||||
{
|
||||
if (!hprev) {
|
||||
WNDCLASS c = { 0 };
|
||||
c.lpfnWndProc = (WNDPROC)WindowProc;
|
||||
c.hInstance = hinst;
|
||||
c.hIcon = LoadIcon(0, IDI_APPLICATION);
|
||||
c.hCursor = LoadCursor(0, IDC_ARROW);
|
||||
c.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
|
||||
c.lpszClassName = "MainWindow";
|
||||
RegisterClass(&c);
|
||||
}
|
||||
|
||||
HWND h = CreateWindow("MainWindow", /* window class name*/
|
||||
"WindowTitle", /* title */
|
||||
WS_OVERLAPPEDWINDOW, /* style */
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, /* position */
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, /* size */
|
||||
0, /* parent */
|
||||
0, /* menu */
|
||||
hinst, 0 /* lparam */
|
||||
);
|
||||
|
||||
ShowWindow(h, show);
|
||||
|
||||
while (1) { /* or while(running) */
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT)
|
||||
return (int)msg.wParam;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
236
api/cpp/tests/manual/platform_native/windowadapter_win.h
Normal file
236
api/cpp/tests/manual/platform_native/windowadapter_win.h
Normal file
|
@ -0,0 +1,236 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <slint_platform.h>
|
||||
#include "appview.h"
|
||||
#include <cassert>
|
||||
#include <windows.h>
|
||||
|
||||
namespace slint_platform = slint::experimental::platform;
|
||||
|
||||
struct Geometry
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
struct MyWindowAdapter : public slint_platform::WindowAdapter<slint_platform::SkiaRenderer>
|
||||
{
|
||||
HWND parentWindow;
|
||||
HINSTANCE hInstance = GetModuleHandleW(nullptr);
|
||||
mutable std::optional<HWND> hwnd;
|
||||
Geometry geometry = { 0, 0, 600, 300 };
|
||||
|
||||
MyWindowAdapter(HWND winId) : parentWindow(winId) { }
|
||||
|
||||
slint::PhysicalSize windowSize() const
|
||||
{
|
||||
RECT r;
|
||||
GetWindowRect(*hwnd, &r);
|
||||
return slint::PhysicalSize({ uint32_t(r.right - r.left), uint32_t(r.bottom - r.top) });
|
||||
}
|
||||
|
||||
void show() const override
|
||||
{
|
||||
if (hwnd)
|
||||
return;
|
||||
// Register the window class.
|
||||
const wchar_t CLASS_NAME[] = L"Sample Window Class";
|
||||
|
||||
WNDCLASS wc = {};
|
||||
|
||||
wc.lpfnWndProc = MyWindowAdapter::windowProc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpszClassName = CLASS_NAME;
|
||||
|
||||
RegisterClass(&wc);
|
||||
|
||||
// Create the window.
|
||||
|
||||
hwnd = CreateWindowEx(0, // Optional window styles.
|
||||
CLASS_NAME, // Window class
|
||||
L"Learn to Program Windows", // Window text
|
||||
WS_CHILDWINDOW, // Window style
|
||||
|
||||
// Size and position
|
||||
geometry.x, geometry.y, geometry.width, geometry.height,
|
||||
|
||||
parentWindow,
|
||||
NULL, // Menu
|
||||
hInstance, // Instance handle
|
||||
NULL // Additional application data
|
||||
);
|
||||
|
||||
SetWindowLongPtr(*hwnd, GWLP_USERDATA, (LONG_PTR)this);
|
||||
|
||||
ShowWindow(*hwnd, SW_SHOWNORMAL);
|
||||
renderer().show(*hwnd, hInstance, slint::PhysicalSize({ geometry.width, geometry.height }));
|
||||
}
|
||||
|
||||
void hide() const override
|
||||
{
|
||||
// TODO: destroy window
|
||||
renderer().hide();
|
||||
}
|
||||
|
||||
void request_redraw() const override
|
||||
{
|
||||
if (!hwnd)
|
||||
return;
|
||||
InvalidateRect(*hwnd, nullptr, false);
|
||||
}
|
||||
|
||||
void render()
|
||||
{
|
||||
renderer().render(windowSize());
|
||||
if (has_active_animations())
|
||||
request_redraw();
|
||||
}
|
||||
|
||||
void resize(uint32_t width, uint32_t height)
|
||||
{
|
||||
slint::PhysicalSize windowSize({ width, height });
|
||||
renderer().resize(windowSize);
|
||||
window().set_size(windowSize);
|
||||
}
|
||||
|
||||
void setGeometry(int x, int y, int width, int height)
|
||||
{
|
||||
if (!hwnd) {
|
||||
geometry = { x, y, uint32_t(width), uint32_t(height) };
|
||||
return;
|
||||
}
|
||||
SetWindowPos(*hwnd, nullptr, x, y, width, height, 0);
|
||||
}
|
||||
|
||||
std::optional<slint::cbindgen_private::MouseEvent> mouseEventForMessage(UINT uMsg,
|
||||
LPARAM lParam)
|
||||
{
|
||||
float x = float(LOWORD(lParam));
|
||||
float y = float(HIWORD(lParam));
|
||||
|
||||
auto makePressEvent = [=](UINT uMsg) {
|
||||
slint::cbindgen_private::PointerEventButton button;
|
||||
switch (uMsg) {
|
||||
case WM_LBUTTONDOWN:
|
||||
button = slint::cbindgen_private::PointerEventButton::Left;
|
||||
break;
|
||||
case WM_MBUTTONDOWN:
|
||||
button = slint::cbindgen_private::PointerEventButton::Middle;
|
||||
break;
|
||||
case WM_RBUTTONDOWN:
|
||||
button = slint::cbindgen_private::PointerEventButton::Right;
|
||||
break;
|
||||
default:
|
||||
assert(!"not implemented");
|
||||
}
|
||||
return slint::cbindgen_private::MouseEvent {
|
||||
.tag = slint::cbindgen_private::MouseEvent::Tag::Pressed,
|
||||
.pressed =
|
||||
slint::cbindgen_private::MouseEvent::Pressed_Body {
|
||||
.position = { x, y },
|
||||
.button = button,
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
auto makeReleaseEvent = [=](UINT uMsg) {
|
||||
slint::cbindgen_private::PointerEventButton button;
|
||||
switch (uMsg) {
|
||||
case WM_LBUTTONUP:
|
||||
button = slint::cbindgen_private::PointerEventButton::Left;
|
||||
break;
|
||||
case WM_MBUTTONUP:
|
||||
button = slint::cbindgen_private::PointerEventButton::Middle;
|
||||
break;
|
||||
case WM_RBUTTONUP:
|
||||
button = slint::cbindgen_private::PointerEventButton::Right;
|
||||
break;
|
||||
default:
|
||||
assert(!"not implemented");
|
||||
}
|
||||
return slint::cbindgen_private::MouseEvent {
|
||||
.tag = slint::cbindgen_private::MouseEvent::Tag::Released,
|
||||
.released =
|
||||
slint::cbindgen_private::MouseEvent::Released_Body {
|
||||
.position = { x, y },
|
||||
.button = button,
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
switch (uMsg) {
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
return makeReleaseEvent(uMsg);
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
return makePressEvent(uMsg);
|
||||
case WM_MOUSEMOVE:
|
||||
return slint::cbindgen_private::MouseEvent {
|
||||
.tag = slint::cbindgen_private::MouseEvent::Tag::Moved,
|
||||
.moved =
|
||||
slint::cbindgen_private::MouseEvent::Moved_Body {
|
||||
.position = { x, y },
|
||||
}
|
||||
};
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void mouse_event(UINT uMsg, LPARAM lParam)
|
||||
{
|
||||
if (auto event = mouseEventForMessage(uMsg, lParam)) {
|
||||
dispatch_pointer_event(*event);
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
MyWindowAdapter *self =
|
||||
reinterpret_cast<MyWindowAdapter *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||
if (self == nullptr) {
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
switch (uMsg) {
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
|
||||
case WM_PAINT: {
|
||||
PAINTSTRUCT ps;
|
||||
BeginPaint(hwnd, &ps);
|
||||
slint_platform::update_timers_and_animations();
|
||||
self->render();
|
||||
EndPaint(hwnd, &ps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_SIZE:
|
||||
self->resize(LOWORD(lParam), HIWORD(lParam));
|
||||
return 0;
|
||||
|
||||
case WM_LBUTTONUP:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MOUSEMOVE:
|
||||
slint_platform::update_timers_and_animations();
|
||||
self->mouse_event(uMsg, lParam);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue