Skip to content

Commit

Permalink
Add new menubar option
Browse files Browse the repository at this point in the history
  • Loading branch information
leaanthony committed Jan 17, 2025
1 parent 9a4cb53 commit 260336e
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 10 deletions.
24 changes: 24 additions & 0 deletions v3/examples/window-menu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Window Menu Example

*** Windows Only ***

This example demonstrates how to create a window with a menu bar that can be toggled using the window.ToggleMenuBar() method.

## Features

- Default menu bar with File, Edit, and Help menus
- F1 key to toggle menu bar visibility
- Simple HTML interface with instructions

## Running the Example

```bash
cd v3/examples/window-menu
go run .
```

## How it Works

The example creates a window with a default menu and binds the F10 key to toggle the menu bar's visibility. The menu bar will hide when F10 is pressed and show when F10 is released.

Note: The menu bar toggling functionality only works on Windows. On other platforms, the F10 key binding will have no effect.
14 changes: 14 additions & 0 deletions v3/examples/window-menu/assets/about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<html>
<head>
<title>Window Menu Demo</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<div class="container">
<h1>About Window Menu Demo</h1>
<p>Press F1 to toggle menu bar visibility</p>
<p>Press F2 to show menu bar</p>
<p>Press F3 to hide menu bar</p>
</div>
</body>
</html>
48 changes: 48 additions & 0 deletions v3/examples/window-menu/assets/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<html>
<head>
<title>Window Menu Demo</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
margin: 0;
padding: 2rem;
background: #f5f5f5;
color: #333;
}
.container {
max-width: 600px;
margin: 0 auto;
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
margin-top: 0;
color: #2d2d2d;
}
.key {
background: #e9e9e9;
padding: 2px 8px;
border-radius: 4px;
border: 1px solid #ccc;
font-family: monospace;
}
</style>
</head>
<body>
<div class="container">
<h1>Window Menu Demo</h1>
<p>This example demonstrates the menu bar visibility toggle feature.</p>
<p>Press <span class="key">F1</span> to toggle the menu bar.</p>
<p>Press <span class="key">F2</span> to show the menu bar.</p>
<p>Press <span class="key">F3</span> to hide the menu bar.</p>
<p>The menu includes:</p>
<ul>
<li>File menu with Exit option</li>
<li>MenuBar menu with Hide options</li>
<li>Help menu with About</li>
</ul>
</div>
</body>
</html>
26 changes: 26 additions & 0 deletions v3/examples/window-menu/assets/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
body {
font-family: system-ui, -apple-system, sans-serif;
margin: 0;
padding: 2rem;
background: #f5f5f5;
color: #333;
}
.container {
max-width: 600px;
margin: 0 auto;
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
margin-top: 0;
color: #2d2d2d;
}
.key {
background: #e9e9e9;
padding: 2px 8px;
border-radius: 4px;
border: 1px solid #ccc;
font-family: monospace;
}
64 changes: 64 additions & 0 deletions v3/examples/window-menu/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"embed"
_ "embed"
"github.com/wailsapp/wails/v3/pkg/application"
"log"
)

//go:embed assets/*
var assets embed.FS

func main() {
app := application.New(application.Options{
Name: "Window Menu Demo",
Description: "A demo of menu bar toggling",
Assets: application.AssetOptions{
Handler: application.BundledAssetFileServer(assets),
},
})

// Create a menu
menu := app.NewMenu()
fileMenu := menu.AddSubmenu("File")
fileMenu.Add("Exit").OnClick(func(ctx *application.Context) {
app.Quit()
})

editMenu := menu.AddSubmenu("MenuBar")
editMenu.Add("Hide MenuBar").OnClick(func(ctx *application.Context) {
app.CurrentWindow().HideMenuBar()
})

helpMenu := menu.AddSubmenu("Help")
helpMenu.Add("About").OnClick(func(ctx *application.Context) {
app.CurrentWindow().SetURL("/about.html")
})

// Create window with menu
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "Window Menu Demo",
Width: 800,
Height: 600,
Windows: application.WindowsWindow{
Menu: menu,
},
KeyBindings: map[string]func(window *application.WebviewWindow){
"F1": func(window *application.WebviewWindow) {
window.ToggleMenuBar()
},
"F2": func(window *application.WebviewWindow) {
window.ShowMenuBar()
},
"F3": func(window *application.WebviewWindow) {
window.HideMenuBar()
},
},
})

