Hi @Syntec. Thanks for the detailed report. Your diagnosis is exactly right: each of Left, Top, Width, and Height is currently applied to the operating-system window as a separate operation, so a combined move-and-resize passes through intermediate states - that's the bounce you're seeing. There's nothing wrong with your framework code; the gap is on Objo's side.
I've accepted the feature and tracked it here: https://feedback.objo.dev/feature/1577
What I'm adding
- A new
Window.SetBounds(Left As Integer, Top As Integer, Width As Integer, Height As Integer) method that applies position and size in a single update, so the window moves and resizes in one visual step with no intermediate states. I went with SetBounds rather than your suggested SetPosition since it sets the complete bounds (position and size) but it's otherwise exactly what you proposed.
- Batching for the individual properties too: consecutive assignments like
Win.Left = l
Win.Top = t
Win.Width = w
Win.Height = h
will be applied together in one update, so your existing code will improve without any changes once this ships.
What I'm deliberately not adding
- Suspend/resume of window updates: the UI framework Objo is built on has no cross-platform way to suppress window updates. Emulating it per-OS requires exactly the platform-specific tricks you were trying to avoid, tends to leave drawing artefacts, and risks a permanently frozen window if code fails between the suspend and resume calls. The combined call removes the intermediate states at the source rather than papering over them, so it's simply the better fix.
Declare access to external libraries such as SetWindowPos in user32.dll: this stays out by design. Objo apps remain cross-platform precisely because they can't reach into OS internals - your instinct that a background helper "feels like a bad hack that complicates cross-platform development" matches the exact reason.
- Exposing the native window handle (
hWnd): same reasoning; it would invite code that only works on one platform.
One extra thing worth knowing when building a custom-drawn framework: Moved and Resized are deliberately "settled" events. They fire about a quarter of a second after the window stops changing, so bursts of changes coalesce into a single event. Resize is the continuous one. Live redraws belong in Paint, which follows every change immediately, so make sure nothing in your framework tracks geometry from the settled events or it will lag behind the drag.
Thanks again for the clear write-up - it made this easy to act on. You can follow progress on the Feedback item above.