OSDN Git Service

コーディングスタイルを変更
[kcd/KCD.git] / KCD / SlotItemLevelView.swift
1 //
2 //  SlotItemLevelView.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2017/01/02.
6 //  Copyright © 2017年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 final class SlotItemLevelView: NSTextField {
12     
13     private static var sLevelMaskImage: CGImage?
14     private static var sAirLevelMaskImage: CGImage?
15     private static var sCharacterProtrudeMaskImageMaskImage: CGImage?
16     
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
21     
22     override init(frame frameRect: NSRect) {
23         
24         super.init(frame: frameRect)
25         
26         bind(NSBindingName(#keyPath(slotItemLevel)), to: slotItemController, withKeyPath: "selection.level", options: nil)
27         bind(NSBindingName(#keyPath(slotItemAlv)), to: slotItemController, withKeyPath: "selection.alv", options: nil)
28     }
29     
30     required init?(coder: NSCoder) {
31         
32         super.init(coder: coder)
33         
34         bind(NSBindingName(#keyPath(slotItemLevel)), to: slotItemController, withKeyPath: "selection.level", options: nil)
35         bind(NSBindingName(#keyPath(slotItemAlv)), to: slotItemController, withKeyPath: "selection.alv", options: nil)
36     }
37     
38     deinit {
39         
40         unbind(NSBindingName(#keyPath(slotItemLevel)))
41         unbind(NSBindingName(#keyPath(slotItemAlv)))
42     }
43     
44     // MARK: - Variable
45     @objc dynamic var slotItemController = NSObjectController()
46     @objc dynamic var slotItemLevel: NSNumber? {
47         
48         didSet { needsDisplayInMainThread() }
49     }
50     
51     @objc dynamic var slotItemAlv: NSNumber? {
52         
53         didSet { needsDisplayInMainThread() }
54     }
55     
56     @objc var slotItemID: NSNumber? {
57         
58         didSet {
59             
60             slotItemController.content = nil
61             
62             guard let itemId = slotItemID as? Int else {
63                 
64                 return
65             }
66             
67             slotItemController.content = ServerDataStore.default.slotItem(by: itemId)
68             needsDisplayInMainThread()
69         }
70     }
71     
72     private var maskImage: CGImage? {
73         
74         if let alv = slotItemAlv as? Int, alv != 0 {
75             
76             return airLevelMaskImage
77         }
78         if let lv = slotItemLevel as? Int, lv != 0 {
79             
80             return levelMaskImage
81         }
82         
83         if isCharacterProtrude() {
84             
85             return characterProtrudeMaskImage
86         }
87         
88         return nil
89     }
90     
91     private var levelMaskImage: CGImage {
92         
93         if let r = SlotItemLevelView.sLevelMaskImage {
94             
95             return r
96         }
97         SlotItemLevelView.sLevelMaskImage = maskImage(middle1: 0.75, middle2: 0.85)
98         
99         return SlotItemLevelView.sLevelMaskImage!
100     }
101     
102     private var airLevelMaskImage: CGImage {
103         
104         if let r = SlotItemLevelView.sAirLevelMaskImage {
105             
106             return r
107         }
108         SlotItemLevelView.sAirLevelMaskImage = maskImage(middle1: 0.65, middle2: 0.75)
109         
110         return SlotItemLevelView.sAirLevelMaskImage!
111     }
112     private var characterProtrudeMaskImage: CGImage {
113         
114         if let r = SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage {
115             
116             return r
117         }
118         SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage = maskImage(middle1: 0.9, middle2: 1.0)
119         
120         return SlotItemLevelView.sCharacterProtrudeMaskImageMaskImage!
121     }
122     
123     private var levelOneBezierPath: Polygon {
124         
125         let width = bounds.width
126         let height = bounds.height
127         
128         return Polygon(lineWidth: 1.0)
129             .move(to: NSPoint(x: width - offset, y: 0))
130             .line(to: NSPoint(x: width - offset, y: height))
131     }
132     
133     private var levelTwoBezierPath: Polygon {
134         
135         let width = bounds.width
136         let height = bounds.height
137         
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))
143     }
144     
145     private var levelThreeBezierPath: Polygon {
146         
147         let width = bounds.width
148         let height = bounds.height
149         
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))
157     }
158     
159     private var levelFourBezierPath: Polygon {
160         
161         let width = bounds.width
162         let height = bounds.height
163         
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))
167     }
168     
169     private var levelFiveBezierPath: Polygon {
170         
171         let width = bounds.width
172         let height = bounds.height
173         
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))
179     }
180     
181     private var levelSixBezierPath: Polygon {
182         
183         let width = bounds.width
184         let height = bounds.height
185         
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))
193     }
194     
195     private var levelSevenBezierPath: Polygon {
196         
197         let width = bounds.width
198         let height = bounds.height
199         
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))
207     }
208     
209     private var levelFont: NSFont {
210         
211         return .monospacedDigitSystemFont(ofSize: NSFont.smallSystemFontSize, weight: .regular)
212     }
213     
214     private var levelColor: NSColor {
215         
216         return #colorLiteral(red: 0.135, green: 0.522, blue: 0.619, alpha: 1)
217     }
218     
219     // MARK: - Function
220     override func draw(_ dirtyRect: NSRect) {
221         
222         guard let context = NSGraphicsContext.current?.cgContext else {
223             
224             fatalError("Con not get current CGContext")
225         }
226         
227         context.saveGState()
228         maskImage.map { context.clip(to: bounds, mask: $0) }
229         
230         super.draw(dirtyRect)
231         
232         context.restoreGState()
233         
234         drawLevel()
235         drawAirLevel()
236     }
237     
238     private func bezierPathForALevel(level: Int) -> Polygon? {
239         
240         switch level {
241             
242         case 1: return levelOneBezierPath
243             
244         case 2: return levelTwoBezierPath
245             
246         case 3: return levelThreeBezierPath
247             
248         case 4: return levelFourBezierPath
249             
250         case 5: return levelFiveBezierPath
251             
252         case 6: return levelSixBezierPath
253             
254         case 7: return levelSevenBezierPath
255             
256         default: return nil
257             
258         }
259     }
260     
261     private func colorForALevel(level: Int) -> NSColor? {
262         
263         switch level {
264             
265         case 1, 2, 3: return #colorLiteral(red: 0.257, green: 0.523, blue: 0.643, alpha: 1)
266             
267         case 4, 5, 6, 7: return #colorLiteral(red: 0.784, green: 0.549, blue: 0.000, alpha: 1)
268             
269         default: return nil
270             
271         }
272     }
273     
274     private func shadowForALevel(level: Int) -> NSShadow? {
275         
276         switch level {
277             
278         case 1, 2, 3:
279             let shadow = NSShadow()
280             shadow.shadowColor = #colorLiteral(red: 0.095, green: 0.364, blue: 0.917, alpha: 1)
281             shadow.shadowBlurRadius = 4.0
282             
283             return shadow
284             
285         case 4, 5, 6, 7:
286             let shadow = NSShadow()
287             shadow.shadowColor = .yellow
288             shadow.shadowBlurRadius = 3.0
289             
290             return shadow
291             
292         default:
293             
294             return nil
295         }
296     }
297     
298     private func drawAirLevel() {
299         
300         guard let alv = slotItemAlv as? Int else {
301             
302             return
303         }
304         
305         colorForALevel(level: alv)?.set()
306         shadowForALevel(level: alv)?.set()
307         bezierPathForALevel(level: alv)?.stroke()
308     }
309     
310     private func drawLevel() {
311         
312         guard let lv = slotItemLevel as? Int, lv != 0 else {
313             
314             return
315         }
316         
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)
322         var rect = bounds
323         rect.origin.x = rect.width - boundingRect.width - 1.0
324         rect.origin.y = 0
325         
326         attributedString.draw(in: rect)
327     }
328 }