2 // SlotItemLevelView.swift
5 // Created by Hori,Masaki on 2017/01/02.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
11 final class SlotItemLevelView: NSTextField {
13 private static var sLevelMaskImage: CGImage?
14 private static var sAirLevelMaskImage: CGImage?
15 private static var sCharacterProtrudeMaskImageMaskImage: CGImage?
17 private let offset: CGFloat = 28
18 private let padding: CGFloat = 4
19 private let slideOffset: CGFloat = 3.5
20 private let anglePoint: CGFloat = 4.0
22 override init(frame frameRect: NSRect) {
24 super.init(frame: frameRect)
26 bind(NSBindingName(#keyPath(slotItemLevel)), to: slotItemController, withKeyPath: "selection.level", options: nil)
27 bind(NSBindingName(#keyPath(slotItemAlv)), to: slotItemController, withKeyPath: "selection.alv", options: nil)
30 required init?(coder: NSCoder) {
32 super.init(coder: coder)
34 bind(NSBindingName(#keyPath(slotItemLevel)), to: slotItemController, withKeyPath: "selection.level", options: nil)
35 bind(NSBindingName(#keyPath(slotItemAlv)), to: slotItemController, withKeyPath: "selection.alv", options: nil)
40 unbind(NSBindingName(#keyPath(slotItemLevel)))
41 unbind(NSBindingName(#keyPath(slotItemAlv)))
45 @objc dynamic var slotItemController = NSObjectController()
46 @objc dynamic var slotItemLevel: NSNumber? {
48 didSet { needsDisplay = true }
51 @objc dynamic var slotItemAlv: NSNumber? {
53 didSet { needsDisplay = true }
57 @objc var slotItemID: NSNumber? {
59 slotItemController.content = nil
61 guard let itemId = slotItemID as? Int else { return }
63 slotItemController.content = ServerDataStore.default.slotItem(by: itemId)
68 private var maskImage: CGImage? {
70 if let alv = slotItemAlv as? Int, alv != 0 { return airLevelMaskImage }
71 if let lv = slotItemLevel as? Int, lv != 0 { return levelMaskImage }
73 if isCharacterProtrude() { return characterProtrudeMaskImage }
78 private var levelMaskImage: CGImage {
80 if let r = SlotItemLevelView.sLevelMaskImage { return r }
81 SlotItemLevelView.sLevelMaskImage = maskImage(middle1: 0.75, middle2: 0.85)
83 return SlotItemLevelView.sLevelMaskImage!
86 private var airLevelMaskImage: CGImage {
88 if let r = SlotItemLevelView.sAirLevelMaskImage { return r }
89 SlotItemLevelView.sAirLevelMaskImage = maskImage(middle1: 0.65, middle2: 0.75)
91 return SlotItemLevelView.sAirLevelMaskImage!
93 private var characterProtrudeMaskImage: CGImage {
95 if let r = SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage { return r }
96 SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage = maskImage(middle1: 0.9, middle2: 1.0)
98 return SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage!
101 private var levelOneBezierPath: NSBezierPath? {
103 let width = bounds.width
104 let height = bounds.height
105 let path = multiline(lines: [
107 NSPoint(x: width - offset, y: 0),
108 NSPoint(x: width - offset, y: height)
116 private var levelTwoBezierPath: NSBezierPath? {
118 let width = bounds.width
119 let height = bounds.height
120 let path = multiline(lines:
123 NSPoint(x: width - offset, y: 0),
124 NSPoint(x: width - offset, y: height)
127 NSPoint(x: width - offset - padding, y: 0),
128 NSPoint(x: width - offset - padding, y: height)
136 private var levelThreeBezierPath: NSBezierPath? {
138 let width = bounds.width
139 let height = bounds.height
140 let path = multiline(lines:
143 NSPoint(x: width - offset, y: 0),
144 NSPoint(x: width - offset, y: height)
147 NSPoint(x: width - offset - padding, y: 0),
148 NSPoint(x: width - offset - padding, y: height)
151 NSPoint(x: width - offset - padding * 2, y: 0),
152 NSPoint(x: width - offset - padding * 2, y: height)
160 private var levelFourBezierPath: NSBezierPath? {
162 let width = bounds.width
163 let height = bounds.height
164 let path = multiline(lines:
167 NSPoint(x: width - offset - slideOffset, y: 0),
168 NSPoint(x: width - offset, y: height)
176 private var levelFiveBezierPath: NSBezierPath? {
178 let width = bounds.width
179 let height = bounds.height
180 let path = multiline(lines:
183 NSPoint(x: width - offset - slideOffset, y: 0),
184 NSPoint(x: width - offset, y: height)
187 NSPoint(x: width - offset - padding - slideOffset, y: 0),
188 NSPoint(x: width - offset - padding, y: height)
196 private var levelSixBezierPath: NSBezierPath? {
198 let width = bounds.width
199 let height = bounds.height
200 let path = multiline(lines:
203 NSPoint(x: width - offset - slideOffset, y: 0),
204 NSPoint(x: width - offset, y: height)
207 NSPoint(x: width - offset - padding - slideOffset, y: 0),
208 NSPoint(x: width - offset - padding, y: height)
211 NSPoint(x: width - offset - padding * 2 - slideOffset, y: 0),
212 NSPoint(x: width - offset - padding * 2, y: height)
220 private var levelSevenBezierPath: NSBezierPath? {
222 let width = bounds.width
223 let height = bounds.height
224 let path = polyline(points:
226 NSPoint(x: width - offset - slideOffset, y: 0),
227 NSPoint(x: width - offset, y: height * 0.5),
228 NSPoint(x: width - offset - anglePoint, y: height)
232 NSPoint(x: width - offset - padding - slideOffset, y: 0),
233 NSPoint(x: width - offset - padding, y: height * 0.5),
234 NSPoint(x: width - offset - padding - anglePoint, y: height)
236 .map { path?.append($0) }
237 path?.lineWidth = 2.0
242 private var levelFont: NSFont {
244 return NSFont.monospacedDigitSystemFont(ofSize: NSFont.smallSystemFontSize, weight: .regular)
247 private var levelColor: NSColor {
249 return #colorLiteral(red: 0.135, green: 0.522, blue: 0.619, alpha: 1)
253 override func draw(_ dirtyRect: NSRect) {
255 guard let context = NSGraphicsContext.current?.cgContext else { fatalError("Con not get current CGContext") }
258 maskImage.map { context.clip(to: bounds, mask: $0) }
260 super.draw(dirtyRect)
262 context.restoreGState()
268 private func bezierPathForALevel(level: Int) -> NSBezierPath? {
271 case 1: return levelOneBezierPath
272 case 2: return levelTwoBezierPath
273 case 3: return levelThreeBezierPath
274 case 4: return levelFourBezierPath
275 case 5: return levelFiveBezierPath
276 case 6: return levelSixBezierPath
277 case 7: return levelSevenBezierPath
282 private func colorForALevel(level: Int) -> NSColor? {
285 case 1, 2, 3: return #colorLiteral(red: 0.257, green: 0.523, blue: 0.643, alpha: 1)
286 case 4, 5, 6, 7: return #colorLiteral(red: 0.784, green: 0.549, blue: 0.000, alpha: 1)
291 private func shadowForALevel(level: Int) -> NSShadow? {
295 let shadow = NSShadow()
296 shadow.shadowColor = #colorLiteral(red: 0.095, green: 0.364, blue: 0.917, alpha: 1)
297 shadow.shadowBlurRadius = 4.0
301 let shadow = NSShadow()
302 shadow.shadowColor = NSColor.yellow
303 shadow.shadowBlurRadius = 3.0
311 private func drawAirLevel() {
313 guard let alv = slotItemAlv as? Int else { return }
315 colorForALevel(level: alv)?.set()
316 shadowForALevel(level: alv)?.set()
317 bezierPathForALevel(level: alv)?.stroke()
320 private func drawLevel() {
322 guard let lv = slotItemLevel as? Int, lv != 0 else { return }
324 let string = (lv == 10 ? "max" : "★+\(lv)")
325 let attr: [NSAttributedStringKey: Any] = [.font: levelFont,
326 .foregroundColor: levelColor]
327 let attributedString = NSAttributedString(string: string, attributes: attr)
328 let boundingRect = attributedString.boundingRect(with: bounds.size)
330 rect.origin.x = rect.width - boundingRect.width - 1.0
333 attributedString.draw(in: rect)