Dynamic menu handling

A lot of times when you need to create a menu at runtime you don’t know the number of items and those items may require some additional data to be handled correctly like a folder item, window reference etc. And in cases like this a subclass of MenuItem makes sense so you can add properties to the subclass and implement the action event to handle that extra data. This is a common pattern for things like the Window menu, Font menu and Recent items.

But its not the only way to handle a dynamic menu.

Suppose you want to add a menu at runtime that is well known before hand and depending on which user is using your application the menu has different entries ?

In such a case you can define everything ahead of time and it will all behave just like if you had defined it in the IDE menu editor.

The trick is to know that the “AutoEnable” setting for menus is dependent on the NAME of the menu item, not the text it contains.

Lets see how we can take advantage of this fact.

Create a new desktop application. In Window1’s open event we’re going to add a menu, and some items to that menu.

Add the following code to the Open event


Dim SelfMenu As New MenuItem("Self Service")
Self.MenuBar.Append SelfMenu
SelfMenu.Enabled = True

Dim fMenu As MenuItem

fMenu = New MenuItem("selfchgpswd")
fMenu.Name = "selfchgpswd"
fMenu.Text = "Change Password"
fMenu.AutoEnable = True
SelfMenu.Append(fMenu)

Note that we have set the name. THIS is crucial. And each item added should have a unique name otherwise you can end up with odd problems.

If you run at this point you should see the menu is added but the items are not enabled. But note that we DID set the AutoEnable property. The Xojo runtime will automatically enable this item IF there is a handler for it on our Window.

So lets add a menu handler for selfchgpswd (menu handlers use the NAME to match the menu item with the menu handler to be enabled & executed)

Right click on Window1 in the navigator and select “Add to Window1” and add a menu handler. In the editor field for the menu handler name put in selfchgpswd. You CAN define a menu handler for menu items that do not exist.

In the menu handler simply add

break

And now run.

First you will see that the menu is added AND the item is now enabled ! And if you select that item from the menu you should stop at the break statement – the Code in the menu handler IS being called exactly as we expected.

At the end of the day this gives you a second way to handle menu items that you add and remove at runtime and, in this case, you know beforehand and can add the code just like you would have if you had defined the menu in the Menu editor.

Enjoy !

Much ado about bevel buttons

One of the things that transitioning from the old Classic framework, which was based on Carbon and HIToolbox, to Cocoa brought about was changes to lots of controls. Bevel buttons fall into that boat and now with the support of Dark Mode they look awful – because bevel buttons are based on an old style control that has no modern equivalent in 64 bit Cocoa.

And even worse you have no way to force them to be dark mode compliant – again because the control they are based on doesn’t have a modern equivalent. I’m working on a project that used them in various ways and updating everything to be dark mode compliant was darned hard when you have such controls in use.

Now its easy to say “Oh well Xojo should just update that to whatever is the modern equivalent”. And there’s the problem. There isn’t one that matches all the things that bevel button can do. So Xojo could update to some modern style on macOS but that would silently break code. Or, as they have done so far, do nothing and encourage people to use some other control. That’s not always practical advice either. For this project I’m working on we’ve taken a different path and actually made our own replacement. It supports dark mode. And menus and icons and most everything the existing bevel button does. We’ve left out things that arent used but they would not be hard to add.

And now the entire project is moving along, is looking great in macOS dark mode, and we have a nice cross platform bevel button that we can continue to use.

With that said here’s the replacement we made. It also happens to include some code for Semantic Colors. The project uses the MBS plugins based code so the non-MBS based code may, or may not be, suitable for your uses.

If you ever need to crash

No. Not at my place 😛

Sometimes you NEED to test your app and whatever remediation you have put in place and need a reliable way to make your app crash

Here it is

Dim p As ptr
p.Byte(0) = 123

Those two lines. Insert them anywhere you might put normal code and book your app will crash.

Let the compiler help you

An interesting question posed by a relatively new user came up on the forums. It had to do with how the Array() function worked and how he could force it to return an array of variants to call his method.

His method was defined like

BuildMenu( base as menuitem, params() as variant)

And he would call it like

