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 { needsDisplayInMainThread() }
51 @objc dynamic var slotItemAlv: NSNumber? {
53 didSet { needsDisplayInMainThread() }
56 @objc var slotItemID: NSNumber? {
58 slotItemController.content = nil
60 guard let itemId = slotItemID as? Int else { return }
62 slotItemController.content = ServerDataStore.default.slotItem(by: itemId)
63 needsDisplayInMainThread()
67 private var maskImage: CGImage? {
69 if let alv = slotItemAlv as? Int, alv != 0 { return airLevelMaskImage }
70 if let lv = slotItemLevel as? Int, lv != 0 { return levelMaskImage }
72 if isCharacterProtrude() { return characterProtrudeMaskImage }
77 private var levelMaskImage: CGImage {
79 if let r = SlotItemLevelView.sLevelMaskImage { return r }
80 SlotItemLevelView.sLevelMaskImage = maskImage(middle1: 0.75, middle2: 0.85)
82 return SlotItemLevelView.sLevelMaskImage!
85 private var airLevelMaskImage: CGImage {
87 if let r = SlotItemLevelView.sAirLevelMaskImage { return r }
88 SlotItemLevelView.sAirLevelMaskImage = maskImage(middle1: 0.65, middle2: 0.75)
90 return SlotItemLevelView.sAirLevelMaskImage!
92 private var characterProtrudeMaskImage: CGImage {
94 if let r = SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage { return r }
95 SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage = maskImage(middle1: 0.9, middle2: 1.0)
97 return SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage!
100 private var levelOneBezierPath: Polygon {
102 let width = bounds.width
103 let height = bounds.height
104 return Polygon(lineWidth: 1.0)
105 .move(to: NSPoint(x: width - offset, y: 0))
106 .line(to: NSPoint(x: width - offset, y: height))
109 private var levelTwoBezierPath: Polygon {
111 let width = bounds.width
112 let height = bounds.height
113 return Polygon(lineWidth: 1.0)
114 .move(to: NSPoint(x: width - offset, y: 0))
115 .line(to: NSPoint(x: width - offset, y: height))
116 .move(to: NSPoint(x: width - offset - padding, y: 0))
117 .line(to: NSPoint(x: width - offset - padding, y: height))
120 private var levelThreeBezierPath: Polygon {
122 let width = bounds.width
123 let height = bounds.height
124 return Polygon(lineWidth: 1.0)
125 .move(to: NSPoint(x: width - offset, y: 0))
126 .line(to: NSPoint(x: width - offset, y: height))
127 .move(to: NSPoint(x: width - offset - padding, y: 0))
128 .line(to: NSPoint(x: width - offset - padding, y: height))
129 .move(to: NSPoint(x: width - offset - padding * 2, y: 0))
130 .line(to: NSPoint(x: width - offset - padding * 2, y: height))
133 private var levelFourBezierPath: Polygon {
135 let width = bounds.width
136 let height = bounds.height
137 return Polygon(lineWidth: 2.0)
138 .move(to: NSPoint(x: width - offset - slideOffset, y: 0))
139 .line(to: NSPoint(x: width - offset, y: height))
142 private var levelFiveBezierPath: Polygon {
144 let width = bounds.width
145 let height = bounds.height
146 return Polygon(lineWidth: 2.0)
147 .move(to: NSPoint(x: width - offset - slideOffset, y: 0))
148 .line(to: NSPoint(x: width - offset, y: height))
149 .move(to: NSPoint(x: width - offset - padding - slideOffset, y: 0))
150 .line(to: NSPoint(x: width - offset - padding, y: height))
153 private var levelSixBezierPath: Polygon {
155 let width = bounds.width
156 let height = bounds.height
157 return Polygon(lineWidth: 2.0)
158 .move(to: NSPoint(x: width - offset - slideOffset, y: 0))
159 .line(to: NSPoint(x: width - offset, y: height))
160 .move(to: NSPoint(x: width - offset - padding - slideOffset, y: 0))
161 .line(to: NSPoint(x: width - offset - padding, y: height))
162 .move(to: NSPoint(x: width - offset - padding * 2 - slideOffset, y: 0))
163 .line(to: NSPoint(x: width - offset - padding * 2, y: height))
166 private var levelSevenBezierPath: Polygon {
168 let width = bounds.width
169 let height = bounds.height
170 return Polygon(lineWidth: 2.0)
171 .move(to: NSPoint(x: width - offset - slideOffset, y: 0))
172 .line(to: NSPoint(x: width - offset, y: height * 0.5))
173 .line(to: NSPoint(x: width - offset - anglePoint, y: height))
174 .move(to: NSPoint(x: width - offset - padding - slideOffset, y: 0))
175 .line(to: NSPoint(x: width - offset - padding, y: height * 0.5))
176 .line(to: NSPoint(x: width - offset - padding - anglePoint, y: height))
179 private var levelFont: NSFont {
181 return .monospacedDigitSystemFont(ofSize: NSFont.smallSystemFontSize, weight: .regular)
184 private var levelColor: NSColor {
186 return #colorLiteral(red: 0.135, green: 0.522, blue: 0.619, alpha: 1)
190 override func draw(_ dirtyRect: NSRect) {
192 guard let context = NSGraphicsContext.current?.cgContext else { fatalError("Con not get current CGContext") }
195 maskImage.map { context.clip(to: bounds, mask: $0) }
197 super.draw(dirtyRect)
199 context.restoreGState()
205 private func bezierPathForALevel(level: Int) -> Polygon? {
208 case 1: return levelOneBezierPath
209 case 2: return levelTwoBezierPath
210 case 3: return levelThreeBezierPath
211 case 4: return levelFourBezierPath
212 case 5: return levelFiveBezierPath
213 case 6: return levelSixBezierPath
214 case 7: return levelSevenBezierPath
219 private func colorForALevel(level: Int) -> NSColor? {
222 case 1, 2, 3: return #colorLiteral(red: 0.257, green: 0.523, blue: 0.643, alpha: 1)
223 case 4, 5, 6, 7: return #colorLiteral(red: 0.784, green: 0.549, blue: 0.000, alpha: 1)
228 private func shadowForALevel(level: Int) -> NSShadow? {
232 let shadow = NSShadow()
233 shadow.shadowColor = #colorLiteral(red: 0.095, green: 0.364, blue: 0.917, alpha: 1)
234 shadow.shadowBlurRadius = 4.0
238 let shadow = NSShadow()
239 shadow.shadowColor = .yellow
240 shadow.shadowBlurRadius = 3.0
248 private func drawAirLevel() {
250 guard let alv = slotItemAlv as? Int else { return }
252 colorForALevel(level: alv)?.set()
253 shadowForALevel(level: alv)?.set()
254 bezierPathForALevel(level: alv)?.stroke()
257 private func drawLevel() {
259 guard let lv = slotItemLevel as? Int, lv != 0 else { return }
261 let string = (lv == 10 ? "max" : "★+\(lv)")
262 let attr: [NSAttributedStringKey: Any] = [.font: levelFont,
263 .foregroundColor: levelColor]
264 let attributedString = NSAttributedString(string: string, attributes: attr)
265 let boundingRect = attributedString.boundingRect(with: bounds.size)
267 rect.origin.x = rect.width - boundingRect.width - 1.0
270 attributedString.draw(in: rect)