diff --git a/config.go b/config.go index dd0c457c3..67ee7e9ef 100644 --- a/config.go +++ b/config.go @@ -31,6 +31,18 @@ type AppConfig struct { // Indicates whether your app should be resizable Resizable bool + // Minimum width of a resizable window. If set, MinHeight should also be set. + MinWidth int + + // Minimum height of a resizable window. If set, MinWidth should also be set. + MinHeight int + + // Maximum width of a resizable window. If set, MaxHeight should also be set. + MaxWidth int + + // Maximum height of a resizable window. If set, MaxWidth should also be set. + MaxHeight int + // Indicated if the devtools should be disabled DisableInspector bool } @@ -65,6 +77,26 @@ func (a *AppConfig) GetResizable() bool { return a.Resizable } +// GetMinWidth returns the minimum width of the window +func (a *AppConfig) GetMinWidth() int { + return a.MinWidth +} + +// GetMinHeight returns the minimum height of the window +func (a *AppConfig) GetMinHeight() int { + return a.MinHeight +} + +// GetMaxWidth returns the maximum width of the window +func (a *AppConfig) GetMaxWidth() int { + return a.MaxWidth +} + +// GetMaxHeight returns the maximum height of the window +func (a *AppConfig) GetMaxHeight() int { + return a.MaxHeight +} + // GetDisableInspector returns true if the inspector should be disabled func (a *AppConfig) GetDisableInspector() bool { return a.DisableInspector @@ -115,6 +147,23 @@ func (a *AppConfig) merge(in *AppConfig) error { if in.Height != 0 { a.Height = in.Height } + + if in.MinWidth != 0 { + a.MinWidth = in.MinWidth + } + + if in.MinHeight != 0 { + a.MinHeight = in.MinHeight + } + + if in.MaxWidth != 0 { + a.MaxWidth = in.MaxWidth + } + + if in.MaxHeight != 0 { + a.MaxHeight = in.MaxHeight + } + a.Resizable = in.Resizable a.DisableInspector = in.DisableInspector @@ -127,6 +176,10 @@ func newConfig(userConfig *AppConfig) (*AppConfig, error) { Width: 800, Height: 600, Resizable: true, + MinWidth: -1, + MinHeight: -1, + MaxWidth: -1, + MaxHeight: -1, Title: "My Wails App", Colour: "#FFF", // White by default HTML: defaultHTML, diff --git a/lib/interfaces/appconfig.go b/lib/interfaces/appconfig.go index 6e063b4d4..2feb157a8 100644 --- a/lib/interfaces/appconfig.go +++ b/lib/interfaces/appconfig.go @@ -5,6 +5,10 @@ type AppConfig interface { GetWidth() int GetHeight() int GetTitle() string + GetMinWidth() int + GetMinHeight() int + GetMaxWidth() int + GetMaxHeight() int GetResizable() bool GetHTML() string GetDisableInspector() bool diff --git a/lib/interfaces/renderer.go b/lib/interfaces/renderer.go index a0502c871..8f5dbfb36 100644 --- a/lib/interfaces/renderer.go +++ b/lib/interfaces/renderer.go @@ -22,6 +22,10 @@ type Renderer interface { // Window Runtime SetColour(string) error + + SetMinSize(width, height int) + SetMaxSize(width, height int) + Fullscreen() UnFullscreen() SetTitle(title string) diff --git a/lib/interfaces/runtime.go b/lib/interfaces/runtime.go index fefd2997e..dfc0e6bc0 100644 --- a/lib/interfaces/runtime.go +++ b/lib/interfaces/runtime.go @@ -1,4 +1,4 @@ package interfaces // Runtime interface -type Runtime interface {} \ No newline at end of file +type Runtime interface{} diff --git a/lib/renderer/bridge/bridge.go b/lib/renderer/bridge/bridge.go index 4c6bc4cce..3fd3646cb 100644 --- a/lib/renderer/bridge/bridge.go +++ b/lib/renderer/bridge/bridge.go @@ -186,6 +186,18 @@ func (h *Bridge) SetColour(colour string) error { return nil } +// SetMinSize is unsupported for Bridge but required +// for the Renderer interface +func (h *Bridge) SetMinSize(width, height int) { + h.log.Warn("SetMinSize() unsupported in bridge mode") +} + +// SetMaxSize is unsupported for Bridge but required +// for the Renderer interface +func (h *Bridge) SetMaxSize(width, height int) { + h.log.Warn("SetMaxSize() unsupported in bridge mode") +} + // Fullscreen is unsupported for Bridge but required // for the Renderer interface func (h *Bridge) Fullscreen() { diff --git a/lib/renderer/webview.go b/lib/renderer/webview.go index 1c0ff73a7..054cbdc4e 100644 --- a/lib/renderer/webview.go +++ b/lib/renderer/webview.go @@ -29,6 +29,8 @@ type WebView struct { config interfaces.AppConfig eventManager interfaces.EventManager bindingCache []string + + maximumSizeSet bool } // NewWebView returns a new WebView struct @@ -52,10 +54,37 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana // Save the config w.config = config + width := config.GetWidth() + height := config.GetHeight() + + // Clamp width and height + minWidth, minHeight := config.GetMinWidth(), config.GetMinHeight() + maxWidth, maxHeight := config.GetMaxWidth(), config.GetMaxHeight() + setMinSize := minWidth != -1 && minHeight != -1 + setMaxSize := maxWidth != -1 && maxHeight != -1 + + if setMinSize { + if width < minWidth { + width = minWidth + } + if height < minHeight { + height = minHeight + } + } + + if setMaxSize { + if width > maxWidth { + width = maxWidth + } + if height > maxHeight { + height = maxHeight + } + } + // Create the WebView instance w.window = wv.NewWebview(wv.Settings{ - Width: config.GetWidth(), - Height: config.GetHeight(), + Width: width, + Height: height, Title: config.GetTitle(), Resizable: config.GetResizable(), URL: config.GetHTML(), @@ -64,6 +93,16 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana w.ipc.Dispatch(message, w.callback) }, }) + fmt.Println("Control") + + // Set minimum and maximum sizes + if setMinSize { + w.SetMinSize(minWidth, minHeight) + } + if setMaxSize { + w.SetMaxSize(maxWidth, maxHeight) + fmt.Println("Max") + } // SignalManager.OnExit(w.Exit) @@ -74,6 +113,7 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana } w.log.Info("Initialised") + return nil } @@ -353,11 +393,37 @@ func (w *WebView) NotifyEvent(event *messages.EventData) error { return w.evalJS(message) } +// SetMinSize sets the minimum size of a resizable window +func (w *WebView) SetMinSize(width, height int) { + if w.config.GetResizable() == false { + w.log.Warn("Cannot call SetMinSize() - App.Resizable = false") + return + } + w.window.Dispatch(func() { + w.window.SetMinSize(width, height) + }) +} + +// SetMaxSize sets the maximum size of a resizable window +func (w *WebView) SetMaxSize(width, height int) { + if w.config.GetResizable() == false { + w.log.Warn("Cannot call SetMaxSize() - App.Resizable = false") + return + } + w.maximumSizeSet = true + w.window.Dispatch(func() { + w.window.SetMaxSize(width, height) + }) +} + // Fullscreen makes the main window go fullscreen func (w *WebView) Fullscreen() { if w.config.GetResizable() == false { w.log.Warn("Cannot call Fullscreen() - App.Resizable = false") return + } else if w.maximumSizeSet { + w.log.Warn("Cannot call Fullscreen() - Maximum size of window set") + return } w.window.Dispatch(func() { w.window.SetFullscreen(true) diff --git a/lib/renderer/webview/webview.go b/lib/renderer/webview/webview.go index 0e5da75cb..60ce89e26 100755 --- a/lib/renderer/webview/webview.go +++ b/lib/renderer/webview/webview.go @@ -69,6 +69,14 @@ static inline void CgoWebViewFocus(void *w) { webview_focus((struct webview *)w); } +static inline void CgoWebViewMinSize(void *w, int width, int height) { + webview_minsize((struct webview *)w, width, height); +} + +static inline void CgoWebViewMaxSize(void *w, int width, int height) { + webview_maxsize((struct webview *)w, width, height); +} + static inline void CgoWebViewSetFullscreen(void *w, int fullscreen) { webview_set_fullscreen((struct webview *)w, fullscreen); } @@ -178,6 +186,12 @@ type WebView interface { // Focus() puts the main window into focus Focus() + // SetMinSize() sets the minimum size of the window + SetMinSize(width, height int) + + // SetMaxSize() sets the maximum size of the window + SetMaxSize(width, height int) + // SetFullscreen() controls window full-screen mode. This method must be // called from the main thread only. See Dispatch() for more details. SetFullscreen(fullscreen bool) @@ -319,6 +333,14 @@ func (w *webview) Focus() { C.CgoWebViewFocus(w.w) } +func (w *webview) SetMinSize(width, height int) { + C.CgoWebViewMinSize(w.w, C.int(width), C.int(height)) +} + +func (w *webview) SetMaxSize(width, height int) { + C.CgoWebViewMaxSize(w.w, C.int(width), C.int(height)) +} + func (w *webview) SetFullscreen(fullscreen bool) { C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen))) } diff --git a/lib/renderer/webview/webview.h b/lib/renderer/webview/webview.h index f1327b59d..da041b0d3 100644 --- a/lib/renderer/webview/webview.h +++ b/lib/renderer/webview/webview.h @@ -54,6 +54,11 @@ extern "C" int ready; int js_busy; int should_exit; + + int min_width; + int min_height; + int max_width; + int max_height; }; #elif defined(WEBVIEW_WINAPI) #define CINTERFACE @@ -75,6 +80,11 @@ struct webview_priv DWORD saved_style; DWORD saved_ex_style; RECT saved_rect; + + int min_width; + int min_height; + int max_width; + int max_height; }; #elif defined(WEBVIEW_COCOA) #import @@ -169,6 +179,8 @@ struct webview_priv WEBVIEW_API int webview_inject_css(struct webview *w, const char *css); WEBVIEW_API void webview_set_title(struct webview *w, const char *title); WEBVIEW_API void webview_focus(struct webview *w); + WEBVIEW_API void webview_minsize(struct webview *w, int width, int height); + WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height); WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen); WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, uint8_t b, uint8_t a); @@ -330,6 +342,12 @@ struct webview_priv w->priv.should_exit = 0; w->priv.queue = g_async_queue_new(); w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + w->priv.min_width = -1; + w->priv.min_height = -1; + w->priv.max_width = -1; + w->priv.max_height = -1; + gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title); if (w->resizable) @@ -402,6 +420,44 @@ struct webview_priv gtk_window_present(GTK_WINDOW(w->priv.window)); } + WEBVIEW_API void webview_minsize(struct webview *w, int width, int height) { + + w->priv.min_width = width; + w->priv.min_height = height; + + GdkGeometry hints; + GdkWindowHints usedHints = (GdkWindowHints) GDK_HINT_MIN_SIZE; + + hints.min_width = w->priv.min_width; + hints.min_height = w->priv.min_height; + if (w->priv.max_width != -1) { + hints.max_width = w->priv.max_width; + hints.max_height = w->priv.max_height; + usedHints = (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); + } + + gtk_window_set_geometry_hints(GTK_WINDOW(w->priv.window), w->priv.window, &hints, usedHints); + } + + WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height) { + + w->priv.max_width = width; + w->priv.max_height = height; + + GdkGeometry hints; + GdkWindowHints usedHints = (GdkWindowHints) GDK_HINT_MAX_SIZE; + + if (w->priv.min_width != -1) { + hints.min_width = w->priv.min_width; + hints.min_height = w->priv.min_height; + usedHints = (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); + } + hints.max_width = w->priv.max_width; + hints.max_height = w->priv.max_height; + + gtk_window_set_geometry_hints(GTK_WINDOW(w->priv.window), w->priv.window, &hints, usedHints); + } + WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { if (fullscreen) @@ -1337,7 +1393,39 @@ struct webview_priv case WM_CREATE: w = (struct webview *)((CREATESTRUCT *)lParam)->lpCreateParams; w->priv.hwnd = hwnd; + return EmbedBrowserObject(w); + case WM_GETMINMAXINFO: + { + if (w != NULL) { + // get pixel density + HDC hDC = GetDC(NULL); + double DPIScaleX = GetDeviceCaps(hDC, 88)/96.0; + double DPIScaleY = GetDeviceCaps(hDC, 90)/96.0; + ReleaseDC(NULL, hDC); + + RECT rcClient, rcWind; + POINT ptDiff; + GetClientRect(hwnd, &rcClient); + GetWindowRect(hwnd, &rcWind); + + int widthExtra = (rcWind.right - rcWind.left) - rcClient.right; + int heightExtra = (rcWind.bottom - rcWind.top) - rcClient.bottom; + + LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; + + if (w->priv.min_width != -1) { + lpMMI->ptMinTrackSize.x = w->priv.min_width * DPIScaleX + widthExtra; + lpMMI->ptMinTrackSize.y = w->priv.min_height * DPIScaleY + heightExtra; + } + if (w->priv.max_width != -1) { + lpMMI->ptMaxTrackSize.x = w->priv.max_width * DPIScaleX + widthExtra; + lpMMI->ptMaxTrackSize.y = w->priv.max_height * DPIScaleY + heightExtra; + } + } + + return 0; + } case WM_DESTROY: UnEmbedBrowserObject(w); PostQuitMessage(0); @@ -1402,6 +1490,9 @@ struct webview_priv WEBVIEW_API int webview_init(struct webview *w) { + w->priv.min_width = -1; + w->priv.max_width = -1; + WNDCLASSEX wc; HINSTANCE hInstance; DWORD style; @@ -1652,6 +1743,16 @@ struct webview_priv SetFocus(w->priv.hwnd); } + WEBVIEW_API void webview_minsize(struct webview *w, int width, int height) { + w->priv.min_width = width; + w->priv.min_height = height; + } + + WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height) { + w->priv.max_width = width; + w->priv.max_height = height; + } + WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { if (w->priv.is_fullscreen == !!fullscreen) @@ -2223,7 +2324,26 @@ struct webview_priv { [w->priv.window makeKeyWindow]; } - + + WEBVIEW_API void webview_minsize(struct webview *w, int width, int height) { + NSSize size; + size.width = width; + size.height = height; + [w->priv.window setMinSize:size]; + } + + WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height) { + NSSize size; + size.width = width; + size.height = height; + [w->priv.window setMaxSize:size]; + + [w->priv.window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorFullScreenNone|NSWindowCollectionBehaviorFullScreenDisallowsTiling]; + + NSButton *button = [w->priv.window standardWindowButton:NSWindowZoomButton]; + [button setEnabled: NO]; + } + WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) { int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) == @@ -2378,4 +2498,4 @@ struct webview_priv } #endif -#endif /* WEBVIEW_H */ +#endif /* WEBVIEW_H */ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..48e341a09 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +} diff --git a/runtime/window.go b/runtime/window.go index 960e02b7b..3665cbaaf 100644 --- a/runtime/window.go +++ b/runtime/window.go @@ -67,6 +67,16 @@ func (r *Window) SetColour(colour string) error { return r.renderer.SetColour(colour) } +// SetMinSize sets the minimum size of a resizable window +func (r *Window) SetMinSize(width, height int) { + r.renderer.SetMinSize(width, height) +} + +// SetMaxSize sets the maximum size of a resizable window +func (r *Window) SetMaxSize(width, height int) { + r.renderer.SetMaxSize(width, height) +} + // Fullscreen makes the window fullscreen func (r *Window) Fullscreen() { r.renderer.Fullscreen()