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 class SlotItemLevelView: NSTextField {
12     private static var sLevelMaskImage: CGImage?
13     private static var sAirLevelMaskImage: CGImage?
14     
15     private let offset: CGFloat = 28
16     private let padding: CGFloat = 4
17     private let slideOffset: CGFloat = 3.5
18     private let anglePoint: CGFloat = 4.0
19     
20     override init(frame frameRect: NSRect) {
21         super.init(frame: frameRect)
22         bind(#keyPath(slotItemLevel), to: slotItemController, withKeyPath: "selection.level", options: nil)
23         bind(#keyPath(slotItemAlv), to: slotItemController, withKeyPath: "selection.alv", options: nil)
24     }
25     required init?(coder: NSCoder) {
26         super.init(coder: coder)
27         bind(#keyPath(slotItemLevel), to: slotItemController, withKeyPath: "selection.level", options: nil)
28         bind(#keyPath(slotItemAlv), to: slotItemController, withKeyPath: "selection.alv", options: nil)
29     }
30     deinit {
31         unbind(#keyPath(slotItemLevel))
32         unbind(#keyPath(slotItemAlv))
33     }
34     
35     // MARK: - Variable
36     dynamic var slotItemController = NSObjectController()
37     dynamic var slotItemLevel: NSNumber? {
38         didSet { needsDisplay = true }
39     }
40     dynamic var slotItemAlv: NSNumber? {
41         didSet { needsDisplay = true }
42     }
43     var slotItemID: NSNumber? {
44         didSet {
45             slotItemController.content = nil
46             guard let itemId = slotItemID as? Int else { return }
47             slotItemController.content = ServerDataStore.default.slotItem(by: itemId)
48             needsDisplay = true
49         }
50     }
51     private var maskImage: CGImage? {
52         if let alv = slotItemAlv as? Int, alv != 0 { return airLevelMaskImage }
53         if let lv = slotItemLevel as? Int, lv != 0 { return levelMaskImage }
54         return nil
55     }
56     private var levelMaskImage: CGImage {
57         if let r = SlotItemLevelView.sLevelMaskImage { return r }
58         SlotItemLevelView.sLevelMaskImage = maskImage(middle1: 0.75, middle2: 0.85)
59         return SlotItemLevelView.sLevelMaskImage!
60     }
61     private var airLevelMaskImage: CGImage {
62         if let r = SlotItemLevelView.sAirLevelMaskImage { return r }
63         SlotItemLevelView.sAirLevelMaskImage = maskImage(middle1: 0.65, middle2: 0.75)
64         return SlotItemLevelView.sAirLevelMaskImage!
65     }
66     private var levelOneBezierPath: NSBezierPath? {
67         let width = bounds.width
68         let height = bounds.height
69         let path = multiline { [(NSPoint(x: width - offset, y: 0), NSPoint(x: width - offset, y: height))] }
70         path.lineWidth = 1.0
71         return path
72     }
73     private var levelTwoBezierPath: NSBezierPath? {
74         let width = bounds.width
75         let height = bounds.height
76         let path = multiline {
77             [(NSPoint, NSPoint)]()
78                 .appended { (NSPoint(x: width - offset, y: 0), NSPoint(x: width - offset, y: height)) }
79                 .appended {
80                     (NSPoint(x: width - offset - padding, y: 0), NSPoint(x: width - offset - padding, y: height))
81             }
82         }
83         path.lineWidth = 1.0
84         return path
85     }
86     private var levelThreeBezierPath: NSBezierPath? {
87         let width = bounds.width
88         let height = bounds.height
89         let path = multiline {
90             [(NSPoint, NSPoint)]()
91                 .appended { (NSPoint(x: width - offset, y: 0), NSPoint(x: width - offset, y: height)) }
92                 .appended {
93                     (NSPoint(x: width - offset - padding, y: 0), NSPoint(x: width - offset - padding, y: height))
94                 }
95                 .appended {
96                     (NSPoint(x: width - offset - padding * 2, y: 0),
97                      NSPoint(x: width - offset - padding * 2, y: height))
98             }
99         }
100         path.lineWidth = 1.0
101         return path
102     }
103     private var levelFourBezierPath: NSBezierPath? {
104         let width = bounds.width
105         let height = bounds.height
106         let path = multiline {
107             [(NSPoint, NSPoint)]()
108                 .appended { (NSPoint(x: width - offset - slideOffset, y: 0), NSPoint(x: width - offset, y: height)) }
109         }
110         path.lineWidth = 2.0
111         return path
112     }
113     private var levelFiveBezierPath: NSBezierPath? {
114         let width = bounds.width
115         let height = bounds.height
116         let path = multiline {
117             [(NSPoint, NSPoint)]()
118                 .appended { (NSPoint(x: width - offset - slideOffset, y: 0), NSPoint(x: width - offset, y: height)) }
119                 .appended {
120                     (NSPoint(x: width - offset - padding - slideOffset, y: 0),
121                      NSPoint(x: width - offset - padding, y: height))
122             }
123         }
124         path.lineWidth = 2.0
125         return path
126     }
127     private var levelSixBezierPath: NSBezierPath? {
128         let width = bounds.width
129         let height = bounds.height
130         let path = multiline {
131             [(NSPoint, NSPoint)]()
132                 .appended { (NSPoint(x: width - offset - slideOffset, y: 0), NSPoint(x: width - offset, y: height)) }
133                 .appended {
134                     (NSPoint(x: width - offset - padding - slideOffset, y: 0),
135                      NSPoint(x: width - offset - padding, y: height))
136                 }
137                 .appended {
138                     (NSPoint(x: width - offset - padding * 2 - slideOffset, y: 0),
139                      NSPoint(x: width - offset - padding * 2, y: height))
140             }
141         }
142         path.lineWidth = 2.0
143         return path
144     }
145     private var levelSevenBezierPath: NSBezierPath? {
146         let width = bounds.width
147         let height = bounds.height
148         let path = polyline {
149             [NSPoint]()
150                 .appended { NSPoint(x: width - offset - slideOffset, y: 0) }
151                 .appended { NSPoint(x: width - offset, y: height * 0.5) }
152                 .appended { NSPoint(x: width - offset - anglePoint, y: height) }
153         }
154         polyline {
155             [NSPoint]()
156                 .appended { NSPoint(x: width - offset - padding - slideOffset, y: 0) }
157                 .appended { NSPoint(x: width - offset - padding, y: height * 0.5) }
158                 .appended { NSPoint(x: width - offset - padding - anglePoint, y: height) }
159             }
160             .map { path?.append($0) }
161         path?.lineWidth = 2.0
162         return path
163     }
164     private var levelFont: NSFont {
165         return NSFont.monospacedDigitSystemFont(ofSize: NSFont.smallSystemFontSize(), weight: NSFontWeightRegular)
166     }
167     private var levelColor: NSColor {
168         return #colorLiteral(red: 0.135, green: 0.522, blue: 0.619, alpha: 1)
169     }
170     
171     // MARK: - Function
172     override func draw(_ dirtyRect: NSRect) {
173         guard let context = NSGraphicsContext.current()?.cgContext
174             else { fatalError("Con not get current CGContext") }
175         context.saveGState()
176         maskImage.map { context.clip(to: bounds, mask: $0) }
177         super.draw(dirtyRect)
178         context.restoreGState()
179         
180         drawLevel()
181         drawAirLevel()
182     }
183     private func maskImage(middle1: CGFloat, middle2: CGFloat) -> CGImage {
184         let colorspace = CGColorSpaceCreateDeviceGray()
185         guard let maskContext = CGContext(data: nil,
186                                           width: Int(bounds.width),
187                                           height: Int(bounds.height),
188                                           bitsPerComponent: 8,
189                                           bytesPerRow: Int(bounds.width),
190                                           space: colorspace,
191                                           bitmapInfo: 0)
192             else { fatalError("Can not create bitmap context") }
193         let maskGraphicsContext = NSGraphicsContext(cgContext: maskContext, flipped: false)
194         NSGraphicsContext.saveGraphicsState()
195         defer { NSGraphicsContext.restoreGraphicsState() }
196         NSGraphicsContext.setCurrent(maskGraphicsContext)
197         
198         let gradient = NSGradient(colorsAndLocations: (NSColor.white, 0.0),
199                                   (NSColor.white, middle1),
200                                   (NSColor.black, middle2),
201                                   (NSColor.black, 1.0))
202         gradient?.draw(in: bounds, angle: 0.0)
203         guard let r = maskContext.makeImage()
204             else { fatalError(" can not create image from context") }
205         return r
206     }
207     private func bezierPathForALevel(level: Int) -> NSBezierPath? {
208         switch level {
209         case 1: return levelOneBezierPath
210         case 2: return levelTwoBezierPath
211         case 3: return levelThreeBezierPath
212         case 4: return levelFourBezierPath
213         case 5: return levelFiveBezierPath
214         case 6: return levelSixBezierPath
215         case 7: return levelSevenBezierPath
216         default: return nil
217         }
218     }
219     private func colorForALevel(level: Int) -> NSColor? {
220         switch level {
221         case 1, 2, 3: return #colorLiteral(red: 0.257, green: 0.523, blue: 0.643, alpha: 1)
222         case 4, 5, 6, 7: return #colorLiteral(red: 0.784, green: 0.549, blue: 0.000, alpha: 1)
223         default: return nil
224         }
225     }
226     private func shadowForALevel(level: Int) -> NSShadow? {
227         switch level {
228         case 1, 2, 3:
229             let shadow = NSShadow()
230             shadow.shadowColor = #colorLiteral(red: 0.095, green: 0.364, blue: 0.917, alpha: 1)
231             shadow.shadowBlurRadius = 4.0
232             return shadow
233         case 4, 5, 6, 7:
234             let shadow = NSShadow()
235             shadow.shadowColor = NSColor.yellow
236             shadow.shadowBlurRadius = 3.0
237             return shadow
238         default:
239             return nil
240         }
241     }
242     private func drawAirLevel() {
243         guard let alv = slotItemAlv as? Int else { return }
244         colorForALevel(level: alv)?.set()
245         shadowForALevel(level: alv)?.set()
246         bezierPathForALevel(level: alv)?.stroke()
247     }
248     private func drawLevel() {
249         guard let lv = slotItemLevel as? Int, lv != 0 else { return }
250         let string: String = lv == 10 ? "max" : "★+\(lv)"
251         let attr: [String: Any] = [ NSFontAttributeName: levelFont,
252                                      NSForegroundColorAttributeName: levelColor ]
253         let attributedString = NSAttributedString(string: string, attributes: attr)
254         let boundingRect = attributedString.boundingRect(with: bounds.size)
255         var rect = bounds
256         rect.origin.x = rect.width - boundingRect.width - 1.0
257         rect.origin.y = 0
258         attributedString.draw(in: rect)
259     }
260 }