Skip to content

Commit cd950f4

Browse files
committed
Merge branch 'release/1.4.0'
2 parents 5e0b2de + bba5c8e commit cd950f4

File tree

10 files changed

+295
-80
lines changed

10 files changed

+295
-80
lines changed

Press/Snapshot.png

1.06 MB
Loading

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Song Rating
22
macOS app for rating music in iTunes/Music.app
33

4+
<img src="./Press/Snapshot.png" width=300 style="border-radius:4px">
5+
46
## Requirements
57
- macOS 10.14 +
68

Song Rating.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -776,15 +776,15 @@
776776
CODE_SIGN_IDENTITY = "Apple Development";
777777
CODE_SIGN_STYLE = Automatic;
778778
COMBINE_HIDPI_IMAGES = YES;
779-
CURRENT_PROJECT_VERSION = 11;
779+
CURRENT_PROJECT_VERSION = 13;
780780
DEVELOPMENT_TEAM = A8K92XFF77;
781781
ENABLE_HARDENED_RUNTIME = YES;
782782
INFOPLIST_FILE = "Song Rating/Info.plist";
783783
LD_RUNPATH_SEARCH_PATHS = (
784784
"$(inherited)",
785785
"@executable_path/../Frameworks",
786786
);
787-
MARKETING_VERSION = 1.3.0;
787+
MARKETING_VERSION = 1.4.0;
788788
PRODUCT_BUNDLE_IDENTIFIER = "com.mainasuk.Song-Rating";
789789
PRODUCT_NAME = "$(TARGET_NAME)";
790790
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -804,15 +804,15 @@
804804
CODE_SIGN_IDENTITY = "Apple Development";
805805
CODE_SIGN_STYLE = Automatic;
806806
COMBINE_HIDPI_IMAGES = YES;
807-
CURRENT_PROJECT_VERSION = 11;
807+
CURRENT_PROJECT_VERSION = 13;
808808
DEVELOPMENT_TEAM = A8K92XFF77;
809809
ENABLE_HARDENED_RUNTIME = YES;
810810
INFOPLIST_FILE = "Song Rating/Info.plist";
811811
LD_RUNPATH_SEARCH_PATHS = (
812812
"$(inherited)",
813813
"@executable_path/../Frameworks",
814814
);
815-
MARKETING_VERSION = 1.3.0;
815+
MARKETING_VERSION = 1.4.0;
816816
PRODUCT_BUNDLE_IDENTIFIER = "com.mainasuk.Song-Rating";
817817
PRODUCT_NAME = "$(TARGET_NAME)";
818818
PROVISIONING_PROFILE_SPECIFIER = "";

