5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 15:30:37 +08:00

Support for minimum and maximum window sizes (#612)

* add support for minimum and maximum window sizes

* attempt to fix windows

* bug fixes

* support min/max window sizes on Linux and Windows

* fix min/max window sizes on Linux

* formatting and comments

* fixes Windows DPI issue, clamps width/height values to min/max

* App can't go into full screen when max size is set for Mac

* fixed Linux maximum width/height on window maximize

* Revert "fixed Linux maximum width/height on window maximize"

This reverts commit 3f7ba8b264.

The fix glitches on PopOS

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
This commit is contained in:
RH12503 2021-04-02 13:14:09 +08:00 committed by GitHub
parent 2b6860b6c3
commit a1bd1013cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 299 additions and 5 deletions

View File

@ -31,6 +31,18 @@ type AppConfig struct {
// Indicates whether your app should be resizable // Indicates whether your app should be resizable
Resizable bool 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 // Indicated if the devtools should be disabled
DisableInspector bool DisableInspector bool
} }
@ -65,6 +77,26 @@ func (a *AppConfig) GetResizable() bool {
return a.Resizable 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 // GetDisableInspector returns true if the inspector should be disabled
func (a *AppConfig) GetDisableInspector() bool { func (a *AppConfig) GetDisableInspector() bool {
return a.DisableInspector return a.DisableInspector
@ -115,6 +147,23 @@ func (a *AppConfig) merge(in *AppConfig) error {
if in.Height != 0 { if in.Height != 0 {
a.Height = in.Height 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.Resizable = in.Resizable
a.DisableInspector = in.DisableInspector a.DisableInspector = in.DisableInspector
@ -127,6 +176,10 @@ func newConfig(userConfig *AppConfig) (*AppConfig, error) {
Width: 800, Width: 800,
Height: 600, Height: 600,
Resizable: true, Resizable: true,
MinWidth: -1,
MinHeight: -1,
MaxWidth: -1,
MaxHeight: -1,
Title: "My Wails App", Title: "My Wails App",
Colour: "#FFF", // White by default Colour: "#FFF", // White by default
HTML: defaultHTML, HTML: defaultHTML,

View File

@ -5,6 +5,10 @@ type AppConfig interface {
GetWidth() int GetWidth() int
GetHeight() int GetHeight() int
GetTitle() string GetTitle() string
GetMinWidth() int
GetMinHeight() int
GetMaxWidth() int
GetMaxHeight() int
GetResizable() bool GetResizable() bool
GetHTML() string GetHTML() string
GetDisableInspector() bool GetDisableInspector() bool

View File

@ -22,6 +22,10 @@ type Renderer interface {
// Window Runtime // Window Runtime
SetColour(string) error SetColour(string) error
SetMinSize(width, height int)
SetMaxSize(width, height int)
Fullscreen() Fullscreen()
UnFullscreen() UnFullscreen()
SetTitle(title string) SetTitle(title string)

View File

@ -1,4 +1,4 @@
package interfaces package interfaces
// Runtime interface // Runtime interface
type Runtime interface {} type Runtime interface{}

View File

@ -186,6 +186,18 @@ func (h *Bridge) SetColour(colour string) error {
return nil 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 // Fullscreen is unsupported for Bridge but required
// for the Renderer interface // for the Renderer interface
func (h *Bridge) Fullscreen() { func (h *Bridge) Fullscreen() {

View File

@ -29,6 +29,8 @@ type WebView struct {
config interfaces.AppConfig config interfaces.AppConfig
eventManager interfaces.EventManager eventManager interfaces.EventManager
bindingCache []string bindingCache []string
maximumSizeSet bool
} }
// NewWebView returns a new WebView struct // NewWebView returns a new WebView struct
@ -52,10 +54,37 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana
// Save the config // Save the config
w.config = 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 // Create the WebView instance
w.window = wv.NewWebview(wv.Settings{ w.window = wv.NewWebview(wv.Settings{
Width: config.GetWidth(), Width: width,
Height: config.GetHeight(), Height: height,
Title: config.GetTitle(), Title: config.GetTitle(),
Resizable: config.GetResizable(), Resizable: config.GetResizable(),
URL: config.GetHTML(), URL: config.GetHTML(),
@ -64,6 +93,16 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana
w.ipc.Dispatch(message, w.callback) 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) // SignalManager.OnExit(w.Exit)
@ -74,6 +113,7 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana
} }
w.log.Info("Initialised") w.log.Info("Initialised")
return nil return nil
} }
@ -353,11 +393,37 @@ func (w *WebView) NotifyEvent(event *messages.EventData) error {
return w.evalJS(message) 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 // Fullscreen makes the main window go fullscreen
func (w *WebView) Fullscreen() { func (w *WebView) Fullscreen() {
if w.config.GetResizable() == false { if w.config.GetResizable() == false {
w.log.Warn("Cannot call Fullscreen() - App.Resizable = false") w.log.Warn("Cannot call Fullscreen() - App.Resizable = false")
return return
} else if w.maximumSizeSet {
w.log.Warn("Cannot call Fullscreen() - Maximum size of window set")
return
} }
w.window.Dispatch(func() { w.window.Dispatch(func() {
w.window.SetFullscreen(true) w.window.SetFullscreen(true)

View File

@ -69,6 +69,14 @@ static inline void CgoWebViewFocus(void *w) {
webview_focus((struct webview *)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) { static inline void CgoWebViewSetFullscreen(void *w, int fullscreen) {
webview_set_fullscreen((struct webview *)w, fullscreen); webview_set_fullscreen((struct webview *)w, fullscreen);
} }
@ -178,6 +186,12 @@ type WebView interface {
// Focus() puts the main window into focus // Focus() puts the main window into focus
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 // SetFullscreen() controls window full-screen mode. This method must be
// called from the main thread only. See Dispatch() for more details. // called from the main thread only. See Dispatch() for more details.
SetFullscreen(fullscreen bool) SetFullscreen(fullscreen bool)
@ -319,6 +333,14 @@ func (w *webview) Focus() {
C.CgoWebViewFocus(w.w) 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) { func (w *webview) SetFullscreen(fullscreen bool) {
C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen))) C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen)))
} }

View File

@ -54,6 +54,11 @@ extern "C"
int ready; int ready;
int js_busy; int js_busy;
int should_exit; int should_exit;
int min_width;
int min_height;
int max_width;
int max_height;
}; };
#elif defined(WEBVIEW_WINAPI) #elif defined(WEBVIEW_WINAPI)
#define CINTERFACE #define CINTERFACE
@ -75,6 +80,11 @@ struct webview_priv
DWORD saved_style; DWORD saved_style;
DWORD saved_ex_style; DWORD saved_ex_style;
RECT saved_rect; RECT saved_rect;
int min_width;
int min_height;
int max_width;
int max_height;
}; };
#elif defined(WEBVIEW_COCOA) #elif defined(WEBVIEW_COCOA)
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@ -169,6 +179,8 @@ struct webview_priv
WEBVIEW_API int webview_inject_css(struct webview *w, const char *css); 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_set_title(struct webview *w, const char *title);
WEBVIEW_API void webview_focus(struct webview *w); 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_fullscreen(struct webview *w, int fullscreen);
WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g, WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
uint8_t b, uint8_t a); uint8_t b, uint8_t a);
@ -330,6 +342,12 @@ struct webview_priv
w->priv.should_exit = 0; w->priv.should_exit = 0;
w->priv.queue = g_async_queue_new(); w->priv.queue = g_async_queue_new();
w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 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); gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title);
if (w->resizable) if (w->resizable)
@ -402,6 +420,44 @@ struct webview_priv
gtk_window_present(GTK_WINDOW(w->priv.window)); 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) WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen)
{ {
if (fullscreen) if (fullscreen)
@ -1337,7 +1393,39 @@ struct webview_priv
case WM_CREATE: case WM_CREATE:
w = (struct webview *)((CREATESTRUCT *)lParam)->lpCreateParams; w = (struct webview *)((CREATESTRUCT *)lParam)->lpCreateParams;
w->priv.hwnd = hwnd; w->priv.hwnd = hwnd;
return EmbedBrowserObject(w); 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: case WM_DESTROY:
UnEmbedBrowserObject(w); UnEmbedBrowserObject(w);
PostQuitMessage(0); PostQuitMessage(0);
@ -1402,6 +1490,9 @@ struct webview_priv
WEBVIEW_API int webview_init(struct webview *w) WEBVIEW_API int webview_init(struct webview *w)
{ {
w->priv.min_width = -1;
w->priv.max_width = -1;
WNDCLASSEX wc; WNDCLASSEX wc;
HINSTANCE hInstance; HINSTANCE hInstance;
DWORD style; DWORD style;
@ -1652,6 +1743,16 @@ struct webview_priv
SetFocus(w->priv.hwnd); 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) WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen)
{ {
if (w->priv.is_fullscreen == !!fullscreen) if (w->priv.is_fullscreen == !!fullscreen)
@ -2223,7 +2324,26 @@ struct webview_priv
{ {
[w->priv.window makeKeyWindow]; [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) WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen)
{ {
int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) == int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) ==
@ -2378,4 +2498,4 @@ struct webview_priv
} }
#endif #endif
#endif /* WEBVIEW_H */ #endif /* WEBVIEW_H */

3
package-lock.json generated Normal file
View File

@ -0,0 +1,3 @@
{
"lockfileVersion": 1
}

View File

@ -67,6 +67,16 @@ func (r *Window) SetColour(colour string) error {
return r.renderer.SetColour(colour) 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 // Fullscreen makes the window fullscreen
func (r *Window) Fullscreen() { func (r *Window) Fullscreen() {
r.renderer.Fullscreen() r.renderer.Fullscreen()