err := app.Run()
if err != nil {
log.Fatal(err)
}
}
27 changes: 27 additions & 0 deletions v3/pkg/application/webview_window.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ type (
delete()
selectAll()
redo()
showMenuBar()
hideMenuBar()
toggleMenuBar()
}
)

Expand Down Expand Up @@ -1344,3 +1347,27 @@ func (w *WebviewWindow) redo() {
}
w.impl.redo()
}

// ShowMenuBar shows the menu bar for the window.
func (w *WebviewWindow) ShowMenuBar() {
if w.impl == nil || w.isDestroyed() {
return
}
InvokeSync(w.impl.showMenuBar)
}

// HideMenuBar hides the menu bar for the window.
func (w *WebviewWindow) HideMenuBar() {
if w.impl == nil || w.isDestroyed() {
return
}
InvokeSync(w.impl.hideMenuBar)
}

// ToggleMenuBar toggles the menu bar for the window.
func (w *WebviewWindow) ToggleMenuBar() {
if w.impl == nil || w.isDestroyed() {
return
}
InvokeSync(w.impl.toggleMenuBar)
}
4 changes: 4 additions & 0 deletions v3/pkg/application/webview_window_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -1438,3 +1438,7 @@ func (w *macosWebviewWindow) delete() {

func (w *macosWebviewWindow) redo() {
}

func (w *macosWebviewWindow) showMenuBar() {}
func (w *macosWebviewWindow) hideMenuBar() {}
func (w *macosWebviewWindow) toggleMenuBar() {}
4 changes: 4 additions & 0 deletions v3/pkg/application/webview_window_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,7 @@ func (w *linuxWebviewWindow) isIgnoreMouseEvents() bool {
func (w *linuxWebviewWindow) setIgnoreMouseEvents(ignore bool) {
w.ignoreMouse(w.ignoreMouseEvents)
}

func (w *linuxWebviewWindow) showMenuBar() {}
func (w *linuxWebviewWindow) hideMenuBar() {}
func (w *linuxWebviewWindow) toggleMenuBar() {}
79 changes: 78 additions & 1 deletion v3/pkg/application/webview_window_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -1169,9 +1169,37 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp
w.parent.emit(events.Windows.WindowBackgroundErase)
return 1 // Let WebView2 handle background erasing
// Check for keypress
case w32.WM_SYSCOMMAND:
switch wparam {
case w32.SC_KEYMENU:
if lparam == 0 {
// F10 or plain Alt key
if w.processKeyBinding(w32.VK_F10) {
return 0
}
} else {
// Alt + key combination
// The character code is in the low word of lparam
char := byte(lparam & 0xFF)
// Convert ASCII to virtual key code if needed
vkey := w32.VkKeyScan(uint16(char))
if w.processKeyBinding(uint(vkey)) {
return 0
}
}
}
case w32.WM_SYSKEYDOWN:
globalApplication.info("w32.WM_SYSKEYDOWN: %v", uint(wparam))
w.parent.emit(events.Windows.WindowKeyDown)
if w.processKeyBinding(uint(wparam)) {
return 0
}
case w32.WM_SYSKEYUP:
w.parent.emit(events.Windows.WindowKeyUp)
case w32.WM_KEYDOWN:
w.processKeyBinding(uint(wparam))
globalApplication.info("w32.WM_KEYDOWN: %v", uint(wparam))
w.parent.emit(events.Windows.WindowKeyDown)
w.processKeyBinding(uint(wparam))
case w32.WM_KEYUP:
w.parent.emit(events.Windows.WindowKeyUp)
case w32.WM_SIZE:
Expand Down Expand Up @@ -1917,6 +1945,43 @@ func (w *windowsWebviewWindow) setMinimiseButtonEnabled(enabled bool) {
w.setStyle(enabled, w32.WS_MINIMIZEBOX)
}

func (w *windowsWebviewWindow) toggleMenuBar() {
if w.menu != nil {
if w32.GetMenu(w.hwnd) == 0 {
w32.SetMenu(w.hwnd, w.menu.menu)
} else {
w32.SetMenu(w.hwnd, 0)
}

// Get the bounds of the client area
//bounds := w32.GetClientRect(w.hwnd)

// Resize the webview
w.chromium.Resize()

// Update size of webview
w.update()
// Restore focus to the webview after toggling menu
w.focus()
}
}

func (w *windowsWebviewWindow) enableRedraw() {
w32.SendMessage(w.hwnd, w32.WM_SETREDRAW, 1, 0)
w32.RedrawWindow(w.hwnd, nil, 0, w32.RDW_ERASE|w32.RDW_FRAME|w32.RDW_INVALIDATE|w32.RDW_ALLCHILDREN)
}

func (w *windowsWebviewWindow) disableRedraw() {
w32.SendMessage(w.hwnd, w32.WM_SETREDRAW, 0, 0)
}

func (w *windowsWebviewWindow) disableRedrawWithCallback(callback func()) {
w.disableRedraw()
callback()
w.enableRedraw()

}

func NewIconFromResource(instance w32.HINSTANCE, resId uint16) (w32.HICON, error) {
var err error
var result w32.HICON
Expand Down Expand Up @@ -1986,3 +2051,15 @@ func (w *windowsWebviewWindow) setPadding(padding edge.Rect) {
}
w.chromium.SetPadding(padding)
}

func (w *windowsWebviewWindow) showMenuBar() {
if w.menu != nil {
w32.SetMenu(w.hwnd, w.menu.menu)
}
}

func (w *windowsWebviewWindow) hideMenuBar() {
if w.menu != nil {
w32.SetMenu(w.hwnd, 0)
}
}
3 changes: 3 additions & 0 deletions v3/pkg/application/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Window interface {
HandleWindowEvent(id uint)
Height() int
Hide() Window
HideMenuBar()
ID() uint
Info(message string, args ...any)
IsFocused() bool
Expand Down Expand Up @@ -69,10 +70,12 @@ type Window interface {
SetURL(s string) Window
SetZoom(magnification float64) Window
Show() Window
ShowMenuBar()
Size() (width int, height int)
OpenDevTools()
ToggleFullscreen()
ToggleMaximise()
ToggleMenuBar()
UnFullscreen()
UnMaximise()
UnMinimise()
Expand Down
30 changes: 30 additions & 0 deletions v3/pkg/w32/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,36 @@ const (
WM_DPICHANGED = 0x02E0
)

const (
SC_SIZE = 0xF000 // Resize the window
SC_MOVE = 0xF010 // Move the window
SC_MINIMIZE = 0xF020 // Minimize the window
SC_MAXIMIZE = 0xF030 // Maximize the window
SC_NEXTWINDOW = 0xF040 // Move to next window
SC_PREVWINDOW = 0xF050 // Move to previous window
SC_CLOSE = 0xF060 // Close the window
SC_VSCROLL = 0xF070 // Vertical scroll
SC_HSCROLL = 0xF080 // Horizontal scroll
SC_MOUSEMENU = 0xF090 // Mouse menu
SC_KEYMENU = 0xF100 // Key menu (triggered by Alt or F10)
SC_ARRANGE = 0xF110 // Arrange windows
SC_RESTORE = 0xF120 // Restore window from minimized/maximized
SC_TASKLIST = 0xF130 // Task list
SC_SCREENSAVE = 0xF140 // Screen saver
SC_HOTKEY = 0xF150 // Hotkey
SC_DEFAULT = 0xF160 // Default command
SC_MONITORPOWER = 0xF170 // Monitor power
SC_CONTEXTHELP = 0xF180 // Context help
SC_SEPARATOR = 0xF00F // Separator
)

const (
// Remove the Close option from the window menu
SC_MASK_CLOSE = ^uint16(SC_CLOSE)
// Mask for extracting the system command
SC_MASK_CMD = 0xFFF0
)

// WM_ACTIVATE
const (
WA_INACTIVE = 0
Expand Down
Loading

0 comments on commit 260336e

Please sign in to comment.