Repository: fyne-io/fyne
File: internal/driver/glfw/menu_darwin.go (Go)

Code before:
		nsMenuItem := insertNativeMenuItem(nsMenu, item, nextItemID, -1)
		nextItemID = registerCallback(w, item, nextItemID)
		if item.ChildMenu != nil {
			nextItemID = addNativeSubmenu(w, nsMenuItem, item.ChildMenu, nextItemID)
		}
	}
	return nsMenu, nextItemID
}

//export exceptionCallback
func exceptionCallback(e *C.char) {
	msg := C.GoString(e)
	if ecb == nil {
		panic("unhandled Obj-C exception: " + msg)
	}
	ecb(msg)
}

func handleSpecialItems(w *window, menu *fyne.Menu, nextItemID int, addSeparator bool) (*fyne.Menu, int) {
	for i, item := range menu.Items {
		switch item.Label {
		case "Settings", "Settings…", "Preferences", "Preferences…":
			items := make([]*fyne.MenuItem, 0, len(menu.Items)-1)
			items = append(items, menu.Items[:i]...)
			items = append(items, menu.Items[i+1:]...)
			menu.Items = items

			insertNativeMenuItem(C.darwinAppMenu(), item, nextItemID, 1)
			if addSeparator {
				C.insertDarwinMenuItem(
					C.darwinAppMenu(),
					C.CString(""),
					C.CString(""),
					C.uint(0),
					C.int(nextItemID),
					C.int(1),
					C.bool(true),
					unsafe.Pointer(nil),
					C.uint(0),
				)
			}
			nextItemID = registerCallback(w, item, nextItemID)
		case "About":
			items := make([]*fyne.MenuItem, 0, len(menu.Items)-1)
			items = append(items, menu.Items[:i]...)
			if i < len(menu.Items)-1 {
				items = append(items, menu.Items[i+1:]...)
			}
			menu.Items = items

			insertNativeMenuItem(C.darwinAppMenu(), item, nextItemID, 1)

Review comment (suggestion):
I can see that removing the recursion is a win but now you are modifying the input.
In the end you are modifying the window’s `mainMenu` which is provided by the app developer and I think we should not do that.
So, either we have three options:
- create a new menu for each special item (like the recursion did)
- create a menu copy up front and then modify this (useless copy when there are no special items but this is a setup task, only)
- create a menu copy on demand (probably leads to more complicated code)

Code after:
		nsMenuItem := insertNativeMenuItem(nsMenu, item, nextItemID, -1)
		nextItemID = registerCallback(w, item, nextItemID)
		if item.ChildMenu != nil {
			nextItemID = addNativeSubmenu(w, nsMenuItem, item.ChildMenu, nextItemID)
		}
	}
	return nsMenu, nextItemID
}

//export exceptionCallback
func exceptionCallback(e *C.char) {
	msg := C.GoString(e)
	if ecb == nil {
		panic("unhandled Obj-C exception: " + msg)
	}
	ecb(msg)
}

func handleSpecialItems(w *window, menu *fyne.Menu, nextItemID int, addSeparator bool) (*fyne.Menu, int) {
	menu = fyne.NewMenu(menu.Label, menu.Items...) // copy so we can manipulate
	for i := 0; i < len(menu.Items); i++ {
		item := menu.Items[i]
		switch item.Label {
		case "About", "Settings", "Settings…", "Preferences", "Preferences…":
			items := make([]*fyne.MenuItem, 0, len(menu.Items)-1)
			items = append(items, menu.Items[:i]...)
			items = append(items, menu.Items[i+1:]...)
			menu, nextItemID = handleSpecialItems(w, fyne.NewMenu(menu.Label, items...), nextItemID, false)
			i--

			insertNativeMenuItem(C.darwinAppMenu(), item, nextItemID, 1)
			if addSeparator && item.Label != "About" {
				C.insertDarwinMenuItem(
					C.darwinAppMenu(),
					C.CString(""),
					C.CString(""),
					C.uint(0),
					C.int(nextItemID),
					C.int(1),
					C.bool(true),
					unsafe.Pointer(nil),
					C.uint(0),
				)
			}
			nextItemID = registerCallback(w, item, nextItemID)
		}
	}
	return menu, nextItemID
}

// TODO: theme change support, see NSSystemColorsDidChangeNotification