Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[V3] Fix initially-hidden menuItem [mac/win] #4116

Open
wants to merge 15 commits into
base: v3-alpha
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions docs/src/content/docs/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add method `Close` on `sqlite` service to close the DB manually by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067)
- Add cancellation support for query methods on `sqlite` service by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067)
- Add prepared statement support to `sqlite` service with JS bindings by [@fbbdev](https://github.com/fbbdev) in [#4067](https://github.com/wailsapp/wails/pull/4067)
- Add `SetMenu()` on window to allow for setting a menu on a window by [@leaanthony](https://github.com/leaanthony)

### Fixed

Expand Down
37 changes: 37 additions & 0 deletions docs/src/content/docs/learn/application-menu.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,43 @@ Create a new application menu using the `NewMenu` method:
menu := app.NewMenu()
```

## Setting the Menu

The way to set the menu varies on the platform:

<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">

On macOS, there is only one menu bar per application. Set the menu using the `SetMenu` method of the application:

```go
app.SetMenu(menu)
```

</TabItem>

<TabItem label="Windows" icon="fa-brands:windows">

On Windows, there is a menu bar per window. Set the menu using the `SetMenu` method of the window:

```go
window.SetMenu(menu)
```

</TabItem>

<TabItem label="Linux" icon="fa-brands:linux">

On Linux, the menu bar is typically per window. Set the menu using the `SetMenu` method of the window:

```go
window.SetMenu(menu)
```

</TabItem>
</Tabs>


## Menu Roles

Wails provides predefined menu roles that automatically create platform-appropriate menu structures:
Expand Down
20 changes: 9 additions & 11 deletions v3/examples/menu/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,20 @@ func main() {
if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu)
}
fileMenu := menu.AddRole(application.FileMenu)
_ = fileMenu
//fileMenu.FindByRole(application.Open).OnClick(func(context *application.Context) {
// selection, err := application.OpenFileDialog().PromptForSingleSelection()
// if err != nil {
// println("Error: " + err.Error())
// return
// }
// println("You selected: " + selection)
//})
menu.AddRole(application.FileMenu)
menu.AddRole(application.EditMenu)
menu.AddRole(application.WindowMenu)
menu.AddRole(application.HelpMenu)

// Let's make a "Demo" menu
myMenu := menu.AddSubmenu("Demo")

// Hidden menu item that can be unhidden
hidden := myMenu.Add("I was hidden").SetHidden(true)
myMenu.Add("Toggle the hidden menu").OnClick(func(ctx *application.Context) {
hidden.SetHidden(!hidden.Hidden())
})

// Disabled menu item
myMenu.Add("Not Enabled").SetEnabled(false)

Expand Down Expand Up @@ -118,7 +115,8 @@ func main() {
})
app.SetMenu(menu)

app.NewWebviewWindow().SetBackgroundColour(application.NewRGB(33, 37, 41))
window := app.NewWebviewWindow().SetBackgroundColour(application.NewRGB(33, 37, 41))
window.SetMenu(menu)

err := app.Run()

Expand Down
6 changes: 3 additions & 3 deletions v3/pkg/application/menu_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,6 @@ func (m *macosMenu) update() {

func (m *macosMenu) processMenu(parent unsafe.Pointer, menu *Menu) {
for _, item := range menu.items {
if item.hidden {
continue
}
switch item.itemType {
case submenu:
submenu := item.submenu
Expand All @@ -102,6 +99,9 @@ func (m *macosMenu) processMenu(parent unsafe.Pointer, menu *Menu) {
case text, checkbox, radio:
menuItem := newMenuItemImpl(item)
item.impl = menuItem
if item.hidden {
menuItem.setHidden(true)
}
C.addMenuItem(parent, menuItem.nsMenuItem)
case separator:
C.addMenuSeparator(parent)
Expand Down
17 changes: 13 additions & 4 deletions v3/pkg/application/menu_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ func (w *windowsMenu) update() {

func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) {
for _, item := range inputMenu.items {
w.currentMenuID++
itemID := w.currentMenuID
w.menuMapping[itemID] = item

menuItemImpl := newMenuItemImpl(item, parentMenu, itemID)
menuItemImpl.parent = inputMenu
item.impl = menuItemImpl

if item.Hidden() {
if item.accelerator != nil && item.callback != nil {
if w.parentWindow != nil {
Expand All @@ -44,11 +52,7 @@ func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) {
globalApplication.removeKeyBinding(item.accelerator.String())
}
}
continue
}
w.currentMenuID++
itemID := w.currentMenuID
w.menuMapping[itemID] = item

flags := uint32(w32.MF_STRING)
if item.disabled {
Expand Down Expand Up @@ -84,6 +88,11 @@ func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) {
}
var menuText = w32.MustStringToUTF16Ptr(thisText)

// If the item is hidden, don't append
if item.Hidden() {
continue
}

w32.AppendMenu(parentMenu, flags, uintptr(itemID), menuText)
if item.bitmap != nil {
w32.SetMenuIcons(parentMenu, itemID, item.bitmap, nil)
Expand Down
14 changes: 6 additions & 8 deletions v3/pkg/application/menuitem_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ type windowsMenuItem struct {
}

func (m *windowsMenuItem) setHidden(hidden bool) {
m.hidden = hidden
if m.hidden {
// iterate the parent items and find the menu item before us
if hidden && !m.hidden {
m.hidden = true
// iterate the parent items and find the menu item after us
for i, item := range m.parent.items {
if item == m.menuItem {
if i < len(m.parent.items)-1 {
Expand All @@ -37,13 +37,11 @@ func (m *windowsMenuItem) setHidden(hidden bool) {
break
}
}
// Get the position of this menu item in the parent menu
// m.pos = w32.GetMenuItemPosition(m.hMenu, uint32(m.id))
// Remove from parent menu
w32.RemoveMenu(m.hMenu, m.id, w32.MF_BYCOMMAND)
} else {
// Add to parent menu
// Get the position of the item before us
} else if !hidden && m.hidden {
m.hidden = false
// Add to parent menu before the "itemAfter"
var pos int
if m.itemAfter != nil {
for i, item := range m.parent.items {
Expand Down
23 changes: 14 additions & 9 deletions v3/pkg/application/popupmenu_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ func (p *Win32Menu) newMenu() w32.HMENU {
func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) {
currentRadioGroup := RadioGroup{}
for _, item := range inputMenu.items {
p.currentMenuID++
itemID := p.currentMenuID
p.menuMapping[itemID] = item

menuItemImpl := newMenuItemImpl(item, parentMenu, itemID)
menuItemImpl.parent = inputMenu
item.impl = menuItemImpl

if item.Hidden() {
if item.accelerator != nil {
if p.parentWindow != nil {
Expand All @@ -71,14 +79,7 @@ func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) {
globalApplication.removeKeyBinding(item.accelerator.String())
}
}
continue
}
p.currentMenuID++
itemID := p.currentMenuID
p.menuMapping[itemID] = item

menuItemImpl := newMenuItemImpl(item, parentMenu, itemID)
menuItemImpl.parent = inputMenu

flags := uint32(w32.MF_STRING)
if item.disabled {
Expand Down Expand Up @@ -131,6 +132,12 @@ func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) {
}
}
}

// If the item is hidden, don't append
if item.Hidden() {
continue
}

ok := w32.AppendMenu(parentMenu, flags, uintptr(itemID), w32.MustStringToUTF16Ptr(menuText))
if !ok {
globalApplication.fatal("error adding menu item '%s'", menuText)
Expand All @@ -141,8 +148,6 @@ func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) {
globalApplication.fatal("error setting menu icons: %w", err)
}
}

item.impl = menuItemImpl
}
if len(currentRadioGroup) > 0 {
for _, radioMember := range currentRadioGroup {
Expand Down
17 changes: 17 additions & 0 deletions v3/pkg/application/webview_window.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ type (
showMenuBar()
hideMenuBar()
toggleMenuBar()
setMenu(menu *Menu)
}
)

Expand Down Expand Up @@ -168,6 +169,22 @@ type WebviewWindow struct {
unconditionallyClose bool
}

func (w *WebviewWindow) SetMenu(menu *Menu) {
switch runtime.GOOS {
case "darwin":
return
case "windows":
w.options.Windows.Menu = menu
case "linux":
w.options.Linux.Menu = menu
}
if w.impl != nil {
InvokeSync(func() {
w.impl.setMenu(menu)
})
}
}

// EmitEvent emits an event from the window
func (w *WebviewWindow) EmitEvent(name string, data ...any) {
globalApplication.emitEvent(&CustomEvent{
Expand Down
7 changes: 4 additions & 3 deletions v3/pkg/application/webview_window_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,7 @@ func (w *macosWebviewWindow) delete() {
func (w *macosWebviewWindow) redo() {
}

func (w *macosWebviewWindow) showMenuBar() {}
func (w *macosWebviewWindow) hideMenuBar() {}
func (w *macosWebviewWindow) toggleMenuBar() {}
func (w *macosWebviewWindow) showMenuBar() {}
func (w *macosWebviewWindow) hideMenuBar() {}
func (w *macosWebviewWindow) toggleMenuBar() {}
func (w *macosWebviewWindow) setMenu(_ *Menu) {}
9 changes: 9 additions & 0 deletions v3/pkg/application/webview_window_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,15 @@ func (w *linuxWebviewWindow) setPhysicalBounds(physicalBounds Rect) {
w.setBounds(physicalBounds)
}

func (w *linuxWebviewWindow) setMenu(menu *Menu) {
if menu == nil {
w.gtkmenu = nil
return
}
w.parent.options.Linux.Menu = menu
w.gtkmenu = (menu.impl).(*linuxMenu).native
}

func (w *linuxWebviewWindow) run() {
for eventId := range w.parent.eventListeners {
w.on(eventId)
Expand Down
7 changes: 7 additions & 0 deletions v3/pkg/application/webview_window_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ type windowsWebviewWindow struct {
isMinimizing bool
}

func (w *windowsWebviewWindow) setMenu(menu *Menu) {
menu.Update()
w.menu = NewApplicationMenu(w, menu)
w.menu.parentWindow = w
w32.SetMenu(w.hwnd, w.menu.menu)
}

func (w *windowsWebviewWindow) cut() {
w.execJS("document.execCommand('cut')")
}
Expand Down
1 change: 1 addition & 0 deletions v3/pkg/application/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,5 @@ type Window interface {
ZoomIn()
ZoomOut()
ZoomReset() Window
SetMenu(menu *Menu)
}
Loading