Song Rating/AppDelegate.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ extension AppDelegate {
114114
os_log("%{public}s[%{public}ld], %{public}s: Default shortcut set fail", ((#file as NSString).lastPathComponent), #line, #function)
115115
}
116116

117-
// register application behavior
117+
// register application default behavior
118118
UserDefaults.standard.register(defaults: [
119119
ApplicationKey.isFirstLaunch.rawValue : true,
120-
ApplicationKey.launchAtLogin.rawValue : false
120+
ApplicationKey.launchAtLogin.rawValue : false,
121+
ApplicationKey.allowHalfStar.rawValue : false
121122
])
122123

123124
// setup observer

Song Rating/Controllers/PreferencesViewController.swift

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,21 @@ final class PreferencesViewController: NSViewController {
1515
return NSTextField(labelWithString: "sample").font!.pointSize
1616
}
1717

18-
lazy var StartupTextField: NSTextField = {
18+
lazy var startupTextField: NSTextField = {
1919
return NSTextField(labelWithString: "Startup: ")
2020
}()
21+
lazy var halfStarTextField: NSTextField = {
22+
return NSTextField(labelWithString: "Half star: ")
23+
}()
2124
lazy var songRatingDownTextField: NSTextField = {
2225
return NSTextField(labelWithString: "Song rating down: ")
2326
}()
2427
lazy var songRatingUpTextField: NSTextField = {
2528
return NSTextField(labelWithString: "Song rating up: ")
2629
}()
30+
lazy var showOrClosePopoverTextField: NSTextField = {
31+
return NSTextField(labelWithString: "Show/Close popover: ")
32+
}()
2733
lazy var songRating5TextField: NSTextField = {
2834
let attributedString = PreferencesViewController.starsAttributedString(count: 5, fontSize: PreferencesViewController.defaultTextFieldFontSize)
2935
attributedString.append(NSAttributedString(string: ": "))
@@ -52,13 +58,15 @@ final class PreferencesViewController: NSViewController {
5258
lazy var songRating0TextField: NSTextField = {
5359
return NSTextField(labelWithString: "Remove stars: ")
5460
}()
55-
lazy var showOrClosePopoverTextField: NSTextField = {
56-
return NSTextField(labelWithString: "Show/Close popover: ")
57-
}()
61+
5862
let launchAtLoginCheckboxButton: NSButton = {
5963
let button = NSButton(checkboxWithTitle: "Launch at login", target: nil, action: nil)
6064
return button
6165
}()
66+
let halfStarCheckboxButton: NSButton = {
67+
let button = NSButton(checkboxWithTitle: "Enable", target: nil, action: nil)
68+
return button
69+
}()
6270
let songRatingDownShortcutView: MASShortcutView = {
6371
let shortcutView = MASShortcutView()
6472
shortcutView.associatedUserDefaultsKey = ShortcutKey.songRatingDown.rawValue
@@ -69,6 +77,11 @@ final class PreferencesViewController: NSViewController {
6977
shortcutView.associatedUserDefaultsKey = ShortcutKey.songRatingUp.rawValue
7078
return shortcutView
7179
}()
80+
let showOrClosePopoverShortcutView: MASShortcutView = {
81+
let shortcutView = MASShortcutView()
82+
shortcutView.associatedUserDefaultsKey = ShortcutKey.showOrClosePopover.rawValue
83+
return shortcutView
84+
}()
7285
let songRating5ShortcutView: MASShortcutView = {
7386
let shortcutView = MASShortcutView()
7487
shortcutView.associatedUserDefaultsKey = ShortcutKey.songRating5.rawValue
@@ -99,12 +112,6 @@ final class PreferencesViewController: NSViewController {
99112
shortcutView.associatedUserDefaultsKey = ShortcutKey.songRating0.rawValue
100113
return shortcutView
101114
}()
102-
let showOrClosePopoverShortcutView: MASShortcutView = {
103-
let shortcutView = MASShortcutView()
104-
shortcutView.associatedUserDefaultsKey = ShortcutKey.showOrClosePopover.rawValue
105-
return shortcutView
106-
}()
107-
108115

109116
let leadingPaddingView = NSView()
110117
let trailingPaddingView = NSView()
@@ -113,7 +120,8 @@ final class PreferencesViewController: NSViewController {
113120
let empty = NSGridCell.emptyContentView
114121

115122
let gridView = NSGridView(views: [
116-
[StartupTextField, launchAtLoginCheckboxButton],
123+
[startupTextField, launchAtLoginCheckboxButton],
124+
[halfStarTextField, halfStarCheckboxButton],
117125
[NSBox.separatorLine],
118126
[songRatingDownTextField, songRatingDownShortcutView],
119127
[songRatingUpTextField, songRatingUpShortcutView],
@@ -148,13 +156,15 @@ final class PreferencesViewController: NSViewController {
148156
}()
149157

150158
var launchAtLoginObservation: NSKeyValueObservation?
159+
var halfStarObservation: NSKeyValueObservation?
151160

152161
override func loadView() {
153162
self.view = NSView()
154163
}
155164

156165
deinit {
157166
launchAtLoginObservation?.invalidate()
167+
halfStarObservation?.invalidate()
158168
}
159169

160170
}
@@ -163,7 +173,7 @@ extension PreferencesViewController {
163173
private static func starsAttributedString(count: Int, fontSize: CGFloat) -> NSMutableAttributedString {
164174
let font = NSFont.systemFont(ofSize: fontSize)
165175
let stars = Stars(
166-
stars: Array(repeating: Star(size: CGSize(width: fontSize, height: fontSize), fill: true), count: count),
176+
stars: Array(repeating: Star(size: CGSize(width: fontSize, height: fontSize), style: .full), count: count),
167177
spacing: 3
168178
)
169179
var image = stars.image
@@ -195,6 +205,10 @@ extension PreferencesViewController {
195205
@objc private func launchAtLoginCheckboxButtonChanged(_ sender: NSButton) {
196206
UserDefaults.standard.launchAtLogin = sender.state == .on
197207
}
208+
209+
@objc private func halfStarCheckboxButtonChanged(_ sender: NSButton) {
210+
UserDefaults.standard.allowHalfStar = sender.state == .on
211+
}
198212

199213
}
200214

@@ -229,6 +243,12 @@ extension PreferencesViewController {
229243
launchAtLoginObservation = UserDefaults.standard.observe(\.launchAtLogin, options: [.initial, .new]) { [weak self] defaults, launchAtLogin in
230244
self?.launchAtLoginCheckboxButton.state = defaults.launchAtLogin ? .on : .off
231245
}
246+
247+
halfStarCheckboxButton.target = self
248+
halfStarCheckboxButton.action = #selector(PreferencesViewController.halfStarCheckboxButtonChanged(_:))
249+
halfStarObservation = UserDefaults.standard.observe(\.allowHalfStar, options: [.initial, .new]) { [weak self] defaults, launchAtLogin in
250+
self?.halfStarCheckboxButton.state = defaults.allowHalfStar ? .on : .off
251+
}
232252
}
233253

234254
override func viewDidAppear() {

Song Rating/Controls/MenuBarRatingControl.swift

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,24 @@ final class MenuBarRatingControl {
5858
let ratingControl = RatingControl(rating: 0)
5959
let menuBarIcon: MenuBarIcon
6060
let trackingAreaResponser = TrackingAreaResponder()
61-
61+
62+
private let clickGestureRecognizer: NSClickGestureRecognizer = {
63+
let gestureRecognizer = NSClickGestureRecognizer()
64+
return gestureRecognizer
65+
}()
66+
private let doubleClickGestureRecognizer: NSClickGestureRecognizer = {
67+
let gestureRecognizer = NSClickGestureRecognizer()
68+
gestureRecognizer.numberOfClicksRequired = 2
69+
return gestureRecognizer
70+
}()
71+
private let pressGestureRecognizer: NSPressGestureRecognizer = {
72+
let gestureRecognizer = NSPressGestureRecognizer()
73+
return gestureRecognizer
74+
}()
75+
private let panGestureRecognizer: NSPanGestureRecognizer = {
76+
let gestureRecognizer = NSPanGestureRecognizer()
77+
return gestureRecognizer
78+
}()
6279

6380
private(set) lazy var menuBarMenu: NSMenu = {
6481
let menu = NSMenu()
@@ -91,9 +108,9 @@ final class MenuBarRatingControl {
91108
}
92109
private(set) var playState: PlayInfo.PlayerState = .unknown {
93110
didSet {
94-
// FIXME: close undetached popover when menu bar collapse
111+
// FIXME: close attached popover when menu bar collapse
95112
if playState == .unknown {
96-
WindowManager.shared.undetachedPopover?.close()
113+
WindowManager.shared.attachedPopover?.close()
97114
}
98115
}
99116
}
@@ -111,10 +128,30 @@ final class MenuBarRatingControl {
111128
}
112129

113130
button.image = ratingControl.starsImage
114-
button.sendAction(on: [.leftMouseUp, .rightMouseUp, .leftMouseDragged])
131+
button.sendAction(on: [.rightMouseUp])
115132
button.action = #selector(MenuBarRatingControl.action(_:))
116133
button.target = self
117134
button.setButtonType(.momentaryChange)
135+
136+
// set fail rule
137+
doubleClickGestureRecognizer.shouldRequireFailure(of: clickGestureRecognizer)
138+
panGestureRecognizer.shouldRequireFailure(of: pressGestureRecognizer)
139+
140+
clickGestureRecognizer.action = #selector(MenuBarRatingControl.clickGestureRecognizerHandler(_:))
141+
clickGestureRecognizer.target = self
142+
button.addGestureRecognizer(clickGestureRecognizer)
143+
144+
doubleClickGestureRecognizer.action = #selector(MenuBarRatingControl.doubleClickGestureRecognizerHandler(_:))
145+
doubleClickGestureRecognizer.target = self
146+
button.addGestureRecognizer(doubleClickGestureRecognizer)
147+
148+
pressGestureRecognizer.action = #selector(MenuBarRatingControl.pressGestureRecognizerHandler(_:))
149+
pressGestureRecognizer.target = self
150+
button.addGestureRecognizer(pressGestureRecognizer)
151+
152+
panGestureRecognizer.action = #selector(MenuBarRatingControl.panGestureRecognizerHandler(_:))
153+
panGestureRecognizer.target = self
154+
button.addGestureRecognizer(panGestureRecognizer)
118155

119156
let trackingArea = NSTrackingArea(rect: button.bounds, options: [.activeAlways, .mouseEnteredAndExited, .mouseMoved], owner: trackingAreaResponser, userInfo: nil)
120157
button.addTrackingArea(trackingArea)
@@ -154,32 +191,74 @@ extension MenuBarRatingControl {
154191

155192
extension MenuBarRatingControl {
156193

157-
@objc func action(_ sender: NSButton) {
194+
@objc private func action(_ sender: NSButton) {
158195
guard let event = NSApp.currentEvent else {
159196
return
160197
}
161198
os_log("%{public}s[%{public}ld], %{public}s: menu bar button receive event %s", ((#file as NSString).lastPathComponent), #line, #function, event.debugDescription)
162199

163200
switch event.type {
164-
case .leftMouseUp where isStop :
165-
let position = NSPoint(x: 0, y: sender.bounds.height + 5)
166-
menuBarMenu.popUp(positioning: nil, at: position, in: sender)
167-
168201
case .rightMouseUp where isStop:
169202
let position = sender.convert(event.locationInWindow, to: nil)
170203
menuBarMenu.popUp(positioning: nil, at: position, in: sender)
171204

172-
case .leftMouseUp, .leftMouseDragged:
173-
ratingControl.action(from: sender, with: event)
174-
175205
case .rightMouseUp:
176206
WindowManager.shared.triggerPopover()
177207

178208
default:
179209
os_log("%{public}s[%{public}ld], %{public}s: no handler for event %s", ((#file as NSString).lastPathComponent), #line, #function, event.debugDescription)
180210
}
181211
}
212+
213+
@objc private func clickGestureRecognizerHandler(_ sender: NSClickGestureRecognizer) {
214+
os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, sender.debugDescription)
215+
guard let button = statusItem.button else { return }
216+
217+
switch sender.state {
218+
case .ended:
219+
ratingControl.action(from: button, by: sender, behavior: .full)
220+
default:
221+
break
222+
}
223+
}
224+
225+
@objc private func doubleClickGestureRecognizerHandler(_ sender: NSClickGestureRecognizer) {
226+
os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, sender.debugDescription)
227+
guard let button = statusItem.button else { return }
228+
229+
switch sender.state {
230+
case .ended:
231+
ratingControl.action(from: button, by: sender, behavior: UserDefaults.standard.allowHalfStar ? .half : .full)
232+
default:
233+
break
234+
}
235+
}
236+
237+
@objc private func pressGestureRecognizerHandler(_ sender: NSPressGestureRecognizer) {
238+
os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, sender.debugDescription)
239+
guard let button = statusItem.button else { return }
240+
241+
switch sender.state {
242+
case .ended:
243+
ratingControl.action(from: button, by: sender, behavior: .full)
244+
default:
245+
break
246+
}
247+
}
182248

249+
250+
@objc private func panGestureRecognizerHandler(_ sender: NSPanGestureRecognizer) {
251+
os_log("%{public}s[%{public}ld], %{public}s: %s", ((#file as NSString).lastPathComponent), #line, #function, sender.debugDescription)
252+
guard let button = statusItem.button else { return }
253+
254+
switch sender.state {
255+
case .changed, .ended:
256+
ratingControl.action(from: button, by: sender, behavior: UserDefaults.standard.allowHalfStar ? .both: .full)
257+
default:
258+
break
259+
}
260+
}
261+
183262
}
184263

185264
// MARK: - RatingControlDelegate
@@ -212,8 +291,8 @@ extension MenuBarRatingControl {
212291
guard !isStop else {
213292
return
214293
}
215-
216-
ratingControl.update(rating: ratingControl.rating + 20)
294+
let ratingChange: Int = UserDefaults.standard.allowHalfStar ? 10 : 20
295+
ratingControl.update(rating: ratingControl.rating + ratingChange)
217296
iTunesRadioStation.shared.setRating(ratingControl.rating)
218297
}
219298

@@ -223,7 +302,8 @@ extension MenuBarRatingControl {
223302
return
224303
}
225304

226-
ratingControl.update(rating: ratingControl.rating - 20)
305+
let ratingChange: Int = UserDefaults.standard.allowHalfStar ? 10 : 20
306+
ratingControl.update(rating: ratingControl.rating - ratingChange)
227307
iTunesRadioStation.shared.setRating(ratingControl.rating)
228308
}
229309

0 commit comments

Comments
 (0)