Result = BuildMenu (base, Array ("Run", False, "Edit", "Rename", "Delete") )

and this would build up a menu from the base item and append menu items to it. Those new menu items were configured by the array of variants

‘False’ means the previous menu item is disabled (‘True’ means it is enabled, but is optional and assumed)

Because the boolean elements are optional, it is possible to make a call using only string elements, such as:

Result = BuildMenu (base, Array (“Run”, “Edit”, “Rename”, “Delete”) )

But when I do this, I get the following compile error:

So he’s trying to figure out how to force Array to return variants ALL the time. Which is the wrong question to ask in many ways.

Using an array of variants means the compiler cannot help you out and tell you when you are trying to assign the wrong kind of value to another.

Normally if you did something like

dim foo as integer
foo = "123"

the compiler will warn you about this. But if you change this simple code to :

dim foo as variant
foo = "123

you will no longer get a compile error. And depending on how you use “foo” you may, or may not, get some kind of runtime error.

This is why some of the long time users of Xojo suggest avoiding variants unless you absolutely cannot do something any other way.

Variants make it possible to bypass and avoid type checking by the compiler. What this means is that by using a variant you are possibly only finding out about errors at runtime because the compiler cannot do its normal helpful type checking to make sure you catch errors early on.

So in this particular thread on the forums the right question isn’t “how to force the array() function to always return a variant array” but “how to design this particular API so variants are not required”. And that is certainly possible since Xojo supports variable argument lists, optional parameters and other features that retain strong type checking at compile time AND still allow for a good degree of flexibility in designing an API.

And I made a suggestion on that thread :

Result = BuildMenu (base, BuildItem( “Run”, false), BuildItem(“edit”), BuildItem(“Rename”), BuildItem(“Delete”))
with buildmenu defined as

Sub BuildMenu(base as menuitem, paramarray items as menuitem)
// just append the items passed to the base
for i as integer = 0 to items.ubound
base.append items(i)
next
end sub

and BuildItem as

Function BuildItem(name as string, enabled as boolean = false)
dim item as new menuitem(name)
item.enabled = enabled
return item
end function

Avoid variants where you can and design an API that permits the compiler to help you out at compile time. The earlier you catch possible bugs the easier, and less costly, they are to fix.

The compiler can help you IF you let it.

Speed tip with popupmenus

In a different forum a user reminded me of a speed tip that had cropped up once or twice over the years. It has to do with popupmenus.

Basically on older versions of macOS if you did


PopupMenu1.ListIndex = -1
For i As Integer = 0 To 1000
  PopupMenu1.AddRow Str(i)
Next

This behaved not so badly although there is a slightly faster variation

But on Mojave this is noticeably slow. This simple loop could take as much as a second on Mojave in dark mode.

To speed it up it was discovered that if you added an item in position(0), set the listindex to 0 then added all the items and removed that extraneous item at position 0 then the speed was hardly impacted at all.

PopupMenu2.AddRow ""
PopupMenu2.ListIndex = 0
For i As Integer = 0 To 1000
  PopupMenu2.AddRow Str(i)
Next
PopupMenu2.RemoveRow 0

My testing on 10.12.6 and 10.14.5, in light and dark modes, using 2019r1.1 with the following code shows the differences in the debugger

Sub Action()
  Dim startTime1 As Double 
  Dim endTime1 As Double
  
  Dim startTime2 As Double 
  Dim endTime2 As Double
  
  
  startTime1 = Microseconds
  PopupMenu1.ListIndex = -1
  For i As Integer = 0 To 1000
    PopupMenu1.AddRow Str(i)
  Next
  endTime1 = Microseconds
  
  
  startTime2 = Microseconds
  PopupMenu2.AddRow ""
  PopupMenu2.ListIndex = 0
  For i As Integer = 0 To 1000
    PopupMenu2.AddRow Str(i)
  Next
  PopupMenu2.RemoveRow 0
  endTime2 = Microseconds
  
  
  Dim total1 As Double = (endtime1 - startTime1) / 1000000
  Dim total2 As Double = (endtime2 - startTime2) / 1000000
  
  label1.text = str(total1, "###.00000")
  label2.text = str(total2, "###.00000")

