Skip to content

Commit

Permalink
add an API to easily build Mac Os menus
Browse files Browse the repository at this point in the history
  • Loading branch information
demarey committed Apr 16, 2024
1 parent 1039792 commit f4a3560
Show file tree
Hide file tree
Showing 7 changed files with 307 additions and 41 deletions.
102 changes: 75 additions & 27 deletions src/ObjectiveC-Cocoa/CocoaMenu.class.st
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
"
I represent an item of a Cocoa Menu.
I can hold items or submenus.
"
Class {
#name : #CocoaMenu,
#superclass : #Object,
#instVars : [
'nsMenu',
'items',
'title',
'nsTitle',
'items',
'menuItem'
],
#classVars : [
Expand All @@ -14,15 +18,23 @@ Class {
#category : #'ObjectiveC-Cocoa-Menus'
}

{ #category : #'instance creation' }
CocoaMenu class >> with: aNSMenu [

^ self new
setMenu: aNSMenu;
yourself
]

{ #category : #adding }
CocoaMenu >> addItemWithTitle: aString action: aFullBlockClosure [
CocoaMenu >> addItemWithTitle: aString action: aBlock [

^ self addItemWithTitle: aString action: aFullBlockClosure shortcut: ''
^ self addItemWithTitle: aString action: aBlock shortcut: ''
]

{ #category : #adding }
CocoaMenu >> addItemWithTitle: aTitle action: actionBlock shortcut: shortcutString [

items add: (CocoaMenuItem new
title: aTitle;
action: actionBlock;
Expand All @@ -31,60 +43,96 @@ CocoaMenu >> addItemWithTitle: aTitle action: actionBlock shortcut: shortcutStri
]

{ #category : #adding }
CocoaMenu >> addSubmenu: aTitle with: builderBlock [

| m |
m := self class new.
m title: aTitle.
builderBlock value: m.
items add: m.
^ m
CocoaMenu >> addSeparator [

items add: CocoaMenuSeparator new
]

{ #category : #adding }
CocoaMenu >> addToMenu: aCocoaMenu [
CocoaMenu >> addServicesMenu [

items add: CocoaServicesMenu new
]

{ #category : #adding }
CocoaMenu >> addSubmenu: aTitle with: builderBlock [

| menu |
menu := self class new.
menu title: aTitle.
builderBlock value: menu.
items add: menu.
^ menu
]

{ #category : #private }
CocoaMenu >> addToMenu: aCocoaMenu [
<ignoreNotImplementedSelectors: #(#addItemWithTitle:action:keyEquivalent: #setSubmenu:forItem:)>

self buildNSMenu.

menuItem := aCocoaMenu nsMenu addItemWithTitle: nsTitle action: ObjCObject nil keyEquivalent:'' asNSString.
menuItem := aCocoaMenu nsMenu
addItemWithTitle: nsTitle
action: ObjCObject nil
keyEquivalent: '' asNSString.

aCocoaMenu nsMenu setSubmenu: nsMenu forItem: menuItem.

]

{ #category : #building }
{ #category : #configuring }
CocoaMenu >> beMainMenu [

self buildNSMenu.
#NSApplication inObjC sharedApplication setMainMenu: nsMenu.
MainMenu := self
]

{ #category : #private }
CocoaMenu >> buildNSMenu [
<ignoreNotImplementedSelectors: #(#initWithTitle:)>

nsTitle := title asNSString.
nsTitle := (title ifNil: [ '' ]) asNSString.
nsMenu := #NSMenu inObjC alloc initWithTitle: nsTitle.

items do: [ :i | i addToMenu: self ].

items do: [ :item | item addToMenu: self ].
^ nsMenu
]

{ #category : #initialization }
CocoaMenu >> initialize [

super initialize.
items := OrderedCollection new
]

{ #category : #accessing }
CocoaMenu >> nsMenu [
CocoaMenu >> nsMenu [

^ nsMenu
]

{ #category : #initialization }
CocoaMenu >> setAsMainMenu [
{ #category : #printing }
CocoaMenu >> printOn: aStream [
aStream << self class name << '(' << (title ifNil: [ '' ]) << ')'
]

{ #category : #removing }
CocoaMenu >> removeAllItems [

nsMenu removeAllItems.

self buildNSMenu.
#NSApplication inObjC sharedApplication setMainMenu: nsMenu.
self flag: 'TODO: release ObjC menu items'.
items := OrderedCollection new.

]

MainMenu := self
{ #category : #private }
CocoaMenu >> setMenu: aNSMenu [

nsMenu := aNSMenu
]

{ #category : #accessing }
CocoaMenu >> title: aString [

title := aString
]
33 changes: 23 additions & 10 deletions src/ObjectiveC-Cocoa/CocoaMenuItem.class.st
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
"
I represent an item of a Cocoa menu.
I can hold menu items or submenus.
"
Class {
#name : #CocoaMenuItem,
#superclass : #Object,
#instVars : [
'title',
'action',
'shortcut',
'title',
'target',
'nsTitle',
'nsShortcut',
Expand All @@ -14,31 +18,40 @@ Class {
}

{ #category : #accessing }
CocoaMenuItem >> action: aFullBlockClosure [
action := aFullBlockClosure
CocoaMenuItem >> action: aBlock [

action := aBlock
]

{ #category : #adding }
CocoaMenuItem >> addToMenu: aCocoaMenu [

target := CocoaMenuTarget new block: action; yourself.
ObjCProxyClass newFor: target.
CocoaMenuItem >> addToMenu: aCocoaMenu [
<ignoreNotImplementedSelectors: #(#addItemWithTitle:action:keyEquivalent: #setTarget: #setEnabled:)>

nsTitle := title asNSString.
nsShortcut := shortcut asNSString.

menuItem := aCocoaMenu nsMenu addItemWithTitle: nsTitle action: #execute asObjCSelector keyEquivalent: nsShortcut.

menuItem := aCocoaMenu nsMenu
addItemWithTitle: nsTitle
action: #execute asObjCSelector
keyEquivalent: nsShortcut.

target := CocoaMenuTarget new
block: action;
yourself.
ObjCProxyClass newFor: target.
menuItem setTarget: target.
menuItem setEnabled: true.
menuItem setEnabled: true

]

{ #category : #accessing }
CocoaMenuItem >> shortcut: aString [

shortcut := aString
]

{ #category : #accessing }
CocoaMenuItem >> title: aString [

title := aString
]
17 changes: 17 additions & 0 deletions src/ObjectiveC-Cocoa/CocoaMenuSeparator.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"
A Cocoa menu separator. I'm also a menu item.
"
Class {
#name : #CocoaMenuSeparator,
#superclass : #Object,
#category : #'ObjectiveC-Cocoa-Menus'
}

{ #category : #adding }
CocoaMenuSeparator >> addToMenu: aCocoaMenu [
<ignoreNotImplementedSelectors: #(#separatorItem)>

| separator |
separator := #NSMenuItem inObjC separatorItem.
aCocoaMenu nsMenu addItem: separator
]
5 changes: 4 additions & 1 deletion src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"
I represent a Cocoa menu target, i.e. the ObjectiveC object that will receive the callbacks.
"
Class {
#name : #CocoaMenuTarget,
#superclass : #Object,
Expand All @@ -20,7 +23,7 @@ CocoaMenuTarget >> block: anObject [
block := anObject
]

{ #category : #accessing }
{ #category : #execution }
CocoaMenuTarget >> execute [
<objCSignature: #(void ())>

Expand Down
42 changes: 42 additions & 0 deletions src/ObjectiveC-Cocoa/CocoaServicesMenu.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"
I represent the Mac os ""Services"" menu.
You can use me to add a ""Services"" entry to your app menu.
"
Class {
#name : #CocoaServicesMenu,
#superclass : #CocoaMenu,
#category : #'ObjectiveC-Cocoa-Menus'
}

{ #category : #configuring }
CocoaServicesMenu >> beServicesMenu [
<ignoreNotImplementedSelectors: #(#setServicesMenu:)>

#NSApplication inObjC sharedApplication setServicesMenu: nsMenu
]

{ #category : #private }
CocoaServicesMenu >> buildNSMenu [

nsMenu := self servicesMenu.
nsMenu isNull
ifTrue: [
super buildNSMenu.
self beServicesMenu ]
ifFalse: [ nsTitle := title asNSString ].
^ nsMenu
]

{ #category : #initialization }
CocoaServicesMenu >> initialize [

super initialize.
title := 'Services'
]

{ #category : #accessing }
CocoaServicesMenu >> servicesMenu [
"Get the Application services menu if set or null"

^ #NSApplication inObjC sharedApplication servicesMenu
]
14 changes: 11 additions & 3 deletions src/ObjectiveC-Cocoa/String.extension.st
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ Extension { #name : #String }

{ #category : #'*ObjectiveC-Cocoa' }
String >> asNSString [
^ self
ifNotEmpty: [ #NSString inObjC alloc initWithUTF8String: self ]
ifEmpty: [ #NSString inObjC string ]

<ignoreNotImplementedSelectors: #( #initWithUTF8String: )>
| encoded param |
encoded := self utf8Encoded.
param := ByteArray new: encoded size + 1.
param pinInMemory.

LibC memCopy: encoded to: param size: encoded size.
param at: encoded size + 1 put: 0.

^ #NSString inObjC alloc initWithUTF8String: param
]
Loading

0 comments on commit f4a3560

Please sign in to comment.