mirror of
https://github.com/wavetermdev/wails.git
synced 2026-08-05 13:53:43 -07:00
Initial menu item bitmap support
This commit is contained in:
@@ -20,4 +20,8 @@ go run .
|
||||
|
||||
# Known Issues
|
||||
|
||||
- [Resize cursor still visible when not resizable](https://github.com/orgs/wailsapp/projects/6/views/1?pane=issue&itemId=40962163)
|
||||
- [Resize cursor still visible when not resizable](https://github.com/orgs/wailsapp/projects/6/views/1?pane=issue&itemId=40962163)
|
||||
|
||||
---
|
||||
|
||||
Icon attribution: [Click icons created by kusumapotter - Flaticon](https://www.flaticon.com/free-icons/click)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 323 B |
@@ -8,6 +8,9 @@ import (
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
//go:embed icon.png
|
||||
var clickBitmap []byte
|
||||
|
||||
func main() {
|
||||
|
||||
app := application.New(application.Options{
|
||||
@@ -32,7 +35,7 @@ func main() {
|
||||
myMenu.Add("Not Enabled").SetEnabled(false)
|
||||
|
||||
// Click callbacks
|
||||
myMenu.Add("Click Me!").OnClick(func(ctx *application.Context) {
|
||||
myMenu.Add("Click Me!").SetBitmap(clickBitmap).OnClick(func(ctx *application.Context) {
|
||||
switch ctx.ClickedMenuItem().Label() {
|
||||
case "Click Me!":
|
||||
ctx.ClickedMenuItem().SetLabel("Thanks mate!")
|
||||
|
||||
@@ -415,6 +415,13 @@ func menuItemSetLabel(widget pointer, label string) {
|
||||
C.free(unsafe.Pointer(value))
|
||||
}
|
||||
|
||||
func menuItemSetBitmap(widget pointer, data []byte) {
|
||||
image := C.gdk_pixbuf_new_from_inline(C.int(len(data)), (*C.guchar)(unsafe.Pointer(&data[0])), C.FALSE, nil)
|
||||
imageWidget := C.gtk_image_new_from_pixbuf(image)
|
||||
C.gtk_image_set_pixel_size((*C.GtkImage)(imageWidget), 16)
|
||||
C.gtk_menu_item_set_image((*C.GtkMenuItem)(widget), imageWidget)
|
||||
}
|
||||
|
||||
func menuItemSetToolTip(widget pointer, tooltip string) {
|
||||
value := C.CString(tooltip)
|
||||
C.gtk_widget_set_tooltip_text(
|
||||
|
||||
@@ -100,6 +100,9 @@ func (m *macosMenu) processMenu(parent unsafe.Pointer, menu *Menu) {
|
||||
case separator:
|
||||
C.addMenuSeparator(parent)
|
||||
}
|
||||
if item.bitmap != nil {
|
||||
C.setMenuItemBitmap(item.impl, (*C.uchar)(&item.bitmap[0]), C.int(len(item.bitmap)))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,9 @@ func (w *windowsMenu) processMenu(parentMenu w32.HMENU, inputMenu *Menu) {
|
||||
var menuText = w32.MustStringToUTF16Ptr(item.Label())
|
||||
|
||||
w32.AppendMenu(parentMenu, flags, uintptr(itemID), menuText)
|
||||
if item.bitmap != nil {
|
||||
w32.SetMenuIcons(parentMenu, itemID, item.bitmap, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ type menuItemImpl interface {
|
||||
setChecked(checked bool)
|
||||
setAccelerator(accelerator *accelerator)
|
||||
setHidden(hidden bool)
|
||||
setBitmap(bitmap []byte)
|
||||
}
|
||||
|
||||
type MenuItem struct {
|
||||
@@ -49,6 +50,7 @@ type MenuItem struct {
|
||||
disabled bool
|
||||
checked bool
|
||||
hidden bool
|
||||
bitmap []byte
|
||||
submenu *Menu
|
||||
callback func(*Context)
|
||||
itemType menuItemType
|
||||
@@ -259,6 +261,14 @@ func (m *MenuItem) SetEnabled(enabled bool) *MenuItem {
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MenuItem) SetBitmap(bitmap []byte) *MenuItem {
|
||||
m.bitmap = bitmap
|
||||
if m.impl != nil {
|
||||
m.impl.setBitmap(bitmap)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MenuItem) SetChecked(checked bool) *MenuItem {
|
||||
m.checked = checked
|
||||
if m.impl != nil {
|
||||
|
||||
@@ -332,6 +332,11 @@ static void showAll(void) {
|
||||
[[NSApplication sharedApplication] unhideAllApplications:nil];
|
||||
}
|
||||
|
||||
static void setMenuItemBitmap(void* nsMenuItem, unsigned char *bitmap, int length) {
|
||||
MenuItem *menuItem = (MenuItem *)nsMenuItem;
|
||||
NSImage *image = [[NSImage alloc] initWithData:[NSData dataWithBytes:bitmap length:length]];
|
||||
[menuItem setImage:image];
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
@@ -339,33 +344,37 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type windowsMenuItem struct {
|
||||
type macosMenuItem struct {
|
||||
menuItem *MenuItem
|
||||
|
||||
nsMenuItem unsafe.Pointer
|
||||
}
|
||||
|
||||
func (m windowsMenuItem) setTooltip(tooltip string) {
|
||||
func (m macosMenuItem) setTooltip(tooltip string) {
|
||||
C.setMenuItemTooltip(m.nsMenuItem, C.CString(tooltip))
|
||||
}
|
||||
|
||||
func (m windowsMenuItem) setLabel(s string) {
|
||||
func (m macosMenuItem) setLabel(s string) {
|
||||
C.setMenuItemLabel(m.nsMenuItem, C.CString(s))
|
||||
}
|
||||
|
||||
func (m windowsMenuItem) setDisabled(disabled bool) {
|
||||
func (m macosMenuItem) setDisabled(disabled bool) {
|
||||
C.setMenuItemDisabled(m.nsMenuItem, C.bool(disabled))
|
||||
}
|
||||
|
||||
func (m windowsMenuItem) setChecked(checked bool) {
|
||||
func (m macosMenuItem) setChecked(checked bool) {
|
||||
C.setMenuItemChecked(m.nsMenuItem, C.bool(checked))
|
||||
}
|
||||
|
||||
func (m windowsMenuItem) setHidden(hidden bool) {
|
||||
func (m macosMenuItem) setHidden(hidden bool) {
|
||||
C.setMenuItemHidden(m.nsMenuItem, C.bool(hidden))
|
||||
}
|
||||
|
||||
func (m windowsMenuItem) setAccelerator(accelerator *accelerator) {
|
||||
func (m macosMenuItem) setBitmap(bitmap []byte) {
|
||||
C.setMenuItemBitmap(m.nsMenuItem, (*C.uchar)(&bitmap[0]), C.int(len(bitmap)))
|
||||
}
|
||||
|
||||
func (m macosMenuItem) setAccelerator(accelerator *accelerator) {
|
||||
// Set the keyboard shortcut of the menu item
|
||||
var modifier C.int
|
||||
var key *C.char
|
||||
@@ -378,8 +387,8 @@ func (m windowsMenuItem) setAccelerator(accelerator *accelerator) {
|
||||
C.setMenuItemKeyEquivalent(m.nsMenuItem, key, modifier)
|
||||
}
|
||||
|
||||
func newMenuItemImpl(item *MenuItem) *windowsMenuItem {
|
||||
result := &windowsMenuItem{
|
||||
func newMenuItemImpl(item *MenuItem) *macosMenuItem {
|
||||
result := &macosMenuItem{
|
||||
menuItem: item,
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,13 @@ func (l linuxMenuItem) blockSignal() {
|
||||
menuItemSignalBlock(l.native, l.handlerId, true)
|
||||
}
|
||||
}
|
||||
func (l linuxMenuItem) setBitmap(data []byte) {
|
||||
InvokeSync(func() {
|
||||
l.blockSignal()
|
||||
defer l.unBlockSignal()
|
||||
menuItemSetBitmap(l.native, data)
|
||||
})
|
||||
}
|
||||
|
||||
func (l linuxMenuItem) unBlockSignal() {
|
||||
if l.handlerId != 0 {
|
||||
|
||||
@@ -105,6 +105,20 @@ func (m *windowsMenuItem) setAccelerator(accelerator *accelerator) {
|
||||
//C.setMenuItemKeyEquivalent(m.nsMenuItem, key, modifier)
|
||||
}
|
||||
|
||||
func (m *windowsMenuItem) setBitmap(bitmap []byte) {
|
||||
if m.menuItem.bitmap == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Set the icon
|
||||
err := w32.SetMenuIcons(m.hMenu, m.id, bitmap, nil)
|
||||
if err != nil {
|
||||
globalApplication.error("Unable to set bitmap on menu item", "error", err.Error())
|
||||
return
|
||||
}
|
||||
m.update()
|
||||
}
|
||||
|
||||
func newMenuItemImpl(item *MenuItem, parentMenu w32.HMENU, ID int) *windowsMenuItem {
|
||||
result := &windowsMenuItem{
|
||||
menuItem: item,
|
||||
|
||||
@@ -109,6 +109,12 @@ func (p *Win32Menu) buildMenu(parentMenu w32.HMENU, inputMenu *Menu) {
|
||||
if !ok {
|
||||
w32.Fatal(fmt.Sprintf("Error adding menu item: %s", menuText))
|
||||
}
|
||||
if item.bitmap != nil {
|
||||
err := w32.SetMenuIcons(parentMenu, itemID, item.bitmap, nil)
|
||||
if err != nil {
|
||||
w32.Fatal(fmt.Sprintf("Error setting menu icons: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
item.impl = menuItemImpl
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
package w32
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -89,3 +93,45 @@ func CreateLargeHIconFromImage(fileData []byte) (HICON, error) {
|
||||
func SetWindowIcon(hwnd HWND, icon HICON) {
|
||||
SendMessage(hwnd, WM_SETICON, ICON_SMALL, uintptr(icon))
|
||||
}
|
||||
|
||||
func pngToImage(data []byte) (*image.RGBA, error) {
|
||||
img, err := png.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
rgba := image.NewRGBA(bounds)
|
||||
draw.Draw(rgba, bounds, img, bounds.Min, draw.Src)
|
||||
return rgba, nil
|
||||
}
|
||||
|
||||
func SetMenuIcons(parentMenu HMENU, itemID int, unchecked []byte, checked []byte) error {
|
||||
if unchecked == nil {
|
||||
return fmt.Errorf("invalid unchecked bitmap")
|
||||
}
|
||||
var err error
|
||||
var uncheckedIcon, checkedIcon HBITMAP
|
||||
var uncheckedImage, checkedImage *image.RGBA
|
||||
uncheckedImage, err = pngToImage(unchecked)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uncheckedIcon, err = CreateHBITMAPFromImage(uncheckedImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if checked != nil {
|
||||
checkedImage, err = pngToImage(checked)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
checkedIcon, err = CreateHBITMAPFromImage(checkedImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
checkedIcon = uncheckedIcon
|
||||
}
|
||||
return SetMenuItemBitmaps(parentMenu, uint32(itemID), MF_BYCOMMAND, checkedIcon, uncheckedIcon)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package w32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/sys/windows"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
@@ -169,6 +170,8 @@ var (
|
||||
|
||||
procFlashWindowEx = moduser32.NewProc("FlashWindowEx")
|
||||
|
||||
procSetMenuItemBitmaps = moduser32.NewProc("SetMenuItemBitmaps")
|
||||
|
||||
mainThread HANDLE
|
||||
)
|
||||
|
||||
@@ -190,6 +193,20 @@ func RegisterClassEx(wndClassEx *WNDCLASSEX) ATOM {
|
||||
return ATOM(ret)
|
||||
}
|
||||
|
||||
func SetMenuItemBitmaps(hMenu HMENU, uPosition, uFlags uint32, hBitmapUnchecked HBITMAP, hBitmapChecked HBITMAP) error {
|
||||
ret, _, _ := procSetMenuItemBitmaps.Call(
|
||||
hMenu,
|
||||
uintptr(uPosition),
|
||||
uintptr(uFlags),
|
||||
hBitmapUnchecked,
|
||||
hBitmapChecked)
|
||||
|
||||
if ret == 0 {
|
||||
return windows.GetLastError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDesktopWindow() HWND {
|
||||
ret, _, _ := procGetDesktopWindow.Call()
|
||||
return ret
|
||||
|
||||
Reference in New Issue
Block a user