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  }