Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions Motivation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,13 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0700;
LastUpgradeCheck = 0920;
ORGANIZATIONNAME = "Sam Soffes";
TargetAttributes = {
21F222251B73B5AC00B9A11A = {
CreatedOnToolsVersion = 6.4;
LastSwiftMigration = 0830;
ProvisioningStyle = Automatic;
};
21F222391B73B60100B9A11A = {
CreatedOnToolsVersion = 6.4;
Expand Down Expand Up @@ -269,18 +271,26 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_BITCODE = YES;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
Expand All @@ -302,6 +312,7 @@
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
Expand All @@ -313,18 +324,26 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_BITCODE = YES;
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
Expand All @@ -338,38 +357,44 @@
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
};
name = Release;
};
21F222321B73B5AC00B9A11A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = Motivation/Info.plist;
INSTALL_PATH = "$(HOME)/Library/Screen Savers";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.motiviation;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
WRAPPER_EXTENSION = saver;
};
name = Debug;
};
21F222331B73B5AC00B9A11A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = Motivation/Info.plist;
INSTALL_PATH = "$(HOME)/Library/Screen Savers";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.motiviation;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 3.0;
WRAPPER_EXTENSION = saver;
};
name = Release;
Expand Down
76 changes: 38 additions & 38 deletions Motivation/AgeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@ class AgeView: ScreenSaverView {

// MARK: - Properties

private let textLabel: NSTextField = {
fileprivate let textLabel: NSTextField = {
let label = NSTextField()
label.translatesAutoresizingMaskIntoConstraints = false
label.editable = false
label.isEditable = false
label.drawsBackground = false
label.bordered = false
label.bezeled = false
label.selectable = false
label.textColor = .whiteColor()
label.isBordered = false
label.isBezeled = false
label.isSelectable = false
label.textColor = .white
return label
}()

private lazy var configurationWindowController: NSWindowController = {
fileprivate lazy var configurationWindowController: NSWindowController = {
return ConfigurationWindowController()
}()

private var motivationLevel: MotivationLevel
fileprivate var motivationLevel: MotivationLevel

private var birthday: NSDate? {
fileprivate var birthday: Date? {
didSet {
updateFont()
}
Expand All @@ -41,7 +41,7 @@ class AgeView: ScreenSaverView {
// MARK: - Initializers

convenience init() {
self.init(frame: CGRectZero, isPreview: false)
self.init(frame: CGRect.zero, isPreview: false)
}

override init!(frame: NSRect, isPreview: Bool) {
Expand All @@ -57,22 +57,22 @@ class AgeView: ScreenSaverView {
}

deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
NotificationCenter.default.removeObserver(self)
}


// MARK: - NSView

override func drawRect(rect: NSRect) {
let backgroundColor: NSColor = .blackColor()
override func draw(_ rect: NSRect) {
let backgroundColor: NSColor = .black

backgroundColor.setFill()
NSBezierPath.fillRect(bounds)
NSBezierPath.fill(bounds)
}

// If the screen saver changes size, update the font
override func resizeWithOldSuperviewSize(oldSize: NSSize) {
super.resizeWithOldSuperviewSize(oldSize)
override func resize(withOldSuperviewSize oldSize: NSSize) {
super.resize(withOldSuperviewSize: oldSize)
updateFont()
}

Expand Down Expand Up @@ -101,7 +101,7 @@ class AgeView: ScreenSaverView {
// MARK: - Private

/// Shared initializer
private func initialize() {
fileprivate func initialize() {
// Set animation time interval
animationTimeInterval = 1 / 30

Expand All @@ -111,56 +111,56 @@ class AgeView: ScreenSaverView {
// Setup the label
addSubview(textLabel)
addConstraints([
NSLayoutConstraint(item: textLabel, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0),
NSLayoutConstraint(item: textLabel, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .CenterY, multiplier: 1, constant: 0)
NSLayoutConstraint(item: textLabel, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0),
NSLayoutConstraint(item: textLabel, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0)
])

// Listen for configuration changes
NSNotificationCenter.defaultCenter().addObserver(self, selector: "motivationLevelDidChange:", name: Preferences.motivationLevelDidChangeNotificationName, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "birthdayDidChange:", name: Preferences.birthdayDidChangeNotificationName, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(AgeView.motivationLevelDidChange(_:)), name: NSNotification.Name(rawValue: Preferences.motivationLevelDidChangeNotificationName), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(AgeView.birthdayDidChange(_:)), name: NSNotification.Name(rawValue: Preferences.birthdayDidChangeNotificationName), object: nil)
}

/// Age calculation
private func ageForBirthday(birthday: NSDate) -> Double {
let calendar = NSCalendar.currentCalendar()
let now = NSDate()
fileprivate func ageForBirthday(_ birthday: Date) -> Double {
let calendar = Calendar.current
let now = Date()

// An age is defined as the number of years you've been alive plus the number of days, seconds, and nanoseconds
// you've been alive out of that many units in the current year.
let components = calendar.components([NSCalendarUnit.Year, NSCalendarUnit.Day, NSCalendarUnit.Second, NSCalendarUnit.Nanosecond], fromDate: birthday, toDate: now, options: [])
let components = (calendar as NSCalendar).components([NSCalendar.Unit.year, NSCalendar.Unit.day, NSCalendar.Unit.second, NSCalendar.Unit.nanosecond], from: birthday, to: now, options: [])

// We calculate these every time since the values can change when you cross a boundary. Things are too
// complicated to try to figure out when that is and cache them. NSCalendar is made for this.
let daysInYear = Double(calendar.daysInYear(now) ?? 365)
let hoursInDay = Double(calendar.rangeOfUnit(NSCalendarUnit.Hour, inUnit: NSCalendarUnit.Day, forDate: now).length)
let minutesInHour = Double(calendar.rangeOfUnit(NSCalendarUnit.Minute, inUnit: NSCalendarUnit.Hour, forDate: now).length)
let secondsInMinute = Double(calendar.rangeOfUnit(NSCalendarUnit.Second, inUnit: NSCalendarUnit.Minute, forDate: now).length)
let nanosecondsInSecond = Double(calendar.rangeOfUnit(NSCalendarUnit.Nanosecond, inUnit: NSCalendarUnit.Second, forDate: now).length)
let hoursInDay = Double((calendar as NSCalendar).range(of: NSCalendar.Unit.hour, in: NSCalendar.Unit.day, for: now).length)
let minutesInHour = Double((calendar as NSCalendar).range(of: NSCalendar.Unit.minute, in: NSCalendar.Unit.hour, for: now).length)
let secondsInMinute = Double((calendar as NSCalendar).range(of: NSCalendar.Unit.second, in: NSCalendar.Unit.minute, for: now).length)
let nanosecondsInSecond = Double((calendar as NSCalendar).range(of: NSCalendar.Unit.nanosecond, in: NSCalendar.Unit.second, for: now).length)

// Now that we have all of the values, assembling them is easy. We don't get minutes and hours from the calendar
// since it will overflow nicely to seconds. We need days and years since the number of days in a year changes
// more frequently. This will handle leap seconds, days, and years.
let seconds = Double(components.second) + (Double(components.nanosecond) / nanosecondsInSecond)
let seconds = Double(components.second!) + (Double(components.nanosecond!) / nanosecondsInSecond)
let minutes = seconds / secondsInMinute
let hours = minutes / minutesInHour
let days = Double(components.day) + (hours / hoursInDay)
let years = Double(components.year) + (days / daysInYear)
let days = Double(components.day!) + (hours / hoursInDay)
let years = Double(components.year!) + (days / daysInYear)

return years
}

/// Motiviation level changed
@objc private func motivationLevelDidChange(notification: NSNotification?) {
@objc fileprivate func motivationLevelDidChange(_ notification: Notification?) {
motivationLevel = Preferences().motivationLevel
}

/// Birthday changed
@objc private func birthdayDidChange(notification: NSNotification?) {
@objc fileprivate func birthdayDidChange(_ notification: Notification?) {
birthday = Preferences().birthday
}

/// Update the font for the current size
private func updateFont() {
fileprivate func updateFont() {
if birthday != nil {
textLabel.font = fontWithSize(bounds.width / 10)
} else {
Expand All @@ -169,17 +169,17 @@ class AgeView: ScreenSaverView {
}

/// Get a font
private func fontWithSize(fontSize: CGFloat, monospace: Bool = true) -> NSFont {
fileprivate func fontWithSize(_ fontSize: CGFloat, monospace: Bool = true) -> NSFont {
let font: NSFont
if #available(OSX 10.11, *) {
font = .systemFontOfSize(fontSize, weight: NSFontWeightThin)
font = .systemFont(ofSize: fontSize, weight: NSFontWeightThin)
} else {
font = NSFont(name: "HelveticaNeue-Thin", size: fontSize)!
}

let fontDescriptor: NSFontDescriptor
if monospace {
fontDescriptor = font.fontDescriptor.fontDescriptorByAddingAttributes([
fontDescriptor = font.fontDescriptor.addingAttributes([
NSFontFeatureSettingsAttribute: [
[
NSFontFeatureTypeIdentifierKey: kNumberSpacingType,
Expand Down
12 changes: 6 additions & 6 deletions Motivation/ConfigurationWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,23 @@ class ConfigurationWindowController: NSWindowController {
super.windowDidLoad()

switch Preferences().motivationLevel {
case .Light: lightRadio.state = NSOnState
case .Moderate: moderateRadio.state = NSOnState
case .Terrifying: terrifyingRadio.state = NSOnState
case .light: lightRadio.state = NSOnState
case .moderate: moderateRadio.state = NSOnState
case .terrifying: terrifyingRadio.state = NSOnState
}
}


// MARK: - Action

@IBAction func close(sender: AnyObject?) {
@IBAction func close(_ sender: AnyObject?) {
if let window = window {
window.sheetParent?.endSheet(window)
}
}

@IBAction func levelDidChange(sender: AnyObject?) {
guard let button = sender as? NSButton, level = MotivationLevel(rawValue: UInt(button.tag)) else { return }
@IBAction func levelDidChange(_ sender: AnyObject?) {
guard let button = sender as? NSButton, let level = MotivationLevel(rawValue: UInt(button.tag)) else { return }
Preferences().motivationLevel = level
}
}
29 changes: 14 additions & 15 deletions Motivation/NSCalendar+Motivation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,26 @@

import Foundation

extension NSCalendar {
func daysInYear(date: NSDate = NSDate()) -> Int? {
let year = components([NSCalendarUnit.Year], fromDate: date).year
return daysInYear(year)
extension Calendar {
func daysInYear(_ date: Date = Date()) -> Int? {
let year = dateComponents([.year], from: date).year
return daysInYear(year!)
}

func daysInYear(year: Int) -> Int? {
guard let begin = lastDayOfYear(year - 1), end = lastDayOfYear(year) else { return nil }
return components([NSCalendarUnit.Day], fromDate: begin, toDate: end, options: []).day
func daysInYear(_ year: Int) -> Int? {
guard let begin = lastDayOfYear(year - 1), let end = lastDayOfYear(year) else { return nil }
return dateComponents([.day], from: begin, to: end).day
}

func lastDayOfYear(year: Int) -> NSDate? {
let components = NSDateComponents()
func lastDayOfYear(_ year: Int) -> Date? {
var components = DateComponents()
components.year = year
guard let years = dateFromComponents(components) else { return nil }
guard let years = date(from: components) else { return nil }

components.month = rangeOfUnit(NSCalendarUnit.Month, inUnit: NSCalendarUnit.Year, forDate: years).length
guard let months = dateFromComponents(components) else { return nil }
components.month = range(of: Calendar.Component.month, in: Calendar.Component.year, for: years)?.count
guard let months = date(from: components) else { return nil }

components.day = rangeOfUnit(NSCalendarUnit.Day, inUnit: NSCalendarUnit.Month, forDate: months).length

return dateFromComponents(components)
components.day = range(of: Calendar.Component.day, in: Calendar.Component.month, for: months)?.count
return date(from: components)
}
}
Loading