|
|
|
@ -114,121 +114,167 @@ public class ImageEditorStrokeItem: ImageEditorItem {
|
|
|
|
|
|
|
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
|
|
// ImageEditorContents represents a snapshot of canvas
|
|
|
|
|
// state.
|
|
|
|
|
//
|
|
|
|
|
// Instances of ImageEditorContents should be treated
|
|
|
|
|
// as immutable, once configured.
|
|
|
|
|
public class ImageEditorContents: NSObject {
|
|
|
|
|
public class OrderedDictionary<ValueType>: NSObject {
|
|
|
|
|
|
|
|
|
|
// This represents the current state of each item.
|
|
|
|
|
var itemMap = [String: ImageEditorItem]()
|
|
|
|
|
public typealias KeyType = String
|
|
|
|
|
|
|
|
|
|
// This represents the back-to-front ordering of the items.
|
|
|
|
|
var itemIds = [String]()
|
|
|
|
|
var keyValueMap = [KeyType: ValueType]()
|
|
|
|
|
|
|
|
|
|
var orderedKeys = [KeyType]()
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public override init() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public init(itemMap: [String: ImageEditorItem],
|
|
|
|
|
itemIds: [String]) {
|
|
|
|
|
// Used to clone copies of instances of this class.
|
|
|
|
|
public init(keyValueMap: [KeyType: ValueType],
|
|
|
|
|
orderedKeys: [KeyType]) {
|
|
|
|
|
|
|
|
|
|
self.itemMap = itemMap
|
|
|
|
|
self.itemIds = itemIds
|
|
|
|
|
self.keyValueMap = keyValueMap
|
|
|
|
|
self.orderedKeys = orderedKeys
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Since the contents are immutable, we only modify copies
|
|
|
|
|
// made with this method.
|
|
|
|
|
@objc
|
|
|
|
|
public func clone() -> ImageEditorContents {
|
|
|
|
|
return ImageEditorContents(itemMap: itemMap, itemIds: itemIds)
|
|
|
|
|
public func clone() -> OrderedDictionary<ValueType> {
|
|
|
|
|
return OrderedDictionary(keyValueMap: keyValueMap, orderedKeys: orderedKeys)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public func append(item: ImageEditorItem) {
|
|
|
|
|
Logger.verbose("\(item.itemId)")
|
|
|
|
|
|
|
|
|
|
if itemMap[item.itemId] != nil {
|
|
|
|
|
owsFail("Unexpected duplicate item in item map: \(item.itemId)")
|
|
|
|
|
public func append(key: KeyType, value: ValueType) {
|
|
|
|
|
if keyValueMap[key] != nil {
|
|
|
|
|
owsFailDebug("Unexpected duplicate key in key map: \(key)")
|
|
|
|
|
}
|
|
|
|
|
itemMap[item.itemId] = item
|
|
|
|
|
keyValueMap[key] = value
|
|
|
|
|
|
|
|
|
|
if itemIds.contains(item.itemId) {
|
|
|
|
|
owsFail("Unexpected duplicate item in item list: \(item.itemId)")
|
|
|
|
|
if orderedKeys.contains(key) {
|
|
|
|
|
owsFailDebug("Unexpected duplicate key in key list: \(key)")
|
|
|
|
|
} else {
|
|
|
|
|
itemIds.append(item.itemId)
|
|
|
|
|
orderedKeys.append(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if itemIds.count != itemMap.count {
|
|
|
|
|
if orderedKeys.count != keyValueMap.count {
|
|
|
|
|
owsFailDebug("Invalid contents.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public func replace(item: ImageEditorItem) {
|
|
|
|
|
Logger.verbose("\(item.itemId)")
|
|
|
|
|
public func replace(key: KeyType, value: ValueType) {
|
|
|
|
|
if keyValueMap[key] == nil {
|
|
|
|
|
owsFailDebug("Missing key in key map: \(key)")
|
|
|
|
|
}
|
|
|
|
|
keyValueMap[key] = value
|
|
|
|
|
|
|
|
|
|
if itemMap[item.itemId] == nil {
|
|
|
|
|
owsFail("Missing item in item map: \(item.itemId)")
|
|
|
|
|
if !orderedKeys.contains(key) {
|
|
|
|
|
owsFailDebug("Missing key in key list: \(key)")
|
|
|
|
|
}
|
|
|
|
|
itemMap[item.itemId] = item
|
|
|
|
|
|
|
|
|
|
if !itemIds.contains(item.itemId) {
|
|
|
|
|
owsFail("Missing item in item list: \(item.itemId)")
|
|
|
|
|
if orderedKeys.count != keyValueMap.count {
|
|
|
|
|
owsFailDebug("Invalid contents.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func remove(key: KeyType) {
|
|
|
|
|
if keyValueMap[key] == nil {
|
|
|
|
|
owsFailDebug("Missing key in key map: \(key)")
|
|
|
|
|
} else {
|
|
|
|
|
keyValueMap.removeValue(forKey: key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !orderedKeys.contains(key) {
|
|
|
|
|
owsFailDebug("Missing key in key list: \(key)")
|
|
|
|
|
} else {
|
|
|
|
|
orderedKeys = orderedKeys.filter { $0 != key }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if orderedKeys.count != keyValueMap.count {
|
|
|
|
|
owsFailDebug("Invalid contents.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if itemIds.count != itemMap.count {
|
|
|
|
|
public var count: Int {
|
|
|
|
|
if orderedKeys.count != keyValueMap.count {
|
|
|
|
|
owsFailDebug("Invalid contents.")
|
|
|
|
|
}
|
|
|
|
|
return orderedKeys.count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func orderedValues() -> [ValueType] {
|
|
|
|
|
var values = [ValueType]()
|
|
|
|
|
for key in orderedKeys {
|
|
|
|
|
guard let value = self.keyValueMap[key] else {
|
|
|
|
|
owsFailDebug("Missing value")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
values.append(value)
|
|
|
|
|
}
|
|
|
|
|
return values
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
|
|
// ImageEditorContents represents a snapshot of canvas
|
|
|
|
|
// state.
|
|
|
|
|
//
|
|
|
|
|
// Instances of ImageEditorContents should be treated
|
|
|
|
|
// as immutable, once configured.
|
|
|
|
|
public class ImageEditorContents: NSObject {
|
|
|
|
|
|
|
|
|
|
public typealias ItemMapType = OrderedDictionary<ImageEditorItem>
|
|
|
|
|
|
|
|
|
|
// This represents the current state of each item,
|
|
|
|
|
// a mapping of [itemId : item].
|
|
|
|
|
var itemMap = ItemMapType()
|
|
|
|
|
|
|
|
|
|
// Used to create an initial, empty instances of this class.
|
|
|
|
|
public override init() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Used to clone copies of instances of this class.
|
|
|
|
|
public init(itemMap: ItemMapType) {
|
|
|
|
|
self.itemMap = itemMap
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Since the contents are immutable, we only modify copies
|
|
|
|
|
// made with this method.
|
|
|
|
|
public func clone() -> ImageEditorContents {
|
|
|
|
|
return ImageEditorContents(itemMap: itemMap.clone())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public func remove(item: ImageEditorItem) {
|
|
|
|
|
public func append(item: ImageEditorItem) {
|
|
|
|
|
Logger.verbose("\(item.itemId)")
|
|
|
|
|
|
|
|
|
|
remove(itemId: item.itemId)
|
|
|
|
|
itemMap.append(key: item.itemId, value: item)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public func remove(itemId: String) {
|
|
|
|
|
if itemMap[itemId] == nil {
|
|
|
|
|
owsFail("Missing item in item map: \(itemId)")
|
|
|
|
|
} else {
|
|
|
|
|
itemMap.removeValue(forKey: itemId)
|
|
|
|
|
}
|
|
|
|
|
public func replace(item: ImageEditorItem) {
|
|
|
|
|
Logger.verbose("\(item.itemId)")
|
|
|
|
|
|
|
|
|
|
if !itemIds.contains(itemId) {
|
|
|
|
|
owsFail("Missing item in item list: \(itemId)")
|
|
|
|
|
} else {
|
|
|
|
|
itemIds = itemIds.filter { $0 != itemId }
|
|
|
|
|
itemMap.replace(key: item.itemId, value: item)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if itemIds.count != itemMap.count {
|
|
|
|
|
owsFailDebug("Invalid contents.")
|
|
|
|
|
@objc
|
|
|
|
|
public func remove(item: ImageEditorItem) {
|
|
|
|
|
Logger.verbose("\(item.itemId)")
|
|
|
|
|
|
|
|
|
|
itemMap.remove(key: item.itemId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public func remove(itemId: String) {
|
|
|
|
|
Logger.verbose("\(itemId)")
|
|
|
|
|
|
|
|
|
|
itemMap.remove(key: itemId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public func itemCount() -> Int {
|
|
|
|
|
if itemIds.count != itemMap.count {
|
|
|
|
|
owsFailDebug("Invalid contents.")
|
|
|
|
|
}
|
|
|
|
|
return itemIds.count
|
|
|
|
|
return itemMap.count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc
|
|
|
|
|
public func items() -> [ImageEditorItem] {
|
|
|
|
|
var items = [ImageEditorItem]()
|
|
|
|
|
for itemId in itemIds {
|
|
|
|
|
guard let item = self.itemMap[itemId] else {
|
|
|
|
|
owsFailDebug("Missing item")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
items.append(item)
|
|
|
|
|
}
|
|
|
|
|
return items
|
|
|
|
|
return itemMap.orderedValues()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|