End Sub

And the results are surprising and consistent between running in the debugger & a built application

Debug              Total1 (slow)       Total2 (fast)
 10.12.6            0.037 sec            0.012 sec
 10.14.5 (light)    1.152 sec            0.019 sec
 10.14.5 (dark)     1.158 sec            0.019 sec 
 Built x86_64       Total1 (slow)       Total2 (fast)
 10.12.6            0.036 sec            0.010 sec
 10.14.5 (light)    1.174 sec            0.017 sec
 10.14.5 (dark)     1.174 sec            0.028 sec

Whatever changed in Mojave Popupmenus got very slow.

Heads up.

Thanks to Jim McKay for reporting this

A new take on listbox resizing

Normally a listbox doesnt support much in the way of live resizing of rows or columns if you dont have a header row.

Turns out that for something else I’m working on I needed to be able to resize things and NOT have a header row – and also make it possible to resize rows as well by clicking & dragging.

And the result so far is this

Live resizing by clicking & moving the grids themselves !

There is no header on this listbox either.

I’ll see if I can release this as a stand alone item.

Fun things to do with Xcode

I know a lot of you don’t want to mess with Xcode BUT there are some really handy things it can help you with and you don’t have to write any Swift or Objective-C code.

Since you can drop a plist into Xojo to supplement or replace certain keys at some point you may need to create one. Xcode has an excellent editor for plists.

If you start Xcode and select File > New > File and scroll down there is a section that lists various kinds of files that you can edit. In this list is a Plist.

Select that template and edit and you have a plist ready to add to your Xojo project.

But thats only the beginning.

Perhaps the handiest things Xcode can do is examine the view hierarchy of ANY application. In Cocoa every control is a kind of “view” and your entire UI is composed of multiple views one on top of the other and composited together.

If you’re having issues getting things to appear correctly this can be very handy to know whats above or below what in the entire view hierarchy that macOS uses to create what you see on screen.

We’ll work thought a quick example.

Start Xcode. Start a new empty macOS project. Use the Cocoa App template. It doesn’t matter what you call it just that Xcode wont work for this without an open project. Save that project in a new folder on your desktop since we’re going to throw it away when done.

Start Activity Monitor so you can get the the PID of your running application. I’m going to select Xojo since everyone reading this should have a copy.

I happened to show the Memory usage but other views like CPU time also have a PID column. The number in that column is what you need.

Now back to Xcode.

Select Debug > Attach to Process by PID or Name. A small panel should appear and in here you can put the PID from Activity Monitor. Nothing else will appear to change.

However there are new options available.

Select Debug > View Debugging > Capture View Hierarchy. Once you do that you get what appears to be a 2-D wire frame drawing of the user interface views. However, this is NOT just a 2D view. You can slick and drag in the view area and Xcode will reveal the various layers used to composite the image you see on screen.

There are scrollers on the bottom left to adjust the separation of the layers so its easier to see them. You can, in this view, see what is in front of or behind something else and that can help you resolve weird drawing issues and may help reveal how other applications achieve specific affects like the translucency seen in the Finders left hand navigation panes.

As well in this view you can right click on any view and select the “Print description of” menu item. This wont always tell you something really useful in the lower panes. But it does tell you what Cocoa UI element an object is in case you need to use declares to manipulate it.

There are lots of other things you can do in Xcode once you have attached to a process to examine it. Observing the view hierarchy is just one really useful one.

Enjoy !

Keep your code off of my controls

Suppose you are creating a new container control to encapsulate some functionality onto this new control. You’re already thinking about “encapsulation” which is a good thing.

Why, when you create this new container, would you then make all the controls on it public so code outside the container can change it ? That seems like it would be a good way to ruin the encapsulation you’re trying to achieve.

And, if you do this, any changes to your nicely set up container then may require broader changes in your application if, or when, you make changes to your container.

Lets walk through a quick and simple example that illustrates the issue.

Start a new desktop project.

Add a container control to the project – it will cleverly be named ContainerControl1.

Add a Generic Button to this container. It will be called “PushButton1”.

