github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/windows/winc/form.go (about) 1 //go:build windows 2 3 /* 4 * Copyright (C) 2019 The Winc Authors. All Rights Reserved. 5 * Copyright (C) 2010-2013 Allen Dang. All Rights Reserved. 6 */ 7 8 package winc 9 10 import ( 11 "unsafe" 12 13 "github.com/secoba/wails/v2/internal/frontend/desktop/windows/winc/w32" 14 ) 15 16 type LayoutManager interface { 17 Update() 18 } 19 20 // A Form is main window of the application. 21 type Form struct { 22 ControlBase 23 24 layoutMng LayoutManager 25 26 // Fullscreen / Unfullscreen 27 isFullscreen bool 28 previousWindowStyle uint32 29 previousWindowExStyle uint32 30 previousWindowPlacement w32.WINDOWPLACEMENT 31 } 32 33 func NewCustomForm(parent Controller, exStyle int, dwStyle uint) *Form { 34 fm := new(Form) 35 36 RegClassOnlyOnce("winc_Form") 37 38 fm.isForm = true 39 40 if exStyle == 0 { 41 exStyle = w32.WS_EX_CONTROLPARENT | w32.WS_EX_APPWINDOW 42 } 43 44 if dwStyle == 0 { 45 dwStyle = w32.WS_OVERLAPPEDWINDOW 46 } 47 48 fm.hwnd = CreateWindow("winc_Form", parent, uint(exStyle), dwStyle) 49 fm.parent = parent 50 51 // this might fail if icon resource is not embedded in the binary 52 if ico, err := NewIconFromResource(GetAppInstance(), uint16(AppIconID)); err == nil { 53 fm.SetIcon(0, ico) 54 } 55 56 // This forces display of focus rectangles, as soon as the user starts to type. 57 w32.SendMessage(fm.hwnd, w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0) 58 59 RegMsgHandler(fm) 60 61 fm.SetFont(DefaultFont) 62 fm.SetText("Form") 63 return fm 64 } 65 66 func NewForm(parent Controller) *Form { 67 fm := new(Form) 68 69 RegClassOnlyOnce("winc_Form") 70 71 fm.isForm = true 72 fm.hwnd = CreateWindow("winc_Form", parent, w32.WS_EX_CONTROLPARENT|w32.WS_EX_APPWINDOW, w32.WS_OVERLAPPEDWINDOW) 73 fm.parent = parent 74 75 // this might fail if icon resource is not embedded in the binary 76 if ico, err := NewIconFromResource(GetAppInstance(), uint16(AppIconID)); err == nil { 77 fm.SetIcon(0, ico) 78 } 79 80 // This forces display of focus rectangles, as soon as the user starts to type. 81 w32.SendMessage(fm.hwnd, w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0) 82 83 RegMsgHandler(fm) 84 85 fm.SetFont(DefaultFont) 86 fm.SetText("Form") 87 return fm 88 } 89 90 func (fm *Form) SetLayout(mng LayoutManager) { 91 fm.layoutMng = mng 92 } 93 94 // UpdateLayout refresh layout. 95 func (fm *Form) UpdateLayout() { 96 if fm.layoutMng != nil { 97 fm.layoutMng.Update() 98 } 99 } 100 101 func (fm *Form) NewMenu() *Menu { 102 hMenu := w32.CreateMenu() 103 if hMenu == 0 { 104 panic("failed CreateMenu") 105 } 106 m := &Menu{hMenu: hMenu, hwnd: fm.hwnd} 107 if !w32.SetMenu(fm.hwnd, hMenu) { 108 panic("failed SetMenu") 109 } 110 return m 111 } 112 113 func (fm *Form) DisableIcon() { 114 windowInfo := getWindowInfo(fm.hwnd) 115 frameless := windowInfo.IsPopup() 116 if frameless { 117 return 118 } 119 exStyle := w32.GetWindowLong(fm.hwnd, w32.GWL_EXSTYLE) 120 w32.SetWindowLong(fm.hwnd, w32.GWL_EXSTYLE, uint32(exStyle|w32.WS_EX_DLGMODALFRAME)) 121 w32.SetWindowPos(fm.hwnd, 0, 0, 0, 0, 0, 122 uint( 123 w32.SWP_FRAMECHANGED| 124 w32.SWP_NOMOVE| 125 w32.SWP_NOSIZE| 126 w32.SWP_NOZORDER), 127 ) 128 } 129 130 func (fm *Form) Maximise() { 131 w32.ShowWindow(fm.hwnd, w32.SW_MAXIMIZE) 132 } 133 134 func (fm *Form) Minimise() { 135 w32.ShowWindow(fm.hwnd, w32.SW_MINIMIZE) 136 } 137 138 func (fm *Form) Restore() { 139 // SC_RESTORE param for WM_SYSCOMMAND to restore app if it is minimized 140 const SC_RESTORE = 0xF120 141 // restore the minimized window, if it is 142 w32.SendMessage( 143 fm.hwnd, 144 w32.WM_SYSCOMMAND, 145 SC_RESTORE, 146 0, 147 ) 148 w32.ShowWindow(fm.hwnd, w32.SW_RESTORE) 149 } 150 151 // Public methods 152 func (fm *Form) Center() { 153 154 windowInfo := getWindowInfo(fm.hwnd) 155 frameless := windowInfo.IsPopup() 156 157 info := getMonitorInfo(fm.hwnd) 158 workRect := info.RcWork 159 screenMiddleW := workRect.Left + (workRect.Right-workRect.Left)/2 160 screenMiddleH := workRect.Top + (workRect.Bottom-workRect.Top)/2 161 var winRect *w32.RECT 162 if !frameless { 163 winRect = w32.GetWindowRect(fm.hwnd) 164 } else { 165 winRect = w32.GetClientRect(fm.hwnd) 166 } 167 winWidth := winRect.Right - winRect.Left 168 winHeight := winRect.Bottom - winRect.Top 169 windowX := screenMiddleW - (winWidth / 2) 170 windowY := screenMiddleH - (winHeight / 2) 171 w32.SetWindowPos(fm.hwnd, w32.HWND_TOP, int(windowX), int(windowY), int(winWidth), int(winHeight), w32.SWP_NOSIZE) 172 } 173 174 func (fm *Form) Fullscreen() { 175 if fm.isFullscreen { 176 return 177 } 178 179 fm.previousWindowStyle = uint32(w32.GetWindowLongPtr(fm.hwnd, w32.GWL_STYLE)) 180 fm.previousWindowExStyle = uint32(w32.GetWindowLong(fm.hwnd, w32.GWL_EXSTYLE)) 181 182 monitor := w32.MonitorFromWindow(fm.hwnd, w32.MONITOR_DEFAULTTOPRIMARY) 183 var monitorInfo w32.MONITORINFO 184 monitorInfo.CbSize = uint32(unsafe.Sizeof(monitorInfo)) 185 if !w32.GetMonitorInfo(monitor, &monitorInfo) { 186 return 187 } 188 if !w32.GetWindowPlacement(fm.hwnd, &fm.previousWindowPlacement) { 189 return 190 } 191 // According to https://devblogs.microsoft.com/oldnewthing/20050505-04/?p=35703 one should use w32.WS_POPUP | w32.WS_VISIBLE 192 w32.SetWindowLong(fm.hwnd, w32.GWL_STYLE, fm.previousWindowStyle & ^uint32(w32.WS_OVERLAPPEDWINDOW) | (w32.WS_POPUP|w32.WS_VISIBLE)) 193 w32.SetWindowLong(fm.hwnd, w32.GWL_EXSTYLE, fm.previousWindowExStyle & ^uint32(w32.WS_EX_DLGMODALFRAME)) 194 fm.isFullscreen = true 195 w32.SetWindowPos(fm.hwnd, w32.HWND_TOP, 196 int(monitorInfo.RcMonitor.Left), 197 int(monitorInfo.RcMonitor.Top), 198 int(monitorInfo.RcMonitor.Right-monitorInfo.RcMonitor.Left), 199 int(monitorInfo.RcMonitor.Bottom-monitorInfo.RcMonitor.Top), 200 w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED) 201 } 202 203 func (fm *Form) UnFullscreen() { 204 if !fm.isFullscreen { 205 return 206 } 207 w32.SetWindowLong(fm.hwnd, w32.GWL_STYLE, fm.previousWindowStyle) 208 w32.SetWindowLong(fm.hwnd, w32.GWL_EXSTYLE, fm.previousWindowExStyle) 209 w32.SetWindowPlacement(fm.hwnd, &fm.previousWindowPlacement) 210 fm.isFullscreen = false 211 w32.SetWindowPos(fm.hwnd, 0, 0, 0, 0, 0, 212 w32.SWP_NOMOVE|w32.SWP_NOSIZE|w32.SWP_NOZORDER|w32.SWP_NOOWNERZORDER|w32.SWP_FRAMECHANGED) 213 } 214 215 func (fm *Form) IsFullScreen() bool { 216 return fm.isFullscreen 217 } 218 219 // IconType: 1 - ICON_BIG; 0 - ICON_SMALL 220 func (fm *Form) SetIcon(iconType int, icon *Icon) { 221 if iconType > 1 { 222 panic("IconType is invalid") 223 } 224 w32.SendMessage(fm.hwnd, w32.WM_SETICON, uintptr(iconType), uintptr(icon.Handle())) 225 } 226 227 func (fm *Form) EnableMaxButton(b bool) { 228 SetStyle(fm.hwnd, b, w32.WS_MAXIMIZEBOX) 229 } 230 231 func (fm *Form) EnableMinButton(b bool) { 232 SetStyle(fm.hwnd, b, w32.WS_MINIMIZEBOX) 233 } 234 235 func (fm *Form) EnableSizable(b bool) { 236 SetStyle(fm.hwnd, b, w32.WS_THICKFRAME) 237 } 238 239 func (fm *Form) EnableDragMove(_ bool) { 240 //fm.isDragMove = b 241 } 242 243 func (fm *Form) EnableTopMost(b bool) { 244 tag := w32.HWND_NOTOPMOST 245 if b { 246 tag = w32.HWND_TOPMOST 247 } 248 w32.SetWindowPos(fm.hwnd, tag, 0, 0, 0, 0, w32.SWP_NOMOVE|w32.SWP_NOSIZE) 249 } 250 251 func (fm *Form) WndProc(msg uint32, wparam, lparam uintptr) uintptr { 252 253 switch msg { 254 case w32.WM_COMMAND: 255 if lparam == 0 && w32.HIWORD(uint32(wparam)) == 0 { 256 // Menu support. 257 actionID := uint16(w32.LOWORD(uint32(wparam))) 258 if action, ok := actionsByID[actionID]; ok { 259 action.onClick.Fire(NewEvent(fm, nil)) 260 } 261 } 262 case w32.WM_KEYDOWN: 263 // Accelerator support. 264 key := Key(wparam) 265 if uint32(lparam)>>30 == 0 { 266 // Using TranslateAccelerators refused to work, so we handle them 267 // ourselves, at least for now. 268 shortcut := Shortcut{ModifiersDown(), key} 269 if action, ok := shortcut2Action[shortcut]; ok { 270 if action.Enabled() { 271 action.onClick.Fire(NewEvent(fm, nil)) 272 } 273 } 274 } 275 276 case w32.WM_CLOSE: 277 return 0 278 case w32.WM_DESTROY: 279 w32.PostQuitMessage(0) 280 return 0 281 282 case w32.WM_SIZE, w32.WM_PAINT: 283 if fm.layoutMng != nil { 284 fm.layoutMng.Update() 285 } 286 case w32.WM_GETMINMAXINFO: 287 mmi := (*w32.MINMAXINFO)(unsafe.Pointer(lparam)) 288 hasConstraints := false 289 if fm.minWidth > 0 || fm.minHeight > 0 { 290 hasConstraints = true 291 292 width, height := fm.scaleWithWindowDPI(fm.minWidth, fm.minHeight) 293 if width > 0 { 294 mmi.PtMinTrackSize.X = int32(width) 295 } 296 if height > 0 { 297 mmi.PtMinTrackSize.Y = int32(height) 298 } 299 } 300 if fm.maxWidth > 0 || fm.maxHeight > 0 { 301 hasConstraints = true 302 303 width, height := fm.scaleWithWindowDPI(fm.maxWidth, fm.maxHeight) 304 if width > 0 { 305 mmi.PtMaxTrackSize.X = int32(width) 306 } 307 if height > 0 { 308 mmi.PtMaxTrackSize.Y = int32(height) 309 } 310 } 311 if hasConstraints { 312 return 0 313 } 314 } 315 316 return w32.DefWindowProc(fm.hwnd, msg, wparam, lparam) 317 }