Skip to content

Commit 679761a

Browse files
committed
Add content bounds API for window management
Introduces SetContentBounds and GetContentBounds methods to the Window class and platform implementations, allowing direct manipulation and querying of the window's content area (excluding decorations). Updates C API and headers to expose these functions for FFI usage. Refactors macOS menu positioning to use screen coordinates and simplifies coordinate conversion logic.
1 parent 10848a2 commit 679761a

File tree

11 files changed

+171
-47
lines changed

11 files changed

+171
-47
lines changed

src/capi/window_c.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,30 @@ native_size_t native_window_get_content_size(native_window_t window) {
291291
return result;
292292
}
293293

294+
FFI_PLUGIN_EXPORT
295+
void native_window_set_content_bounds(native_window_t window, native_rectangle_t bounds) {
296+
if (!window)
297+
return;
298+
auto* win = static_cast<nativeapi::Window*>(window);
299+
nativeapi::Rectangle rect = {bounds.x, bounds.y, bounds.width, bounds.height};
300+
win->SetContentBounds(rect);
301+
}
302+
303+
FFI_PLUGIN_EXPORT
304+
native_rectangle_t native_window_get_content_bounds(native_window_t window) {
305+
native_rectangle_t result = {0.0, 0.0, 0.0, 0.0};
306+
if (!window)
307+
return result;
308+
309+
auto* win = static_cast<nativeapi::Window*>(window);
310+
nativeapi::Rectangle bounds = win->GetContentBounds();
311+
result.x = bounds.x;
312+
result.y = bounds.y;
313+
result.width = bounds.width;
314+
result.height = bounds.height;
315+
return result;
316+
}
317+
294318
FFI_PLUGIN_EXPORT
295319
void native_window_set_minimum_size(native_window_t window, double width, double height) {
296320
if (!window)

src/capi/window_c.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ void native_window_set_content_size(native_window_t window, double width, double
139139
FFI_PLUGIN_EXPORT
140140
native_size_t native_window_get_content_size(native_window_t window);
141141

142+
FFI_PLUGIN_EXPORT
143+
void native_window_set_content_bounds(native_window_t window, native_rectangle_t bounds);
144+
145+
FFI_PLUGIN_EXPORT
146+
native_rectangle_t native_window_get_content_bounds(native_window_t window);
147+
142148
FFI_PLUGIN_EXPORT
143149
void native_window_set_minimum_size(native_window_t window, double width, double height);
144150

src/platform/android/window_android.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@ Size Window::GetContentSize() const {
188188
return GetSize();
189189
}
190190

191+
void Window::SetContentBounds(Rectangle bounds) {
192+
// On Android, content bounds is the same as window bounds
193+
SetBounds(bounds);
194+
}
195+
196+
Rectangle Window::GetContentBounds() const {
197+
// On Android, content bounds is the same as window bounds
198+
return GetBounds();
199+
}
200+
191201
void Window::SetMinimumSize(Size size) {
192202
ALOGW("SetMinimumSize not fully supported on Android");
193203
}

src/platform/ios/window_ios.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@
188188
return GetSize();
189189
}
190190

191+
void Window::SetContentBounds(Rectangle bounds) {
192+
// On iOS, content bounds is the same as window bounds
193+
SetBounds(bounds);
194+
}
195+
196+
Rectangle Window::GetContentBounds() const {
197+
// On iOS, content bounds is the same as window bounds
198+
return GetBounds();
199+
}
200+
191201
void Window::SetMinimumSize(Size size) {
192202
// Not applicable to iOS windows
193203
}

src/platform/linux/window_linux.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,16 @@ Size Window::GetContentSize() const {
201201
return GetSize();
202202
}
203203

204+
void Window::SetContentBounds(Rectangle bounds) {
205+
// For GDK windows, content bounds is the same as window bounds
206+
SetBounds(bounds);
207+
}
208+
209+
Rectangle Window::GetContentBounds() const {
210+
// For GDK windows, content bounds is the same as window bounds
211+
return GetBounds();
212+
}
213+
204214
void Window::SetMinimumSize(Size size) {
205215
// GTK minimum size constraints would need to be set on the widget level
206216
// For now, we'll provide a basic implementation that doesn't enforce

src/platform/macos/display_manager_macos.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ static Display CreateDisplayFromNSScreen(NSScreen* screen, bool isPrimary) {
8181

8282
Point DisplayManager::GetCursorPosition() {
8383
NSPoint mouseLocation = [NSEvent mouseLocation];
84-
84+
8585
// Convert from bottom-left (macOS default) to top-left coordinate system
8686
CGPoint topLeftPoint = NSPointExt::topLeft(mouseLocation);
87-
87+
8888
Point point;
8989
point.x = topLeftPoint.x;
9090
point.y = topLeftPoint.y;

src/platform/macos/menu_macos.mm

Lines changed: 11 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "../../image.h"
77
#include "../../menu.h"
88
#include "../../menu_event.h"
9+
#include "coordinate_utils_macos.h"
910

1011
// Import Cocoa headers
1112
#import <Cocoa/Cocoa.h>
@@ -666,7 +667,7 @@ - (void)menuDidClose:(NSMenu*)menu {
666667
break;
667668

668669
case PositioningStrategy::Type::CursorPosition: {
669-
NSPoint mouse_location = [NSEvent mouseLocation];
670+
NSPoint mouse_location = NSPointExt::topLeft([NSEvent mouseLocation]);
670671
x = mouse_location.x;
671672
y = mouse_location.y;
672673
break;
@@ -756,53 +757,18 @@ - (void)menuDidClose:(NSMenu*)menu {
756757
break;
757758
}
758759

759-
// Get the main window
760-
NSWindow* main_window = [[NSApplication sharedApplication] mainWindow];
761-
if (!main_window) {
762-
// Fallback to key window if main window is not available
763-
main_window = [[NSApplication sharedApplication] keyWindow];
764-
}
765-
766-
if (!main_window) {
767-
// If still no window, use the old implementation
768-
NSPoint point = NSMakePoint(x, y);
769-
NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeRightMouseDown
770-
location:point
771-
modifierFlags:0
772-
timestamp:0
773-
windowNumber:0
774-
context:nil
775-
eventNumber:0
776-
clickCount:1
777-
pressure:1.0];
778-
779-
@autoreleasepool {
780-
NSView* dummy_view = [[NSView alloc] init];
781-
[NSMenu popUpContextMenu:pimpl_->ns_menu_ withEvent:event forView:dummy_view];
782-
}
783-
784-
return true;
785-
}
786-
787-
NSView* content_view = [main_window contentView];
788-
if (!content_view) {
789-
return false;
790-
}
791-
792-
// Convert coordinates if the content view is not flipped
793-
// In macOS, the default coordinate system has origin at bottom-left
794-
// If view is not flipped, we need to convert from top-left origin
795-
CGFloat final_y = y;
796-
if (![content_view isFlipped]) {
797-
CGFloat frame_height = [content_view frame].size.height;
798-
final_y = frame_height - y;
799-
}
800-
801-
NSPoint point = NSMakePoint(x, final_y);
760+
// Convert coordinates from top-left origin to macOS screen coordinates (bottom-left origin)
761+
// macOS screen coordinates: origin at bottom-left, y grows upward
762+
// Our coordinates: origin at top-left, y grows downward
763+
CGPoint top_left_point = CGPointMake(x, y);
764+
NSPoint point = NSPointExt::bottomLeft(top_left_point);
802765

803766
// Use dispatch to ensure menu popup happens on the main run loop
767+
// Show the menu using screen coordinates (inView:nil)
804768
dispatch_async(dispatch_get_main_queue(), ^{
805-
[pimpl_->ns_menu_ popUpMenuPositioningItem:nil atLocation:point inView:content_view];
769+
@autoreleasepool {
770+
[pimpl_->ns_menu_ popUpMenuPositioningItem:nil atLocation:point inView:nil];
771+
}
806772
});
807773

808774
return true;

src/platform/macos/window_macos.mm

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,25 @@
151151
return size;
152152
}
153153

154+
void Window::SetContentBounds(Rectangle bounds) {
155+
// Convert from topLeft coordinate system to bottom-left (macOS default)
156+
NSRect topLeftRect = NSMakeRect(bounds.x, bounds.y, bounds.width, bounds.height);
157+
NSRect contentRect = NSRectExt::bottomLeft(topLeftRect);
158+
159+
// Set the content view frame
160+
NSRect frameRect = [pimpl_->ns_window_ frameRectForContentRect:contentRect];
161+
[pimpl_->ns_window_ setFrame:frameRect display:YES];
162+
}
163+
164+
Rectangle Window::GetContentBounds() const {
165+
NSRect contentRect = [pimpl_->ns_window_ contentRectForFrameRect:[pimpl_->ns_window_ frame]];
166+
// Convert from bottom-left (macOS default) to top-left coordinate system
167+
CGPoint topLeft = NSRectExt::topLeft(contentRect);
168+
Rectangle bounds = {topLeft.x, topLeft.y, static_cast<double>(contentRect.size.width),
169+
static_cast<double>(contentRect.size.height)};
170+
return bounds;
171+
}
172+
154173
void Window::SetMinimumSize(Size size) {
155174
[pimpl_->ns_window_ setMinSize:NSMakeSize(size.width, size.height)];
156175
}

src/platform/ohos/window_ohos.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,16 @@ Size Window::GetContentSize() const {
170170
return GetSize();
171171
}
172172

173+
void Window::SetContentBounds(Rectangle bounds) {
174+
// On OpenHarmony, content bounds is the same as window bounds
175+
SetBounds(bounds);
176+
}
177+
178+
Rectangle Window::GetContentBounds() const {
179+
// On OpenHarmony, content bounds is the same as window bounds
180+
return GetBounds();
181+
}
182+
173183
void Window::SetMinimumSize(Size size) {
174184
// SetMinimumSize not fully supported on OpenHarmony
175185
}

src/platform/windows/window_windows.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,54 @@ Size Window::GetContentSize() const {
224224
return size;
225225
}
226226

227+
void Window::SetContentBounds(Rectangle bounds) {
228+
if (pimpl_->hwnd_) {
229+
RECT windowRect, clientRect;
230+
GetWindowRect(pimpl_->hwnd_, &windowRect);
231+
GetClientRect(pimpl_->hwnd_, &clientRect);
232+
233+
// Calculate the difference between window and client area
234+
int borderWidth = (windowRect.right - windowRect.left) - clientRect.right;
235+
int borderHeight = (windowRect.bottom - windowRect.top) - clientRect.bottom;
236+
237+
// Get current client area position in screen coordinates
238+
POINT clientTopLeft = {0, 0};
239+
ClientToScreen(pimpl_->hwnd_, &clientTopLeft);
240+
241+
// Calculate the offset from window top-left to client top-left
242+
int offsetX = clientTopLeft.x - windowRect.left;
243+
int offsetY = clientTopLeft.y - windowRect.top;
244+
245+
// Calculate window position so that client area is at bounds position
246+
int windowX = static_cast<int>(bounds.x) - offsetX;
247+
int windowY = static_cast<int>(bounds.y) - offsetY;
248+
int windowWidth = static_cast<int>(bounds.width) + borderWidth;
249+
int windowHeight = static_cast<int>(bounds.height) + borderHeight;
250+
251+
SetWindowPos(pimpl_->hwnd_, nullptr, windowX, windowY, windowWidth, windowHeight, SWP_NOZORDER);
252+
}
253+
}
254+
255+
Rectangle Window::GetContentBounds() const {
256+
Rectangle bounds = {0, 0, 0, 0};
257+
if (pimpl_->hwnd_) {
258+
RECT clientRect;
259+
GetClientRect(pimpl_->hwnd_, &clientRect);
260+
261+
// Convert client rect to screen coordinates
262+
POINT topLeft = {clientRect.left, clientRect.top};
263+
POINT bottomRight = {clientRect.right, clientRect.bottom};
264+
ClientToScreen(pimpl_->hwnd_, &topLeft);
265+
ClientToScreen(pimpl_->hwnd_, &bottomRight);
266+
267+
bounds.x = topLeft.x;
268+
bounds.y = topLeft.y;
269+
bounds.width = bottomRight.x - topLeft.x;
270+
bounds.height = bottomRight.y - topLeft.y;
271+
}
272+
return bounds;
273+
}
274+
227275
void Window::SetMinimumSize(Size size) {
228276
// Windows minimum size would be handled in WM_GETMINMAXINFO message
229277
// This is a placeholder implementation

0 commit comments

Comments
 (0)