Make sure its scope is “Public”

Now add an instance of this container to Window1.

Add the Open Event to Window1.

In there put

ContainerControl11.Pushbutton1.enabled = false

And when you run the pushbutton on the container will indeed show up as disabled.

Now imagine you add a lot of other functionality to your application and there are several places where your code enables or disables this button under various conditions. And perhaps you reuse this container on several other windows.

And then you decide to make some changes to the container. And one of those changes is to rename the button to something more meaningful.

In the project we’ve built so far this isnt so bad. If we rename PushButton1 to “ContainerButton” and try to run again we’ll get an error saying

Window1.Open, line 1
Type "ContainerControl1.ContainerControl1" has no member named "PushButton1"
ContainerControl11.PushButton1.Enabled = false

Now in a small application this is manageable. You simply hunt for and change the names and off you go.

But, you may have to do this many times depending on how many times you set the enabled state of the button. And you have to update them all.

And none of this was necessary.

If instead of letting code outside the container reach in and manipulate the control directly we provided an API to do this and hid the innards then we could be sure we have fewer places to fix and that no code outside the container isnt somehow changing the setup in some way that will cause other issues.

In the example lets do this.

In ContainerControl1 add a method

SetButtonEnabled(assigns isEnabled as boolean)
  ContainerButton.enabled = isEnabled
End Sub
  

Make the container buttons scope Private.

Now if you run you will get an error in the Window1.Open event saying

Window1.Open, line 1
Type "ContainerControl1.ContainerControl1" has no member named "Pushbutton1"
ContainerControl11.Pushbutton1.enabled = False

This is because the push button is now private and hidden to code outside the container control.

So we’ll fix this one line to

ContainerControl11.SetButtonEnabled = False

Now if you run the button is once more disabled. But now when we make changes to the ContainerControl we can focus on the container control and the methods it exposes to the rest of our code and not have to worry that we will have to fix a lot of other code elsewhere in our application.

Lets try this out.

Open the container and change the buttons name to “foobar”. And change our method we had for setting the enabled state to

foobar.enabled = isEnabled

And run the sample again.

We made two small changes and no other code had to change. If we had used this container many many times the time savings could have been significant. And the chance for odd bugs caused by code outside the container altering the containers contents significantly reduced.

Make your controls private and expose an API for them and save yourself a lot of grief.

Picking a good name

Not for your children (although that is important too since kids can be incredibly harsh and will pick on kids with names that they think sound different or odd)

This is to do with naming methods, properties, and just about everything else in your Xojo projects.

A good name indicates a number of things to anyone who reads the code. Purpose is the most obvious.

If all you had was a lot of methods named “foo” or “untitled” your application would be incredibly hard to figure out and maintain for very long.

So having a method named

SplitPropertyValuesIntoDictionary(jsonSource as JSonItem) as Dictionary

Gives you a decent idea what this method will do just by reading the full description and the names of the parameters.

Events have similar naming issues. They should convey what is happening when they are raised. “Action” has never been a great name for the event that is raised by a pushbutton when it is clicked on. Its not entirely horrid but it could be better.

Pushed() 

might be better since that is what has occurred in a push button.

Some events happen DURING some other action. For instance when an expandable row in a listbox is clicked on you currently get a “ExpandRow” event – which might mislead you into thinking you have to write some code to actually DO the expansion like setting the rows expanded property to true as well as other code. Again the name isnt horrible but it could be better.

RowExpanding()

more closely reflects when the event is being raised; during the rows normal course of expanding. Again though this doesn’t convey much information about what is expected. Can you cancel the row from being expanded ? Without reading the documentation this name doesn’t tell you much.

AddRowsToRowExpanding()

might be even better as it tells you exactly what this event is for.

Now I know some will say “OMG thats so much typing” – yes – if you type every last thing out by hand. Learn to use Autocomplete and it will help you immensely and may even help you find other useful properties, methods etc that you may not know existed.

The better you pick names the more easily your code documents itself. I’m not advocating writing no comments in code but they should be about the general process being performed not the mechanics (unless the mechanics are really hairy)

see this excellent post about this subject