diff --git a/cmd/vrc_osc_gui/window_windows.go b/cmd/vrc_osc_gui/window_windows.go index d257555..e0ea441 100644 --- a/cmd/vrc_osc_gui/window_windows.go +++ b/cmd/vrc_osc_gui/window_windows.go @@ -28,6 +28,7 @@ const ( wsVisible = 0x10000000 wsChild = 0x40000000 wsPopup = 0x80000000 + wsSizeBox = 0x00040000 wsClipChildren = 0x02000000 wsClipSiblings = 0x04000000 wsBorder = 0x00800000 @@ -42,6 +43,7 @@ const ( wmPaint = 0x000F wmEraseBkgnd = 0x0014 wmSize = 0x0005 + wmGetMinMaxInfo = 0x0024 wmVScroll = 0x0115 wmMouseWheel = 0x020A wmCloseGUI = 0x0010 @@ -58,6 +60,14 @@ const ( enKillFocus = 0x0200 bnClicked = 0x0000 htCaption = 2 + htLeft = 10 + htRight = 11 + htTop = 12 + htTopLeft = 13 + htTopRight = 14 + htBottom = 15 + htBottomLeft = 16 + htBottomRight = 17 tbButtonStructSize = 0x041E tbAddButtons = 0x0414 tbAddStringW = 0x044D @@ -98,6 +108,9 @@ const ( contentTop = headerHeight + navBarHeight bottomBarHeight = 48 windowWidth = 760 + minWindowWidth = 760 + minWindowHeight = contentTop + leftPaneHeight + bottomBarHeight + resizeGripSize = 8 leftPaneWidth = 445 leftPaneHeight = 560 rightPaneWidth = windowWidth - leftPaneWidth @@ -576,6 +589,15 @@ func runNativeGUI(initialTab string) error { return paintMainWindow(hwnd, &app) case wmEraseBkgnd: return 1 + case wmGetMinMaxInfo: + applyMinMaxInfo(lParam) + return 0 + case wmSize: + layoutMainWindow(&app) + if invalidateRectProc != nil { + invalidateRectProc.Call(hwnd, 0, 1) + } + return 0 case wmCreateGUI: app.hwnd = hwnd buttonClass, _ := syscall.UTF16PtrFromString("BUTTON") @@ -796,8 +818,16 @@ func runNativeGUI(initialTab string) error { case wmLButtonDown: x := int32(lParam & 0xffff) y := int32((lParam >> 16) & 0xffff) + var rc winRect + if getClientRectProc != nil { + getClientRectProc.Call(hwnd, uintptr(unsafe.Pointer(&rc))) + } + if ht := resizeHitTest(x, y, rc); ht != 0 { + releaseCapture.Call() + sendMessageProc.Call(hwnd, wmNCLButtonDown, uintptr(ht), 0) + return 0 + } if y >= 0 && y < headerHeight { - var rc winRect width := int32(windowWidth) if getClientRectProc != nil { getClientRectProc.Call(hwnd, uintptr(unsafe.Pointer(&rc))) @@ -950,7 +980,7 @@ func runNativeGUI(initialTab string) error { } guiLog("stage=create window") - app.hwnd, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(title)), wsPopup|wsVisible|wsClipChildren, 200, 120, windowWidth, contentTop+leftPaneHeight+bottomBarHeight, 0, 0, hInstance, 0) + app.hwnd, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(title)), wsPopup|wsSizeBox|wsVisible|wsClipChildren, 200, 120, windowWidth, contentTop+leftPaneHeight+bottomBarHeight, 0, 0, hInstance, 0) if app.hwnd == 0 { guiLog("stage=create window failed") return fmt.Errorf("create window failed") @@ -1611,33 +1641,43 @@ func resizeGUIForJoinContent(app *guiApp, contentHeight int) { if contentHeight < leftPaneHeight { contentHeight = leftPaneHeight } + if w, h, ok := clientSize(app.hwnd); ok { + if h > contentTop+contentHeight+bottomBarHeight { + contentHeight = h - contentTop - bottomBarHeight + } + if w < minWindowWidth { + w = minWindowWidth + } + resizeFlags := hwndNoMove | hwndNoZOrder | hwndNoActivate + windowHeight := contentTop + contentHeight + bottomBarHeight + setWindowPosProc.Call(app.hwnd, 0, 0, 0, uintptr(w), uintptr(windowHeight), resizeFlags) + layoutMainWindow(app) + return + } resizeFlags := hwndNoMove | hwndNoZOrder | hwndNoActivate windowHeight := contentTop + contentHeight + bottomBarHeight setWindowPosProc.Call(app.hwnd, 0, 0, 0, windowWidth, uintptr(windowHeight), resizeFlags) - for _, item := range []struct { - hwnd uintptr - width int - }{ - {app.leftHwnd, leftPaneWidth}, - {app.rightHwnd, rightPaneWidth}, - {app.settingsPaneHwnd, settingsPaneWidth}, - } { - if item.hwnd != 0 { - setWindowPosProc.Call(item.hwnd, 0, 0, 0, uintptr(item.width), uintptr(contentHeight), resizeFlags) - } - } + layoutMainWindow(app) } func resizeGUIForSettings(app *guiApp) { if app == nil || setWindowPosProc == nil || app.hwnd == 0 { return } - resizeFlags := hwndNoMove | hwndNoZOrder | hwndNoActivate - windowHeight := contentTop + settingsPaneHeight + bottomBarHeight - setWindowPosProc.Call(app.hwnd, 0, 0, 0, windowWidth, uintptr(windowHeight), resizeFlags) - if app.settingsPaneHwnd != 0 { - setWindowPosProc.Call(app.settingsPaneHwnd, 0, 0, uintptr(contentTop), windowWidth, uintptr(settingsPaneHeight), resizeFlags) + contentHeight := settingsPaneHeight + width := windowWidth + if w, h, ok := clientSize(app.hwnd); ok { + if w > width { + width = w + } + if h > contentTop+contentHeight+bottomBarHeight { + contentHeight = h - contentTop - bottomBarHeight + } } + resizeFlags := hwndNoMove | hwndNoZOrder | hwndNoActivate + windowHeight := contentTop + contentHeight + bottomBarHeight + setWindowPosProc.Call(app.hwnd, 0, 0, 0, uintptr(width), uintptr(windowHeight), resizeFlags) + layoutMainWindow(app) } type winRect struct { @@ -1647,6 +1687,110 @@ type winRect struct { Bottom int32 } +type winPoint struct { + X int32 + Y int32 +} + +type minMaxInfo struct { + Reserved winPoint + MaxSize winPoint + MaxPosition winPoint + MinTrackSize winPoint + MaxTrackSize winPoint +} + +func applyMinMaxInfo(lParam uintptr) { + if lParam == 0 { + return + } + info := (*minMaxInfo)(unsafe.Pointer(lParam)) + info.MinTrackSize.X = minWindowWidth + info.MinTrackSize.Y = minWindowHeight +} + +func clientSize(hwnd uintptr) (int, int, bool) { + if hwnd == 0 || getClientRectProc == nil { + return 0, 0, false + } + var rc winRect + getClientRectProc.Call(hwnd, uintptr(unsafe.Pointer(&rc))) + w := int(rc.Right - rc.Left) + h := int(rc.Bottom - rc.Top) + if w <= 0 || h <= 0 { + return 0, 0, false + } + return w, h, true +} + +func resizeHitTest(x, y int32, rc winRect) int32 { + if rc.Right <= 0 || rc.Bottom <= 0 { + return 0 + } + onLeft := x >= 0 && x < resizeGripSize + onRight := x <= rc.Right && x > rc.Right-resizeGripSize + onTop := y >= 0 && y < resizeGripSize + onBottom := y <= rc.Bottom && y > rc.Bottom-resizeGripSize + switch { + case onTop && onLeft: + return htTopLeft + case onTop && onRight: + return htTopRight + case onBottom && onLeft: + return htBottomLeft + case onBottom && onRight: + return htBottomRight + case onLeft: + return htLeft + case onRight: + return htRight + case onTop: + return htTop + case onBottom: + return htBottom + default: + return 0 + } +} + +func layoutMainWindow(app *guiApp) { + if app == nil || app.hwnd == 0 || setWindowPosProc == nil { + return + } + clientW, clientH, ok := clientSize(app.hwnd) + if !ok { + return + } + contentH := clientH - contentTop - bottomBarHeight + if contentH < 1 { + contentH = 1 + } + leftW := leftPaneWidth + if clientW < leftW+160 { + leftW = clientW / 2 + if leftW < 240 { + leftW = 240 + } + } + rightW := clientW - leftW + if rightW < 1 { + rightW = 1 + } + resizeFlags := hwndNoZOrder | hwndNoActivate + if app.navBarHwnd != 0 { + setWindowPosProc.Call(app.navBarHwnd, 0, 0, uintptr(headerHeight), uintptr(clientW), uintptr(navBarHeight), resizeFlags) + } + if app.leftHwnd != 0 { + setWindowPosProc.Call(app.leftHwnd, 0, 0, uintptr(contentTop), uintptr(leftW), uintptr(contentH), resizeFlags) + } + if app.rightHwnd != 0 { + setWindowPosProc.Call(app.rightHwnd, 0, uintptr(leftW), uintptr(contentTop), uintptr(rightW), uintptr(contentH), resizeFlags) + } + if app.settingsPaneHwnd != 0 { + setWindowPosProc.Call(app.settingsPaneHwnd, 0, 0, uintptr(contentTop), uintptr(clientW), uintptr(contentH), resizeFlags) + } +} + type paintStruct struct { Hdc uintptr FErase int32