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? {
60 slotItemController.content = nil
62 guard let itemId = slotItemID as? Int else {
67 slotItemController.content = ServerDataStore.default.slotItem(by: itemId)
68 needsDisplayInMainThread()
72 private var maskImage: CGImage? {
74 if let alv = slotItemAlv as? Int, alv != 0 {
76 return airLevelMaskImage
78 if let lv = slotItemLevel as? Int, lv != 0 {
83 if isCharacterProtrude() {
85 return characterProtrudeMaskImage
91 private var levelMaskImage: CGImage {
93 if let r = SlotItemLevelView.sLevelMaskImage {
97 SlotItemLevelView.sLevelMaskImage = maskImage(middle1: 0.75, middle2: 0.85)
99 return SlotItemLevelView.sLevelMaskImage!
102 private var airLevelMaskImage: CGImage {
104 if let r = SlotItemLevelView.sAirLevelMaskImage {
108 SlotItemLevelView.sAirLevelMaskImage = maskImage(middle1: 0.65, middle2: 0.75)
110 return SlotItemLevelView.sAirLevelMaskImage!
112 private var characterProtrudeMaskImage: CGImage {
114 if let r = SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage {
118 SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage = maskImage(middle1: 0.9, middle2: 1.0)
120 return SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage!
123 private var levelOneBezierPath: Polygon {
125 let width = bounds.width
126 let height = bounds.height
128 return Polygon(lineWidth: 1.0)
129 .move(to: NSPoint(x: width - offset, y: 0))
130 .line(to: NSPoint(x: width - offset, y: height))
133 private var levelTwoBezierPath: Polygon {
135 let width = bounds.width
136 let height = bounds.height
138 return Polygon(lineWidth: 1.0)
139 .move(to: NSPoint(x: width - offset, y: 0))
140 .line(to: NSPoint(x: width - offset, y: height))
141 .move(to: NSPoint(x: width - offset - padding, y: 0))
142 .line(to: NSPoint(x: width - offset - padding, y: height))
145 private var levelThreeBezierPath: Polygon {
147 let width = bounds.width
148 let height = bounds.height
150 return Polygon(lineWidth: 1.0)
151 .move(to: NSPoint(x: width - offset, y: 0))
152 .line(to: NSPoint(x: width - offset, y: height))
153 .move(to: NSPoint(x: width - offset - padding, y: 0))
154 .line(to: NSPoint(x: width - offset - padding, y: height))
155 .move(to: NSPoint(x: width - offset - padding * 2, y: 0))
156 .line(to: NSPoint(x: width - offset - padding * 2, y: height))
159 private var levelFourBezierPath: Polygon {
161 let width = bounds.width
162 let height = bounds.height
164 return Polygon(lineWidth: 2.0)
165 .move(to: NSPoint(x: width - offset - slideOffset, y: 0))
166 .line(to: NSPoint(x: width - offset, y: height))
169 private var levelFiveBezierPath: Polygon {
171 let width = bounds.width
172 let height = bounds.height
174 return Polygon(lineWidth: 2.0)
175 .move(to: NSPoint(x: width - offset - slideOffset, y: 0))
176 .line(to: NSPoint(x: width - offset, y: height))
177 .move(to: NSPoint(x: width - offset - padding - slideOffset, y: 0))
178 .line(to: NSPoint(x: width - offset - padding, y: height))
181 private var levelSixBezierPath: Polygon {
183 let width = bounds.width
184 let height = bounds.height
186 return Polygon(lineWidth: 2.0)
187 .move(to: NSPoint(x: width - offset - slideOffset, y: 0))
188 .line(to: NSPoint(x: width - offset, y: height))
189 .move(to: NSPoint(x: width - offset - padding - slideOffset, y: 0))
190 .line(to: NSPoint(x: width - offset - padding, y: height))
191 .move(to: NSPoint(x: width - offset - padding * 2 - slideOffset, y: 0))
192 .line(to: NSPoint(x: width - offset - padding * 2, y: height))
195 private var levelSevenBezierPath: Polygon {
197 let width = bounds.width
198 let height = bounds.height
200 return Polygon(lineWidth: 2.0)
201 .move(to: NSPoint(x: width - offset - slideOffset, y: 0))
202 .line(to: NSPoint(x: width - offset, y: height * 0.5))
203 .line(to: NSPoint(x: width - offset - anglePoint, y: height))
204 .move(to: NSPoint(x: width - offset - padding - slideOffset, y: 0))
205 .line(to: NSPoint(x: width - offset - padding, y: height * 0.5))
206 .line(to: NSPoint(x: width - offset - padding - anglePoint, y: height))
209 private var levelFont: NSFont {
211 return .monospacedDigitSystemFont(ofSize: NSFont.smallSystemFontSize, weight: .regular)
214 private var levelColor: NSColor {
216 return #colorLiteral(red: 0.135, green: 0.522, blue: 0.619, alpha: 1)
220 override func draw(_ dirtyRect: NSRect) {
222 guard let context = NSGraphicsContext.current?.cgContext else {
224 fatalError("Con not get current CGContext")
228 maskImage.map { context.clip(to: bounds, mask: $0) }
230 super.draw(dirtyRect)
232 context.restoreGState()
238 private func bezierPathForALevel(level: Int) -> Polygon? {
242 case 1: return levelOneBezierPath
244 case 2: return levelTwoBezierPath
246 case 3: return levelThreeBezierPath
248 case 4: return levelFourBezierPath
250 case 5: return levelFiveBezierPath
252 case 6: return levelSixBezierPath
254 case 7: return levelSevenBezierPath
261 private func colorForALevel(level: Int) -> NSColor? {
265 case 1, 2, 3: return #colorLiteral(red: 0.257, green: 0.523, blue: 0.643, alpha: 1)
267 case 4, 5, 6, 7: return #colorLiteral(red: 0.784, green: 0.549, blue: 0.000, alpha: 1)
274 private func shadowForALevel(level: Int) -> NSShadow? {
279 let shadow = NSShadow()
280 shadow.shadowColor = #colorLiteral(red: 0.095, green: 0.364, blue: 0.917, alpha: 1)
281 shadow.shadowBlurRadius = 4.0
286 let shadow = NSShadow()
287 shadow.shadowColor = .yellow
288 shadow.shadowBlurRadius = 3.0
298 private func drawAirLevel() {
300 guard let alv = slotItemAlv as? Int else {
305 colorForALevel(level: alv)?.set()
306 shadowForALevel(level: alv)?.set()
307 bezierPathForALevel(level: alv)?.stroke()
310 private func drawLevel() {
312 guard let lv = slotItemLevel as? Int, lv != 0 else {
317 let string = (lv == 10 ? "max" : "★+\(lv)")
318 let attr: [NSAttributedStringKey: Any] = [.font: levelFont,
319 .foregroundColor: levelColor]
320 let attributedString = NSAttributedString(string: string, attributes: attr)
321 let boundingRect = attributedString.boundingRect(with: bounds.size)
323 rect.origin.x = rect.width - boundingRect.width - 1.0
326 attributedString.draw(in: rect)