[Openmcl-devel] Nibs not being loaded with build-application

Paul Krueger plkrueger at comcast.net
Fri Mar 7 08:42:29 PST 2014

On Mar 6, 2014, at 4:29 PM, Michael Minerva <minerva at agentsheets.com> wrote:

> Hey Paul,
> I've been looking through your contributions but haven't found anything about setting up a main menu for the app. Do you have anything about that in your contribs?
> Thanks,
> --Mike


There is quite a bit of support for making menus and menu items. The actual setting of a main menu is a simple call to an appropriate objective-c function as illustrated below. It would be trivial to wrap that in a lisp function as well. First make sure that you have the latest version of my contrib that was checked in somewhere around the middle of 2013. It is WAY different from what was there previously.

In the source file …krueger/InterfaceProjects/Utilities/menu-utils.lisp you'll find a variety of functions and macros that let you create either standard versions of menus or custom menus of your own or combinations. Those that provide standard menus are keyword-driven. For example the function that you can call to create a default main menu is defined in that file as:

(defun standard-main-menu ()
  ;; Makes and returns a default main menu matching Apple guidelines as of March 2013.
  ;; The caller may modify as desired (either the code or the returned menu).
  ;; If you intend to use the services menu you must call (#/setServicesMenu: #&NSApp services-menu)
  (make-menu "" :app :file :edit :window :help))

Below is an example of how this would be called.

You can check out how the make-menu macro is defined in that same file. It recognizes keywords corresponding to pretty much every standard and optional menu that is discussed in the Apple documentation. You can, of course, create your own menu-item objects as well and use them as arguments in place of, or in addition to, keyword arguments.

One of the other things I did in my contrib is define a large number of initialize-instance :after methods for a whole raft of Objective-C classes including menu-itmes. That lets you create instances of them using pretty much standard Lisp syntax for making instances and using keyword arguments to configure the objects. That avoids having to create Objective-C instances and then having to call a bunch of #/set… methods to initialize them as you want. That also let me do all sorts of type conversion from Lisp to Objective-C in one place (the :after methods) so that I don't have to worry about doing that every time I want to create a new instance of a commonly used Objective-C class. All of those methods are defined in objc-initialize.lisp.  I tried to make all the keywords conform to Apple's documentation, but if you're wondering what keyword arguments are available for any given class of object I've even created an automatic documentation system for that which will tell you what keywords are available for any given class (including those inherited from super-classes). Loading this facility into the CCL IDE is described in the tutorial and also below.

My development support code permits setting the main menu while you're testing within the IDE. You can toggle back and forth between CCL IDE menus and your application menus or even see both sets at the same time if you like. There are other miscellaneous functions available to modify existing menus on the fly while debugging.

If you're going to go nibless, then you need to install a main menu within some code executed at application startup time. Examples of that are discussed in "CCL Developer Tools Tutorial.pdf", but it can be pretty simple. For example, the code that initializes my stand-alone loan computation example app looks like:

(defun init-loan-app (app)
;; The function that gets called to initialize the application
;; It creates and installs a main menu.
  (let ((*app-name-for-menus* "Loan"))
    (#/setMainMenu: app (standard-main-menu))

My other support code for initializing applications would call this with the app argument set to the NSApplication object at startup time. See the tutorials for examples of how this all fits together. Suppose you wanted a more-or-less standard main menu, but wanted to add a single custom menu-item to the file menu between "close" and "close all". You might have something that looks more like:
    (let* ((*app-name-for-menus* "Your App Name")
           (new-menu-item (make-instance 'ns:ns-menu-item :title "My Item" <other keyword args as needed>))
	   (custom-file-menu (make-menu "File" :new :open :sep :close 
                                               :close-all :save :duplicate :revert-to-saved :sep :print))
           (main (make-menu "" :app custom-file-menu :edit :window :help)))
       (#/setMainMenu: app main))

There are a substantial number of dependencies between different parts of my contrib code and the easiest thing to do is to just load all of the main code together. I worked very hard to make sure that this causes no problems for the IDE environment and makes no alteration to it unless and until you make an explicit call asking for it to do so (like installing a menu or something). The best way to load all the commonly used code is to:
	(require :objc-initialize)
as that will then require everything else in the proper sequence to avoid problems. It's possible to pick and choose a smaller set and I've tried hard to make sure that always works ok, so if you do that and encounter dependency problems let me know and I'll try to resolve that.

If you want to load all of my code that supports developing stand-alone apps then you can add:
	(require :install-app-tools)
to your ccl-ide-init.lisp file and that will bring in everything as well. In the process of writing this I realized that there is something missing in the install-app-tools.lisp file. Eventually I'll check in a change to that, but for now if you want to use it, change the eval-when clause at the beginning of that file to be as follows:
	(eval-when (:compile-toplevel :load-toplevel :execute)
           (require :lisp-app-doc)
           (require :dev-tools-interface))
The second require (missing in what is currently checked in) is needed to extend the CCL "Tools" menu to include a selection for "Class Keywords and Binding Targets". This is part of an automatic documentation system that I implemented to show all the available keywords that were defined for all those initialize-instance :after methods that I described above. Since I also support binding from Objective-C view attributes to Lisp slots I also found it useful to automatically document all the available binding targets for those classes. If you select this from the tools menu it will pop up a window that has a class browser on the left. You search for or navigate to the class you want and on the right you will see available initialize-instance keywords and binding targets.

There's just really a lot of support available for creating custom stand-alone apps. To go nibless, which I now think is almost mandatory for Lisp developers, you have to be able to create windows that dynamically reconfigure themselves in response to user actions. The Apple constraint layout system makes that much easier. My Lisp interface to that makes life easier yet. That's documented formally toward the end of the UserInterfaceTutorial.pdf document and several examples of its use are scattered throughout that document. I also have support for document-based apps (saving, printing, etc.).

Enjoy and feel free to ask questions if you run into anything that isn't addressed in the tutorials.

More information about the Openmcl-devel mailing list