Keya has demonstrated above that GTK on
Linux (and hence PureBasic) is not able to display both an icon and a checkmark in a menu entry.
MacOS displays both a checkmark and an icon in a menu entry:
Windows 7 displays an icon overlayed with a checkmark:
So it's a mess when trying to program a cross-platform solution with menu items containing both an icon and a checkmark which should look as uniform as possible on all 3 operating systems (and it's currently not possible at all in Linux...
)
Therefore I rolled up my own solution which uses the PureBasic native way on Windows and separate API solutions for Linux and MacOS (by using a checkmark generated by vector graphics commands which looks very similar to the Windows checkmark and which is overlayed over the icon). So now the Linux and MacOS menu entries look as similar as possible to the Windows ones:
Linux:
MacOS:
The MacOS solution even removes the now unnecessary separate checkmark column with an API command which otherwise would contain a strange looking leading empty space.
I have tested my code example successfully on these operating systems:
- Linux Mint 18 'Sarah' x64 Cinnamon with GTK 3 and PB 5.60 x64
- MacOS 10.6.8 'Snow Leopard' with PB 5.60 x86
- MacOS 10.12.4 'Sierra' with PB 5.60 x86 and x64
- Windows 7 SP1 x64 with PB 5.60 x86 and x64
My example is different from Keya's because you are able to toggle the checkmark in each menu entry (in Keya's example the checkmark is toggled between the two different entries).
Code: Select all
EnableExplicit
Define ImageIDGreen.I
Define ImageIDPink.I
Define SelectedMenuItem.I
CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
Structure MenuItemDataEntry
MenuItemID.I
IsChecked.I
ImageIDChecked.I
ImageIDUnchecked.I
ItemText.S
EndStructure
Define i.I
Define ImageID.I
Define ImageIDCheckmark.I
Define MenuItem.I
Dim MenuItemData.MenuItemDataEntry(0)
Procedure.I CreateVectorCheckmark(ImageID.I)
Protected Result.I
Result = CreateImage(ImageID, 16, 16, 32, #PB_Image_Transparent)
If Result
If ImageID = #PB_Any
ImageID = Result
EndIf
If StartVectorDrawing(ImageVectorOutput(ImageID, #PB_Unit_Pixel)) = 0
FreeImage(ImageID)
ProcedureReturn 0
EndIf
VectorSourceColor($FF000000)
MovePathCursor(5, 7)
AddPathLine(7, 10)
AddPathLine(12, 5)
StrokePath(1.5)
StopVectorDrawing()
ProcedureReturn ImageID
EndIf
EndProcedure
Procedure.I GetMenuItem(Menu.I, ItemText.S)
Protected i.I
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Linux ; ---------------------------------------------
Protected Child.I
Protected ChildrenCount.I
Protected ChildrenList.I
Protected MenuItem.I
Protected WidgetName.S
ChildrenList = gtk_container_get_children_(Menu)
ChildrenCount = g_list_length_(ChildrenList)
If ChildrenCount > 0
For i = 0 To ChildrenCount - 1
Child = g_list_nth_data_(ChildrenList, i)
WidgetName = PeekS(gtk_widget_get_name_(Child), -1, #PB_UTF8)
Select WidgetName
Case "GtkMenuItem", "GtkImageMenuItem"
MenuItem = GetMenuItem(Child, ItemText)
If MenuItem <> 0
Break
Else
Menu = gtk_menu_item_get_submenu_(Child)
If Menu
MenuItem = GetMenuItem(Menu, ItemText)
If MenuItem <> 0
Break
EndIf
EndIf
EndIf
Case "GtkAccelLabel"
If ItemText = PeekS(gtk_label_get_text_(Child), -1, #PB_UTF8)
MenuItem = gtk_widget_get_parent_(Child)
Break
EndIf
EndSelect
Next i
g_list_free_(ChildrenList)
EndIf
ProcedureReturn MenuItem
CompilerCase #PB_OS_MacOS ; ---------------------------------------------
Static IterationCount.I
Static MenuItem.I
Protected ItemCount.I
If PeekS(CocoaMessage(0, CocoaMessage(0, Menu, "className"),
"UTF8String"), -1, #PB_UTF8) = "NSMenu"
ItemCount = CocoaMessage(0, Menu, "numberOfItems")
Else
ItemCount = CocoaMessage(0, Menu, "count")
EndIf
For i = 0 To ItemCount - 1
If IterationCount = 0
MenuItem = CocoaMessage(0, Menu, "objectAtIndex:", i)
Else
MenuItem = CocoaMessage(0, Menu, "itemAtIndex:", i)
EndIf
If ItemText = PeekS(CocoaMessage(0,
CocoaMessage(0, MenuItem, "title"), "UTF8String"), -1, #PB_UTF8)
ProcedureReturn
EndIf
If IterationCount = 0
IterationCount + 1
GetMenuItem(MenuItem, ItemText)
Else
If CocoaMessage(0, MenuItem, "isSeparatorItem") = #False
If CocoaMessage(0, MenuItem, "hasSubmenu")
IterationCount + 1
GetMenuItem(CocoaMessage(0, MenuItem, "submenu"), ItemText)
EndIf
EndIf
EndIf
Next i
IterationCount - 1
ProcedureReturn MenuItem
CompilerEndSelect
EndProcedure
ImageIDCheckmark = CreateVectorCheckmark(#PB_Any)
CompilerEndIf
Procedure MenuItemEx(MenuItemID.I, ItemText.S, ImageID.I)
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
MenuItem(MenuItemID, ItemText, ImageID(ImageID))
CompilerElse
Shared MenuItemData.MenuItemDataEntry()
Shared ImageIDCheckmark.I
Static IsArrayInitialized.I
Protected MenuItemCount.I
MenuItem(MenuItemID, ItemText, ImageID(ImageID))
MenuItemCount = ArraySize(MenuItemData())
If IsArrayInitialized = #True
MenuItemCount + 1
ReDim MenuItemData(MenuItemCount)
Else
IsArrayInitialized = #True
EndIf
MenuItemData(MenuItemCount)\MenuItemID = MenuItemID
MenuItemData(MenuItemCount)\ItemText = ItemText
MenuItemData(MenuItemCount)\ImageIDUnchecked = ImageID
MenuItemData(MenuItemCount)\ImageIDChecked = CopyImage(ImageID, #PB_Any)
If StartDrawing(ImageOutput(MenuItemData(MenuItemCount)\ImageIDChecked))
DrawAlphaImage(ImageID(ImageIDCheckmark), 0, 0, 255)
StopDrawing()
EndIf
CompilerEndIf
EndProcedure
ImageIDPink = CreateImage(#PB_Any, 16, 16, 24, $FF00FF) ; Icon 16x16 Pink
ImageIDGreen = CreateImage(#PB_Any, 16, 16, 24, $00FF00) ; Icon 16x16 Green
OpenWindow(0, 270, 100, 350, 100, "Menu with icon and overlayed checkmark")
If CreateImageMenu(0, WindowID(0))
MenuTitle("Checkmark-Demo")
MenuItemEx(0, "Option 1 (Click to toggle checkmark)", ImageIDPink)
MenuItemEx(1, "Option 2 (Click to toggle checkmark)", ImageIDGreen)
EndIf
CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
; ----- Remove separate checkmark column
Define MenuTitle.I
MenuTitle = CocoaMessage(0, MenuID(0), "objectAtIndex:", 0)
CocoaMessage(0, MenuTitle, "setShowsStateColumn:", #NO)
CompilerEndIf
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
Case #PB_Event_Menu
SelectedMenuItem = EventMenu()
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
SetMenuItemState(0, SelectedMenuItem,
GetMenuItemState(0, SelectedMenuItem) ! 1)
CompilerElse
For i = 0 To ArraySize(MenuItemData())
If MenuItemData(i)\MenuItemID = SelectedMenuItem
If MenuItemData(i)\IsChecked
MenuItemData(i)\IsChecked = #False
ImageID = MenuItemData(i)\ImageIDUnchecked
Else
MenuItemData(i)\IsChecked = #True
ImageID = MenuItemData(i)\ImageIDChecked
EndIf
MenuItem = GetMenuItem(MenuID(0), MenuItemData(i)\ItemText)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Linux
gtk_image_menu_item_set_image_(MenuItem,
gtk_image_new_from_pixbuf_(ImageID(ImageID)))
CompilerCase #PB_OS_MacOS
CocoaMessage(0, MenuItem, "setImage:", ImageID(ImageID))
CompilerEndSelect
Break
EndIf
Next i
CompilerEndIf
EndSelect
ForEver