2 // SlotItemLevelView.swift
5 // Created by Hori,Masaki on 2017/01/02.
6 // Copyright © 2017年 Hori,Masaki. All rights reserved.
11 class SlotItemLevelView: NSTextField {
12 private static var sLevelMaskImage: CGImage?
13 private static var sAirLevelMaskImage: CGImage?
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
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)
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)
31 unbind(#keyPath(slotItemLevel))
32 unbind(#keyPath(slotItemAlv))
36 dynamic var slotItemController = NSObjectController()
37 dynamic var slotItemLevel: NSNumber? {
38 didSet { needsDisplay = true }
40 dynamic var slotItemAlv: NSNumber? {
41 didSet { needsDisplay = true }
43 var slotItemID: NSNumber? {
45 slotItemController.content = nil
46 guard let itemId = slotItemID as? Int else { return }
47 slotItemController.content = ServerDataStore.default.slotItem(by: itemId)
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 }
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!
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!
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))] }
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)) }
80 (NSPoint(x: width - offset - padding, y: 0), NSPoint(x: width - offset - padding, y: height))
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)) }
93 (NSPoint(x: width - offset - padding, y: 0), NSPoint(x: width - offset - padding, y: height))
96 (NSPoint(x: width - offset - padding * 2, y: 0),
97 NSPoint(x: width - offset - padding * 2, y: height))
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)) }
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)) }
120 (NSPoint(x: width - offset - padding - slideOffset, y: 0),
121 NSPoint(x: width - offset - padding, y: height))
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)) }
134 (NSPoint(x: width - offset - padding - slideOffset, y: 0),
135 NSPoint(x: width - offset - padding, y: height))
138 (NSPoint(x: width - offset - padding * 2 - slideOffset, y: 0),
139 NSPoint(x: width - offset - padding * 2, y: height))
145 private var levelSevenBezierPath: NSBezierPath? {
146 let width = bounds.width
147 let height = bounds.height
148 let path = polyline {
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) }
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) }
160 .map { path?.append($0) }
161 path?.lineWidth = 2.0
164 private var levelFont: NSFont {
165 return NSFont.monospacedDigitSystemFont(ofSize: NSFont.smallSystemFontSize(), weight: NSFontWeightRegular)
167 private var levelColor: NSColor {
168 return #colorLiteral(red: 0.135, green: 0.522, blue: 0.619, alpha: 1)
172 override func draw(_ dirtyRect: NSRect) {
173 guard let context = NSGraphicsContext.current()?.cgContext
174 else { fatalError("Con not get current CGContext") }
176 maskImage.map { context.clip(to: bounds, mask: $0) }
177 super.draw(dirtyRect)
178 context.restoreGState()
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),
189 bytesPerRow: Int(bounds.width),
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)
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") }
207 private func bezierPathForALevel(level: Int) -> NSBezierPath? {
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
219 private func colorForALevel(level: Int) -> NSColor? {
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)
226 private func shadowForALevel(level: Int) -> NSShadow? {
229 let shadow = NSShadow()
230 shadow.shadowColor = #colorLiteral(red: 0.095, green: 0.364, blue: 0.917, alpha: 1)
231 shadow.shadowBlurRadius = 4.0
234 let shadow = NSShadow()
235 shadow.shadowColor = NSColor.yellow
236 shadow.shadowBlurRadius = 3.0
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()
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)
256 rect.origin.x = rect.width - boundingRect.width - 1.0
258 attributedString.draw(in: rect)