OSDN Git Service

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