added section on user prefs

master
Claire 2 years ago
parent
commit
0c945c40c4
  1. 5
      Home.md
  2. 1
      Swift-Windows.md
  3. 243
      User-Prefs.md

5
Home.md

@ -3,6 +3,7 @@
## Sections
* The main application (this page)
* [Windows with Swift](/wiki/Swift-Windows)
* [Setting (and retrieving) user preferences](/wiki/User-Prefs)
## Introduction
I recently picked up a very cheap MacBook Air circa 2015. I'm disinterested in running any version of MacOS beyond 10.14, and as time goes on and more and more popular apps drop support for Mojave, I figured I might try my hand at writing apps for my Mac.
@ -50,7 +51,9 @@ To double-check the results of this command, you can run:
xattr -lr .
```
If all went well, you *should* get no results. If something went wrong, try again, and maybe try with `sudo`, to circumvent any permissions issues. These extended attributes will prevent codesigning entirely, which is a *mandatory* step to packaging a MacOS application for release. If you get an error like `resource fork, Finder information, or similar detritus not allowed` during archiving your project for release, try using `xattr` to strip your project of any leftover attributes.
If all went well, you *should* get no results. If something went wrong, try again, and maybe try with `sudo`, to circumvent any permissions issues. These extended attributes will prevent codesigning entirely, which is a *mandatory* step to packaging a MacOS application for release.
If you get an error like `resource fork, Finder information, or similar detritus not allowed` during archiving your project for release, try using `xattr` to strip your project of any leftover attributes. Make sure to **clean your build folder** (under the **Product** menu) after running `xattr`, or you'll continue to get build errors.
### Application icon

1
Swift-Windows.md

@ -4,6 +4,7 @@
* [The main application](/wiki/Home)
* Windows with Swift (this page)
* [Setting (and retrieving) user preferences](/wiki/User-Prefs)
## Introduction

243
User-Prefs.md

@ -0,0 +1,243 @@
# Setting (and retrieving) user preferences
## Sections
* [The main application](/wiki/Home)
* [Windows with Swift](/wiki/Swift-Windows)
* Setting (and retrieving) user preferences (this page)
## Introduction
Version 1.4 of LunaMac introduces a user preference, which defines the preferred icon style - default, alternate dark, or emoji. After I finished and released this project, I decided I didn't like the dark mode icons as much, because the white part of the icon is actually the dark area of the moon. I also wanted to add back support for the emoji icons, in case some people prefer that to the monochrome icons.
## Setting a user preference
Handling all this one of the easiest parts of this project, so I can at least give Apple credit for that much. Apple provides a common interface for application preferences, which in MacOS are managed using special XML **plist** files. Instead of having to write these files directly, you can just use [`UserDefaults.standard`](https://developer.apple.com/documentation/foundation/userdefaults/1416603-standard).
```swift
class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
// user settings
let defaults = UserDefaults.standard
}
```
We're going to set a single preference named `iconPref`, which holds a string value. Each value represents an icon theme: `default`, `darkalt`, and `emoji`.
Before we make a submenu for setting the user preference, we're going to make a function that actually handles setting the value in `defaults`.
This function takes a `sender` argument, which is the [`NSMenuItem`](https://developer.apple.com/documentation/appkit/nsmenuitem) the user selects. We can set the preference based on the contents of [`NSMenuItem.title`](https://developer.apple.com/documentation/appkit/nsmenuitem/1514805-title).
Once we set our user preference, we'll run `updateIcon()`, which will rebuild the icon and menu with the new preferred icon set.
```swift
@objc func setPref(sender: NSMenuItem) {
// sender.title tells us what to do
switch (sender.title) {
case "Default" :
defaults.set("default", forKey: "iconPref")
break
case "Alternate Dark" :
defaults.set("darkalt", forKey: "iconPref")
break
case "Emoji" :
defaults.set("emoji", forKey: "iconPref")
break
default:
break
}
// rebuild the menu
updateIcon()
}
```
## Adding new icons
<img src="https://abettergeek.com/_media/wiki/git/lunamac/newimages.png" align="left">
Next, we need to add icons for these new icon sets. As you'll see below, the alternate dark mode icons can be reused in a different order for half of the phases. For the crescent and gibbous phases, I created new icons with better light/dark proportion. These icons use the same filename prefix, with a suffix of `-alt-dark`. The emoji icons, which are PNG representations of Apple's system emoji font, use a suffix of `-emoji`.
Remember to run `xattr -cr .` after adding new images to your project.
<br clear="left"/>
## Creating the submenu
<img src="https://abettergeek.com/_media/wiki/git/lunamac/prefmenu.png" align="right">
Now we need a menu interface, so the user can select their preferred icon set.
Previously, in `buildMenu()`, we built our menu by directly invoking [NSMenu.addItem](https://developer.apple.com/documentation/appkit/nsmenu/1518176-additem). This works, but it doesn't give us access to the more advanced capabilities of [`NSMenu`](https://developer.apple.com/documentation/appkit/nsmenu), like adding submenus. We're going to add a user setting menu item, which points to a submenu of options.
First, we're going to create a new instance of [`NSMenu`](https://developer.apple.com/documentation/appkit/nsmenu), which will serve as a submenu for the **Icon Style** menu item.
<br clear="right"/>
```swift
@objc func buildMenu(key: String = "default") {
...
// create menu item to hold submenu
let prefMenu = NSMenu(title: "Prefs")
...
}
```
Next, we're going to create the three menu items in our submenu using [`NSMenuItem`](https://developer.apple.com/documentation/appkit/nsmenuitem). All three will call our `setPref()` function when clicked.
```swift
@objc func buildMenu(key: String = "default") {
...
// default
let optDefault = NSMenuItem(
title: "Default",
action: #selector(setPref),
keyEquivalent: ""
)
// alternate dark
let optDarkAlt = NSMenuItem(
title: "Alternate Dark",
action: #selector(setPref),
keyEquivalent: ""
)
// emoji
let optEmoji = NSMenuItem(
title: "Emoji",
action: #selector(setPref),
keyEquivalent: ""
)
...
}
```
You may have noticed in my menu screenshot that the selected theme is indicated with a checkmark. This is done by setting [`NSMenuItem.state`](https://developer.apple.com/documentation/appkit/nsmenuitem/1514804-state) to [`NSControl.StateValue.on`](https://developer.apple.com/documentation/appkit/nscontrol/statevalue/2876355-on).
This is accomplished with a basic `switch case`, which sets the `state` value based on the user's icon preference. If no preference exists, the icon preference is set to `default`.
```swift
@objc func buildMenu(key: String = "default") {
...
// make sure the right menu item is checked
switch (defaults.string(forKey: "iconPref")) {
case "default" :
// standard icons
optDefault.state = NSControl.StateValue.on
break
case "darkalt" :
// inverted dark mode icons
optDarkAlt.state = NSControl.StateValue.on
break
case "emoji" :
// emoji icons
optEmoji.state = NSControl.StateValue.on
break
default :
// default to standard icons
optDefault.state = NSControl.StateValue.on
break
}
...
}
```
Now that we have our menu items all built and set up, we can add them to our submenu object.
```swift
@objc func buildMenu(key: String = "default") {
...
prefMenu.addItem(optDefault)
prefMenu.addItem(optDarkAlt)
prefMenu.addItem(optEmoji)
...
}
```
Now we need to create the [`NSMenuItem`](https://developer.apple.com/documentation/appkit/nsmenuitem) instance for the **Icon Style** menu item, and add it to our main menu object.
```swift
@objc func buildMenu(key: String = "default") {
...
let prefMenuItem = NSMenuItem(
title: "Icon Style",
action: nil,
keyEquivalent: ""
)
statusBarMenu.addItem(prefMenuItem)
...
}
```
The last thing we need to do is add our submenu, `prefMenu`, to the menu item we just created.
```swift
@objc func buildMenu(key: String = "default") {
...
statusBarMenu.setSubmenu(prefMenu, for: prefMenuItem)
...
}
```
You can run your application now, and test the user setting. Change the setting, close the app, relaunch, and your selected icon set should be identified with a checkmark.
Now we can move on to actually displaying different icons based on the user preference.
## Displaying the user's preferred icon set
At the top of our `buildMenu()` function, we're going to add some code to the `if` block used to set the correct icon mode (dark or light).
First, if the user preference is set to `emoji`, we just need to append `-emoji` to the filename.
```swift
@objc func buildMenu(key: String = "default") {
...
// if iconPref is set to emoji
if (defaults.string(forKey: "iconPref") == "emoji") {
suffix += "-emoji"
}
...
}
```
For the monochrome icons, we just need to make a few small adjustments. Since we want the white parts of the icon to represent the illuminated area of the moon, we can just swap the `new` and `full` icons, and also swap the `firstq` and `lastq` icons.
For the other four phases (waxing and waning, crescent and gibbous), we're going to append `-alt` to the filename.
```swift
@objc func buildMenu(key: String = "default") {
...
// otherwise use monochrome icons
else {
// check for dark mode and set the right image file
if #available(OSX 10.14, *) {
let darkMode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
if darkMode == "Dark" {
// if theme is set to dark alt make adjustments
if (defaults.string(forKey: "iconPref") == "darkalt") {
switch (key2) {
case "new" :
key2 = "full"
break
case "full" :
key2 = "new"
break
case "firstq" :
key2 = "lastq"
break
case "lastq" :
key2 = "firstq"
break
case "wanc", "wang", "waxc", "waxg" :
suffix += "-alt"
break
default :
break
}
}
suffix += "-dark"
}
}
}
...
}
```
Loading…
Cancel
Save