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 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)
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)
33 unbind(#keyPath(slotItemLevel))
34 unbind(#keyPath(slotItemAlv))
38 dynamic var slotItemController: NSObjectController
39 dynamic var slotItemLevel: NSNumber? {
40 didSet { needsDisplay = true }
42 dynamic var slotItemAlv: NSNumber? {
43 didSet { needsDisplay = true }
45 var slotItemID: NSNumber? {
47 slotItemController.content = nil
48 guard let itemId = slotItemID as? Int else { return }
49 slotItemController.content = ServerDataStore.default.slotItem(by: itemId)
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 }
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!
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!
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))] }
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)) }
82 (NSPoint(x: width - offset - padding, y: 0), NSPoint(x: width - offset - padding, y: height))
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)) }
95 (NSPoint(x: width - offset - padding, y: 0), NSPoint(x: width - offset - padding, y: height))
98 (NSPoint(x: width - offset - padding * 2, y: 0),
99 NSPoint(x: width - offset - padding * 2, y: height))
102 path?.lineWidth = 1.0
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)) }
112 path?.lineWidth = 2.0
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)) }
122 (NSPoint(x: width - offset - padding - slideOffset, y: 0),
123 NSPoint(x: width - offset - padding, y: height))
126 path?.lineWidth = 2.0
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)) }
136 (NSPoint(x: width - offset - padding - slideOffset, y: 0),
137 NSPoint(x: width - offset - padding, y: height))
140 (NSPoint(x: width - offset - padding * 2 - slideOffset, y: 0),
141 NSPoint(x: width - offset - padding * 2, y: height))
144 path?.lineWidth = 2.0
147 private var levelSevenBezierPath: NSBezierPath? {
148 let width = bounds.width
149 let height = bounds.height
150 let path = polyline {
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) }
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) }
162 .map { path?.append($0) }
163 path?.lineWidth = 2.0
166 private var levelFont: NSFont {
167 return NSFont.monospacedDigitSystemFont(ofSize: NSFont.smallSystemFontSize(), weight: NSFontWeightRegular)
169 private var levelColor: NSColor {
170 return NSColor(calibratedRed: 0.135, green: 0.522, blue: 0.619, alpha: 1.0)
174 override func draw(_ dirtyRect: NSRect) {
175 guard let context = NSGraphicsContext.current()?.cgContext
176 else { fatalError("Con not get current CGContext") }
178 maskImage.map { context.clip(to: bounds, mask: $0) }
179 super.draw(dirtyRect)
180 context.restoreGState()
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),
191 bytesPerRow: Int(bounds.width),
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)
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") }
209 private func bezierPathForALevel(level: Int) -> NSBezierPath? {
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
221 private func colorForALevel(level: Int) -> NSColor? {
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)
228 private func shadowForALevel(level: Int) -> NSShadow? {
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
236 let shadow = NSShadow()
237 shadow.shadowColor = NSColor.yellow
238 shadow.shadowBlurRadius = 3.0
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()
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)
258 rect.origin.x = rect.width - boundingRect.width - 1.0
260 attributedString.draw(in: rect)