Skip to content

Commit 6385b8a

Browse files
authored
refactor(ui/window): refactor appkit internals (#42)
Signed-off-by: Tony Gorez <[email protected]>
1 parent a8a5c87 commit 6385b8a

File tree

3 files changed

+142
-115
lines changed

3 files changed

+142
-115
lines changed

src/ui/window/appkit/window_appkit.mm

+67-64
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,81 @@
22

33
#import <AppKit/AppKit.h> // NSWindowController
44

5-
@interface WindowController : NSWindowController
6-
- (id)initWithNewWindow;
7-
@end
8-
9-
@implementation WindowController
10-
- (id)initWithNewWindow {
11-
// The style of the window, which determines different aspects of the
12-
// window's appearance and behavior. For more information, see
13-
// https://developer.apple.com/documentation/appkit/nswindowstylemask?language=objc
14-
NSWindowStyleMask windowStyleMask =
15-
NSWindowStyleMaskTitled | NSWindowStyleMaskResizable |
16-
NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
17-
18-
NSWindow *window = [[NSWindow alloc]
19-
initWithContentRect:NSMakeRect(0, 0, 0, 0)
20-
styleMask:windowStyleMask
21-
// You should use this mode.
22-
// It supports hardware acceleration, Quartz drawing,
23-
// and takes advantage of the GPU when possible.
24-
backing:NSBackingStoreBuffered
25-
// When defer is set to YES, the window server postpones the
26-
// creation of the window device (the low-level resources
27-
// for rendering) until the window is moved onscreen. This
28-
// can optimize performance by delaying the allocation of
29-
// graphics resources until they are needed.
30-
defer:YES];
31-
32-
// By default, we don't want the title to be visible
33-
[window setTitleVisibility:NSWindowTitleHidden];
34-
35-
// By default, we don't want the title bar to be transparent
36-
// as we hide the title. It lets the window's content extend to the
37-
// top of the window.
38-
[window setTitlebarAppearsTransparent:YES];
39-
40-
[window.contentView setAutoresizesSubviews:YES];
41-
42-
return [super initWithWindow:window];
43-
}
44-
@end
45-
465
namespace sourcemeta::native {
47-
Window::Window() {
48-
internal_ =
49-
static_cast<void *>([[WindowController alloc] initWithNewWindow].window);
50-
}
516

52-
Window::~Window() {
53-
NSWindow *window = static_cast<NSWindow *>(internal_);
54-
[[window windowController] close];
7+
class Window::Internal {
8+
public:
9+
Internal() {
10+
// The style of the window, which determines different aspects of the
11+
// window's appearance and behavior. For more information, see
12+
// https://developer.apple.com/documentation/appkit/nswindowstylemask?language=objc
13+
NSWindowStyleMask windowStyleMask =
14+
NSWindowStyleMaskTitled | NSWindowStyleMaskResizable |
15+
NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
16+
17+
NSWindow *window = [[NSWindow alloc]
18+
initWithContentRect:NSMakeRect(0, 0, 0, 0)
19+
styleMask:windowStyleMask
20+
// You should use this mode.
21+
// It supports hardware acceleration, Quartz drawing,
22+
// and takes advantage of the GPU when possible.
23+
backing:NSBackingStoreBuffered
24+
// When defer is set to YES, the window server postpones
25+
// the creation of the window device (the low-level
26+
// resources for rendering) until the window is moved
27+
// onscreen. This can optimize performance by delaying the
28+
// allocation of graphics resources until they are needed.
29+
defer:YES];
30+
31+
// By default, we don't want the title to be visible
32+
[window setTitleVisibility:NSWindowTitleHidden];
33+
34+
// By default, we don't want the title bar to be transparent
35+
// as we hide the title. It lets the window's content extend to the
36+
// top of the window.
37+
[window setTitlebarAppearsTransparent:YES];
38+
39+
[window.contentView setAutoresizesSubviews:YES];
40+
41+
window_ = window;
42+
}
43+
44+
~Internal() { [[window_ windowController] close]; }
5545

56-
if (internal_) {
57-
CFBridgingRelease(internal_);
46+
auto show() -> void {
47+
NSWindowController *controller = [window_ windowController];
48+
[controller showWindow:nil];
49+
[window_ makeKeyAndOrderFront:controller];
5850
}
59-
}
6051

61-
auto Window::size(const unsigned int width, const unsigned int height) -> void {
62-
NSWindow *window = static_cast<NSWindow *>(internal_);
63-
NSRect frame = [window frame];
52+
auto size(const unsigned int width, const unsigned int height) -> void {
53+
NSRect frame = [window_ frame];
6454

65-
frame.size.width = width;
66-
frame.size.height = height;
55+
frame.size.width = width;
56+
frame.size.height = height;
6757

68-
[window setFrame:frame display:YES animate:YES];
69-
}
58+
[window_ setFrame:frame display:YES animate:YES];
59+
}
7060

71-
auto Window::show() -> void {
72-
NSWindow *window = static_cast<NSWindow *>(internal_);
73-
WindowController *controller = [window windowController];
74-
[controller showWindow:nil];
75-
[window makeKeyAndOrderFront:controller];
61+
auto handle() -> void * { return window_; }
62+
63+
private:
64+
NSWindow *window_;
65+
};
66+
67+
Window::Window() { internal_ = new Window::Internal(); }
68+
69+
Window::~Window() { delete internal_; }
70+
71+
auto Window::size(const unsigned int width, const unsigned int height) -> void {
72+
if (width == 0 || height == 0) {
73+
return;
74+
}
75+
76+
internal_->size(width, height);
7677
}
7778

78-
auto Window::handle() -> void * { return internal_; }
79+
auto Window::show() -> void { internal_->show(); }
80+
81+
auto Window::handle() -> void * { return internal_->handle(); }
7982
} // namespace sourcemeta::native

src/ui/window/include/sourcemeta/native/window.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ class Window {
1818
template <typename T> auto add(T &child) -> void { add_(child); }
1919

2020
private:
21-
using Internal = void *;
22-
Internal internal_;
21+
class Internal;
22+
Internal *internal_;
2323

2424
// TODO(tonygo): Hide this implementation detail
2525
template <typename T> void add_(T &child) {

src/ui/window/win32/window_win32.cc

+73-49
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,35 @@
1-
#include <Windows.h>
21
#include <sourcemeta/native/window.h>
32

3+
#include <Windows.h>
4+
45
#include <functional>
56
#include <iostream>
67
#include <vector>
78

89
namespace sourcemeta::native {
910

10-
struct WindowInternal {
11-
HWND hwnd;
12-
std::vector<std::function<void(void)>> resize_callbacks;
11+
class Window::Internal {
12+
public:
13+
Internal() {
14+
WNDCLASS wc = {};
15+
wc.lpfnWndProc = &Internal::WindowProc;
16+
wc.hInstance = GetModuleHandle(NULL);
17+
wc.lpszClassName = this->class_name_;
18+
RegisterClass(&wc);
1319

20+
// TODO: Make this configurable
21+
this->title_ = "Native Window";
22+
}
23+
24+
// The `CALLBACK` macro is a Windows-specific calling convention that
25+
// guarantees that the stack is cleaned up properly.
1426
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
1527
LPARAM lParam) {
1628
if (uMsg == WM_NCCREATE) {
1729
// Windows gives us back our internal pointer that we passed to
1830
// CreateWindowEx
1931
auto *cs = reinterpret_cast<CREATESTRUCT *>(lParam);
20-
auto internal = static_cast<WindowInternal *>(cs->lpCreateParams);
32+
auto internal = static_cast<Internal *>(cs->lpCreateParams);
2133

2234
// We store it with the window so we can get it back later
2335
SetWindowLongPtr(hwnd, GWLP_USERDATA,
@@ -26,10 +38,10 @@ struct WindowInternal {
2638

2739
switch (uMsg) {
2840
case WM_SIZE: {
29-
auto internal = reinterpret_cast<WindowInternal *>(
30-
GetWindowLongPtr(hwnd, GWLP_USERDATA));
41+
auto internal =
42+
reinterpret_cast<Internal *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
3143
if (internal) {
32-
for (auto &callback : internal->resize_callbacks) {
44+
for (auto &callback : internal->get_resize_callbacks()) {
3345
callback();
3446
}
3547
}
@@ -44,62 +56,74 @@ struct WindowInternal {
4456
}
4557
return DefWindowProc(hwnd, uMsg, wParam, lParam);
4658
}
59+
60+
auto size(const unsigned int width, const unsigned int height) -> void {
61+
if (this->hwnd_) {
62+
SetWindowPos(static_cast<HWND>(this->hwnd_), NULL, 0,
63+
0, // Ignore position
64+
width, height, SWP_NOMOVE | SWP_NOZORDER);
65+
}
66+
}
67+
68+
auto create_window() -> void {
69+
// Create the window
70+
this->hwnd_ =
71+
CreateWindowEx(0, this->class_name_, this->title_, WS_OVERLAPPEDWINDOW,
72+
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
73+
CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), this);
74+
if (!this->hwnd_) {
75+
std::cerr << "Failed to create window. Error: " << GetLastError()
76+
<< std::endl;
77+
return;
78+
}
79+
}
80+
81+
auto show() -> void {
82+
if (!this->hwnd_) {
83+
this->create_window();
84+
}
85+
if (this->hwnd_) {
86+
ShowWindow(this->hwnd_, SW_SHOW);
87+
}
88+
}
89+
90+
auto handle() -> void * { return &this->hwnd_; }
91+
92+
auto add_resize_callback(std::function<void(void)> callback) -> void {
93+
this->resize_callbacks_.push_back(callback);
94+
}
95+
96+
auto get_resize_callbacks() -> std::vector<std::function<void(void)>> {
97+
return this->resize_callbacks_;
98+
}
99+
100+
private:
101+
HWND hwnd_;
102+
std::vector<std::function<void(void)>> resize_callbacks_;
103+
const char *class_name_ = "NativeWin32WindowClass";
104+
const char *title_;
47105
};
48106

49-
Window::Window() : internal_(new WindowInternal{}) {
50-
const char CLASS_NAME[] = "NativeWin32WindowClass";
51-
WNDCLASS wc = {};
52-
wc.lpfnWndProc = WindowInternal::WindowProc;
53-
wc.hInstance = GetModuleHandle(NULL);
54-
wc.lpszClassName = CLASS_NAME;
55-
RegisterClass(&wc);
56-
}
107+
Window::Window() { internal_ = new Internal(); }
57108

58109
Window::~Window() {
59110
if (internal_) {
60-
auto internal = static_cast<WindowInternal *>(internal_);
61-
std::cout << "Window::~Window(): delete internal" << std::endl;
62-
delete internal;
111+
delete internal_;
63112
}
64113
}
65114

66115
auto Window::size(const unsigned int width, const unsigned int height) -> void {
67-
auto internal = static_cast<WindowInternal *>(internal_);
68-
if (internal->hwnd) {
69-
SetWindowPos(static_cast<HWND>(internal->hwnd), NULL, 0,
70-
0, // Ignore position
71-
width, height, SWP_NOMOVE | SWP_NOZORDER);
72-
}
116+
internal_->size(width, height);
73117
}
74118

75119
auto Window::show() -> void {
76-
const char CLASS_NAME[] = "NativeWin32WindowClass";
77-
auto internal = static_cast<WindowInternal *>(internal_);
78-
// TODO(tony): add parameter for title
79-
HWND hwnd =
80-
CreateWindowEx(0, CLASS_NAME, "Native Window", WS_OVERLAPPEDWINDOW,
81-
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
82-
NULL, // Parent window
83-
NULL, // Menu
84-
GetModuleHandle(NULL), internal);
85-
86-
if (!hwnd) {
87-
std::cerr << "Failed to create window. Error: " << GetLastError()
88-
<< std::endl;
89-
return;
90-
}
91-
92-
internal->hwnd = hwnd;
93-
ShowWindow(hwnd, SW_SHOW);
120+
internal_->create_window();
121+
internal_->show();
94122
}
95123

96-
auto Window::handle() -> void * {
97-
auto internal = static_cast<WindowInternal *>(internal_);
98-
return &internal->hwnd;
99-
}
124+
auto Window::handle() -> void * { return internal_->handle(); }
100125

101126
auto Window::on_resize(std::function<void(void)> callback) -> void {
102-
auto internal = static_cast<WindowInternal *>(internal_);
103-
internal->resize_callbacks.push_back(callback);
127+
internal_->add_resize_callback(callback);
104128
}
105129
} // namespace sourcemeta::native

0 commit comments

Comments
 (0)