Skip to content
This repository was archived by the owner on Jun 11, 2024. It is now read-only.

Commit 78b4ee5

Browse files
committedFeb 3, 2022
optimize
1 parent 1983a90 commit 78b4ee5

26 files changed

+198
-124
lines changed
 

‎CoreUtil/CoreUtil.xcodeproj/project.pbxproj

+8
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
B680A8A427A8DA78007CB707 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = B680A8A327A8DA78007CB707 /* Collections */; };
5151
B6AC27A327AA6F5C000FD713 /* Reachability+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6AC27A127AA6F5B000FD713 /* Reachability+Publisher.swift */; };
5252
B6AC27A427AA6F5C000FD713 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6AC27A227AA6F5C000FD713 /* Reachability.swift */; };
53+
B6B5727A27AC223A0069DBA7 /* RestorableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B5727927AC223A0069DBA7 /* RestorableState.swift */; };
54+
B6B5727D27AC22480069DBA7 /* RestorableData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B5727C27AC22480069DBA7 /* RestorableData.swift */; };
5355
B6D1AF3D27A60A210022FED2 /* ExceptionHanlder.m in Sources */ = {isa = PBXBuildFile; fileRef = B6D1AF3A27A60A210022FED2 /* ExceptionHanlder.m */; };
5456
B6D1AF3E27A60A210022FED2 /* ExceptionHanlder.h in Headers */ = {isa = PBXBuildFile; fileRef = B6D1AF3B27A60A210022FED2 /* ExceptionHanlder.h */; settings = {ATTRIBUTES = (Public, ); }; };
5557
B6D1AF3F27A60A210022FED2 /* ExceptionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6D1AF3C27A60A210022FED2 /* ExceptionHandler.swift */; };
@@ -96,6 +98,8 @@
9698
B680A89B27A8DA16007CB707 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = "<group>"; };
9799
B6AC27A127AA6F5B000FD713 /* Reachability+Publisher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reachability+Publisher.swift"; sourceTree = "<group>"; };
98100
B6AC27A227AA6F5C000FD713 /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = "<group>"; };
101+
B6B5727927AC223A0069DBA7 /* RestorableState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorableState.swift; sourceTree = "<group>"; };
102+
B6B5727C27AC22480069DBA7 /* RestorableData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorableData.swift; sourceTree = "<group>"; };
99103
B6D1AF3A27A60A210022FED2 /* ExceptionHanlder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExceptionHanlder.m; sourceTree = "<group>"; };
100104
B6D1AF3B27A60A210022FED2 /* ExceptionHanlder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExceptionHanlder.h; sourceTree = "<group>"; };
101105
B6D1AF3C27A60A210022FED2 /* ExceptionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExceptionHandler.swift; sourceTree = "<group>"; };
@@ -221,6 +225,8 @@
221225
B64B1ED827A4F73700AC2601 /* Observable.swift */,
222226
B64B1EDB27A4F75600AC2601 /* PipeOperator.swift */,
223227
B64B1ED227A4F71F00AC2601 /* NS+OnAwake.swift */,
228+
B6B5727927AC223A0069DBA7 /* RestorableState.swift */,
229+
B6B5727C27AC22480069DBA7 /* RestorableData.swift */,
224230
);
225231
path = Class;
226232
sourceTree = "<group>";
@@ -350,6 +356,7 @@
350356
B6AC27A327AA6F5C000FD713 /* Reachability+Publisher.swift in Sources */,
351357
B64B1F2E27A4F83500AC2601 /* Ex+NSControl.swift in Sources */,
352358
B64B1F1627A4F80800AC2601 /* Ex+CGSize.swift in Sources */,
359+
B6B5727A27AC223A0069DBA7 /* RestorableState.swift in Sources */,
353360
B64B1ED927A4F73700AC2601 /* Observable.swift in Sources */,
354361
B64B201127A50E5200AC2601 /* NSColorView.swift in Sources */,
355362
B64B1EE327A4F79100AC2601 /* NSEvent+HotKey.swift in Sources */,
@@ -377,6 +384,7 @@
377384
B6D1AF3F27A60A210022FED2 /* ExceptionHandler.swift in Sources */,
378385
B64B1ED327A4F71F00AC2601 /* NS+OnAwake.swift in Sources */,
379386
B6AC27A427AA6F5C000FD713 /* Reachability.swift in Sources */,
387+
B6B5727D27AC22480069DBA7 /* RestorableData.swift in Sources */,
380388
);
381389
runOnlyForDeploymentPostprocessing = 0;
382390
};

‎CoreUtil/CoreUtil/Class/Observable.swift

-70
Original file line numberDiff line numberDiff line change
@@ -35,73 +35,3 @@ public struct Observable<Value> {
3535
self.projectedValue = Publisher(value)
3636
}
3737
}
38-
39-
@propertyWrapper
40-
public struct RestorableState<Value: RawRepresentable> {
41-
42-
public struct Publisher: Combine.Publisher {
43-
public typealias Output = Value
44-
public typealias Failure = Never
45-
46-
let subject: CurrentValueSubject<Value, Never>
47-
48-
init(_ value: Value) { self.subject = CurrentValueSubject(value) }
49-
50-
public func receive<S: Subscriber>(subscriber: S) where S.Failure == Self.Failure, S.Input == Self.Output {
51-
self.subject.receive(subscriber: subscriber)
52-
}
53-
}
54-
55-
public let projectedValue: Publisher
56-
public let key: String
57-
58-
public var wrappedValue: Value {
59-
get { projectedValue.subject.value }
60-
set {
61-
projectedValue.subject.send(newValue)
62-
UserDefaults.standard.set(newValue.rawValue, forKey: key)
63-
}
64-
}
65-
public init(wrappedValue initialValue: Value, _ key: String) {
66-
let wrappedValue: Value
67-
68-
if let rawValue = UserDefaults.standard.object(forKey: key) as? Value.RawValue, let value = Value(rawValue: rawValue) {
69-
wrappedValue = value
70-
} else {
71-
wrappedValue = initialValue
72-
}
73-
74-
self.projectedValue = Publisher(wrappedValue)
75-
self.key = key
76-
}
77-
}
78-
79-
extension String: RawRepresentable {
80-
public var rawValue: Self { self }
81-
public init(rawValue: Self) { self = rawValue }
82-
}
83-
84-
extension Bool: RawRepresentable {
85-
public var rawValue: Self { self }
86-
public init(rawValue: Self) { self = rawValue }
87-
}
88-
89-
extension Optional: RawRepresentable {
90-
public var rawValue: Self { self }
91-
public init(rawValue: Self) { self = rawValue }
92-
}
93-
94-
extension Int: RawRepresentable {
95-
public var rawValue: Self { self }
96-
public init(rawValue: Self) { self = rawValue }
97-
}
98-
99-
extension CGFloat: RawRepresentable {
100-
public var rawValue: Self { self }
101-
public init(rawValue: Self) { self = rawValue }
102-
}
103-
104-
extension Double: RawRepresentable {
105-
public var rawValue: Self { self }
106-
public init(rawValue: Self) { self = rawValue }
107-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//
2+
// RestorableData.swift
3+
// CoreUtil
4+
//
5+
// Created by yuki on 2022/02/03.
6+
//
7+
8+
import Cocoa
9+
10+
private let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(Bundle.main.bundleIdentifier ?? "com.noname.app")
11+
12+
private let encoder = JSONEncoder()
13+
private let decoder = JSONDecoder()
14+
private let restorableDataURL = temporaryDirectoryURL.appendingPathComponent("RestorableData") => {
15+
try? FileManager.default.createDirectory(at: $0, withIntermediateDirectories: true, attributes: nil)
16+
}
17+
18+
@propertyWrapper
19+
public struct RestorableData<Value: Codable> {
20+
public struct Publisher: Combine.Publisher {
21+
public typealias Output = Value
22+
public typealias Failure = Never
23+
24+
let subject: CurrentValueSubject<Value, Never>
25+
26+
init(_ value: Value) { self.subject = CurrentValueSubject(value) }
27+
28+
public func receive<S: Subscriber>(subscriber: S) where S.Failure == Self.Failure, S.Input == Self.Output {
29+
self.subject.receive(subscriber: subscriber)
30+
}
31+
}
32+
33+
public let projectedValue: Publisher
34+
public let key: String
35+
public let fileURL: URL
36+
37+
public var wrappedValue: Value {
38+
get { projectedValue.subject.value }
39+
set {
40+
projectedValue.subject.send(newValue)
41+
do { try encoder.encode(newValue).write(to: fileURL) } catch {}
42+
}
43+
}
44+
public init(wrappedValue initialValue: Value, _ key: String) {
45+
self.key = key
46+
self.fileURL = restorableDataURL.appendingPathComponent(key + ".json")
47+
48+
let wrappedValue: Value
49+
do {
50+
wrappedValue = try decoder.decode(Value.self, from: Data(contentsOf: fileURL))
51+
} catch {
52+
wrappedValue = initialValue
53+
}
54+
55+
self.projectedValue = Publisher(wrappedValue)
56+
}
57+
}
58+
59+
final public class NSImageContainer: Codable {
60+
static let dataDirectoryURL = temporaryDirectoryURL.appendingPathComponent("NSImageContainer") => {
61+
try? FileManager.default.createDirectory(at: $0, withIntermediateDirectories: true, attributes: nil)
62+
}
63+
64+
public let image: NSImage
65+
public let id: String
66+
public init(_ image: NSImage) {
67+
self.image = image
68+
self.id = UUID().uuidString
69+
}
70+
71+
public static func wrap(_ image: NSImage) -> NSImageContainer { NSImageContainer(image) }
72+
73+
public func encode(to encoder: Encoder) throws {
74+
var container = encoder.singleValueContainer()
75+
try container.encode(id)
76+
let fileURL = NSImageContainer.dataDirectoryURL.appendingPathComponent(id)
77+
if !FileManager.default.fileExists(atPath: fileURL.path) {
78+
try image.tiffRepresentation?.write(to: fileURL)
79+
}
80+
}
81+
public init(from decoder: Decoder) throws {
82+
let container = try decoder.singleValueContainer()
83+
let id = try container.decode(String.self)
84+
let fileURL = NSImageContainer.dataDirectoryURL.appendingPathComponent(id)
85+
guard let image = NSImage(contentsOf: fileURL) else {
86+
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "No image"))
87+
}
88+
self.image = image
89+
self.id = id
90+
}
91+
}

‎DevToys/DevToys.xcodeproj/project.pbxproj

+4-4
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
B64B202627A52DB400AC2601 /* ToolType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B64B202527A52DB400AC2601 /* ToolType.swift */; };
3636
B64B209827A532DF00AC2601 /* AppWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B64B209727A532DF00AC2601 /* AppWindowController.swift */; };
3737
B65DB78027AC0EB400146A3C /* RegexTesterView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65DB77F27AC0EB400146A3C /* RegexTesterView+.swift */; };
38-
B66850EE27A64D3200A3FE01 /* ToolPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66850ED27A64D3200A3FE01 /* ToolPage.swift */; };
38+
B66850EE27A64D3200A3FE01 /* Page.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66850ED27A64D3200A3FE01 /* Page.swift */; };
3939
B66850F227A65FC200A3FE01 /* SwiftJSONFormatter in Frameworks */ = {isa = PBXBuildFile; productRef = B66850F127A65FC200A3FE01 /* SwiftJSONFormatter */; };
4040
B672CF6527AA763100391A5D /* NetworkSpeedTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B672CF6427AA763100391A5D /* NetworkSpeedTest.swift */; };
4141
B672CF9727AA7E2C00391A5D /* APITestView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = B672CF9627AA7E2C00391A5D /* APITestView+.swift */; };
@@ -130,7 +130,7 @@
130130
B64B202527A52DB400AC2601 /* ToolType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolType.swift; sourceTree = "<group>"; };
131131
B64B209727A532DF00AC2601 /* AppWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppWindowController.swift; sourceTree = "<group>"; };
132132
B65DB77F27AC0EB400146A3C /* RegexTesterView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RegexTesterView+.swift"; sourceTree = "<group>"; };
133-
B66850ED27A64D3200A3FE01 /* ToolPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolPage.swift; sourceTree = "<group>"; };
133+
B66850ED27A64D3200A3FE01 /* Page.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Page.swift; sourceTree = "<group>"; };
134134
B672CF6427AA763100391A5D /* NetworkSpeedTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSpeedTest.swift; sourceTree = "<group>"; };
135135
B672CF9627AA7E2C00391A5D /* APITestView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APITestView+.swift"; sourceTree = "<group>"; };
136136
B672CF9F27AAB8DD00391A5D /* FileDrop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDrop.swift; sourceTree = "<group>"; };
@@ -379,7 +379,7 @@
379379
B6D1AEF927A557280022FED2 /* PopupButton.swift */,
380380
B6D1AEFC27A55ED50022FED2 /* CodeTextView.swift */,
381381
B672CF9F27AAB8DD00391A5D /* FileDrop.swift */,
382-
B66850ED27A64D3200A3FE01 /* ToolPage.swift */,
382+
B66850ED27A64D3200A3FE01 /* Page.swift */,
383383
B608541F27A67E4D003BF243 /* TextField.swift */,
384384
B680A83227A8BF7D007CB707 /* TextViewSection.swift */,
385385
B680A86727A8D4D1007CB707 /* TextFieldSection.swift */,
@@ -519,7 +519,7 @@
519519
B64B1F7727A4FF8B00AC2601 /* ToolMenuView+.swift in Sources */,
520520
B64B202327A52A2D00AC2601 /* R.swift in Sources */,
521521
B64B201D27A5219400AC2601 /* SidebarSearchView+.swift in Sources */,
522-
B66850EE27A64D3200A3FE01 /* ToolPage.swift in Sources */,
522+
B66850EE27A64D3200A3FE01 /* Page.swift in Sources */,
523523
B6D1AEF427A54C440022FED2 /* Section.swift in Sources */,
524524
B64B1F7227A4FDC800AC2601 /* AppModel.swift in Sources */,
525525
B6D1AEFA27A557280022FED2 /* PopupButton.swift in Sources */,

‎DevToys/DevToys/Body/Coder/Base64Decoder.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private enum SourceType: String, TextItem {
7373
var title: String { rawValue }
7474
}
7575

76-
final private class Base64DecoderView: ToolPage {
76+
final private class Base64DecoderView: Page {
7777
let sourceTypePicker = EnumPopupButton<SourceType>()
7878

7979
let fileDrop = FileDrop()

‎DevToys/DevToys/Body/Coder/HTMLDecoder.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ final class HTMLDecoderViewController: ToolPageViewController {
2929
}
3030
}
3131

32-
final private class HTMLDecoderView: ToolPage {
32+
final private class HTMLDecoderView: Page {
3333

3434
let encodeTextSection = CodeViewSection(title: "Encoded", options: [.all], language: .xml)
3535
let decodeTextSection = TextViewSection(title: "Decoded", options: [.all])

‎DevToys/DevToys/Body/Coder/JWTDecoder.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ final class JWTDecoderViewController: ToolPageViewController {
5858
}
5959
}
6060

61-
final private class JWTDecoderView: ToolPage {
61+
final private class JWTDecoderView: Page {
6262

6363
let tokenTextSection = TextViewSection(title: "JWT Token", options: .defaultInput)
6464
let headerCodeSection = CodeViewSection(title: "Header", options: .defaultOutput, language: .javascript)

‎DevToys/DevToys/Body/Coder/URLDecoder.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ final class URLDecoderViewController: ToolPageViewController {
3333
}
3434
}
3535

36-
final private class URLDecoderView: ToolPage {
36+
final private class URLDecoderView: Page {
3737
let encodeTextSection = TextViewSection(title: "Encoded", options: [.all])
3838
let decodeTextSection = TextViewSection(title: "Decoded", options: [.all])
3939

‎DevToys/DevToys/Body/Convert/JSONYamlConverter+.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ private enum FormatStyle: String, TextItem {
100100
var title: String { rawValue }
101101
}
102102

103-
final private class JSONYamlConverterView: ToolPage {
103+
final private class JSONYamlConverterView: Page {
104104

105105
let formatStylePicker = EnumPopupButton<FormatStyle>()
106106

‎DevToys/DevToys/Body/Convert/NumberBaseConverter+.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ final class NumberBaseConverterViewController: ToolPageViewController {
9797
}
9898
}
9999

100-
final private class NumberBaseConverterView: ToolPage {
100+
final private class NumberBaseConverterView: Page {
101101
let formatSwitch = NSSwitch()
102102

103103
let decimalSection = TextFieldSection(title: "Decimal")

‎DevToys/DevToys/Body/Format/JSONFormatter+.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ enum JSONSpacingType: String, TextItem {
5656
var title: String { rawValue }
5757
}
5858

59-
final private class JSONFormatterView: ToolPage {
59+
final private class JSONFormatterView: Page {
6060

6161
let indentControl = EnumPopupButton<JSONSpacingType>()
6262
let inputSection = CodeViewSection(title: "Input", options: .defaultInput, language: .javascript)

‎DevToys/DevToys/Body/Generator/HashGenerator.swift

+12-7
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ final class HashGeneratorViewController: ToolPageViewController {
2222

2323
override func viewDidLoad() {
2424
self.$isUppercase.sink{[unowned self] in self.cell.formatSwitch.isOn = $0 }.store(in: &objectBag)
25-
self.$input.sink{[unowned self] in self.cell.inputSection.string = $0 }.store(in: &objectBag)
25+
self.$input.sink{[unowned self] in self.cell.textInputSection.string = $0 }.store(in: &objectBag)
2626
self.$md5.sink{[unowned self] in self.cell.md5Section.string = $0 }.store(in: &objectBag)
2727
self.$sha1.sink{[unowned self] in self.cell.sha1Section.string = $0 }.store(in: &objectBag)
2828
self.$sha256.sink{[unowned self] in self.cell.sha256Section.string = $0 }.store(in: &objectBag)
2929
self.$sha512.sink{[unowned self] in self.cell.sha512Section.string = $0 }.store(in: &objectBag)
3030

31-
self.cell.inputSection.stringPublisher
32-
.sink{[unowned self] in self.input = $0; updateHash() }.store(in: &objectBag)
31+
// self.cell.inputSection.stringPublisher
32+
// .sink{[unowned self] in self.input = $0; updateHash() }.store(in: &objectBag)
3333
self.cell.formatSwitch.isOnPublisher
3434
.sink{[unowned self] in self.isUppercase = $0; updateHash() }.store(in: &objectBag)
3535
}
@@ -42,10 +42,14 @@ final class HashGeneratorViewController: ToolPageViewController {
4242
}
4343
}
4444

45-
final class HashGeneratorView: ToolPage {
45+
final class HashGeneratorView: Page {
4646
let formatSwitch = NSSwitch()
4747

48-
let inputSection = TextViewSection(title: "Input", options: .all)
48+
let fileDrop = FileDrop()
49+
lazy var fileInputSection = Section(title: "Input", items: [fileDrop])
50+
let textInputSection = TextViewSection(title: "Input", options: .all)
51+
let inputSection = NSPlaceholderView()
52+
4953
let md5Section = TextFieldSection(title: "MD5", isEditable: false)
5054
let sha1Section = TextFieldSection(title: "SHA1", isEditable: false)
5155
let sha256Section = TextFieldSection(title: "SHA256", isEditable: false)
@@ -60,8 +64,9 @@ final class HashGeneratorView: ToolPage {
6064
self.addSection(configurationSection)
6165

6266
self.addSection(inputSection)
63-
self.inputSection.textView.snp.remakeConstraints{ make in
64-
make.height.equalTo(100)
67+
self.inputSection.contentView = textInputSection
68+
self.inputSection.snp.remakeConstraints{ make in
69+
make.height.equalTo(180)
6570
}
6671

6772
self.addSection(md5Section)

‎DevToys/DevToys/Body/Generator/LoremIpsumGenerator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ enum LoremIpsumGenerateType: String, TextItem {
6262
var title: String { rawValue }
6363
}
6464

65-
final private class LoremIpsumGeneratorView: ToolPage {
65+
final private class LoremIpsumGeneratorView: Page {
6666
let typePicker = EnumPopupButton<LoremIpsumGenerateType>()
6767
let lengthField = NumberField()
6868

‎DevToys/DevToys/Body/Generator/UUIDGenerator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ final class UUIDGeneratorViewController: ToolPageViewController {
5454
}
5555
}
5656

57-
final private class UUIDGeneratorView: ToolPage {
57+
final private class UUIDGeneratorView: Page {
5858
let hyphensSwitch = NSSwitch()
5959
let uppercaseSwitch = NSSwitch()
6060
let generateCount = NumberField()

‎DevToys/DevToys/Body/Media/Image Optimizer/ImageOptimaizerView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ final class ImageOptimaizerViewController: ToolPageViewController {
4343
}
4444

4545

46-
final private class ImageOptimaizerView: ToolPage {
46+
final private class ImageOptimaizerView: Page {
4747
private let listView = NSTableView.list()
4848

4949
let urlPublisher = PassthroughSubject<[URL], Never>()

‎DevToys/DevToys/Body/Media/Image Optimizer/ImageOptimizer.swift

-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ enum ImageOptimizer {
2727
if ext == "png" { return PngOptimizer.optimize(url, optimizeLevel: optimizeLevel) }
2828
if ext == "jpg" || ext == "jpeg" { return JpegOptimizer.optimize(url, optimizeLevel: optimizeLevel) }
2929

30-
assertionFailure("\(ext) is not supported.")
3130
return nil
3231
}
3332
}

‎DevToys/DevToys/Body/Media/PDFGenerator.swift

+38-7
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ import CoreUtil
1010
final class PDFGeneratorViewController: ToolPageViewController {
1111
private let cell = PDFGeneratorView()
1212

13-
@Observable var images = [ImageItem]()
13+
@RestorableData("pdf.input") var images = [ImageItem]()
1414

1515
override func loadView() { self.view = cell }
1616

1717
override func viewDidLoad() {
18+
print(_images.fileURL)
1819
self.$images
1920
.sink{[unowned self] in cell.imageListView.imageItems = $0 }.store(in: &objectBag)
2021

@@ -25,11 +26,35 @@ final class PDFGeneratorViewController: ToolPageViewController {
2526
self.cell.imageListView.removePublisher
2627
.sink{[unowned self] in $0.reversed().forEach{ self.images.remove(at: $0) } }.store(in: &objectBag)
2728
self.cell.imageListView.movePublisher
28-
.sink{[unowned self] in self.images.move(fromIndex: $0.from, toIndex: $0.to) }.store(in: &objectBag)
29+
.sink{[unowned self] in self.moveItems($0, to: $1) }.store(in: &objectBag)
2930
self.cell.clearButton.actionPublisher
3031
.sink{[unowned self] in self.images = [] }.store(in: &objectBag)
3132
}
3233

34+
private func moveItems(_ fromRows: [Int], to row: Int) {
35+
guard !fromRows.isEmpty else { return }
36+
37+
let fromMin = fromRows.min()!
38+
39+
let fromRows = fromRows.sorted().reversed()
40+
var nextImages = self.images
41+
var removed = [ImageItem]()
42+
43+
for fromRow in fromRows {
44+
let item = nextImages.remove(at: fromRow)
45+
removed.append(item)
46+
}
47+
48+
removed.reverse()
49+
50+
if row < fromMin {
51+
nextImages.insert(contentsOf: removed, at: row)
52+
} else {
53+
nextImages.insert(contentsOf: removed, at: row - fromRows.count)
54+
}
55+
self.images = nextImages
56+
}
57+
3358
private func readURLs(_ pasteboard: NSPasteboard) {
3459
var newImageItems = [ImageItem]()
3560

@@ -103,7 +128,7 @@ private enum ScaleMode: String, TextItem {
103128
var title: String { rawValue }
104129
}
105130

106-
final private class PDFGeneratorView: ToolPage {
131+
final private class PDFGeneratorView: Page {
107132
let imageListView = ImageListView()
108133
let clearButton = SectionButton(title: "Clear", image: R.Image.clear)
109134
let generateButton = Button(title: "Generate PDF")
@@ -142,16 +167,22 @@ final private class PDFGeneratorView: ToolPage {
142167
}
143168
}
144169

145-
struct ImageItem {
170+
struct ImageItem: Codable {
146171
let title: String
147-
let image: NSImage
172+
var image: NSImage { imageContainer.image }
173+
private let imageContainer: NSImageContainer
174+
175+
init(title: String, image: NSImage) {
176+
self.title = title
177+
self.imageContainer = .wrap(image)
178+
}
148179
}
149180

150181
final private class ImageListView: NSLoadScrollView {
151182
let listView = NSTableView.list()
152183
var imageItems = [ImageItem]() { didSet { listView.reloadData() } }
153184
var removePublisher = PassthroughSubject<[Int], Never>()
154-
var movePublisher = PassthroughSubject<(from: Int, to: Int), Never>()
185+
var movePublisher = PassthroughSubject<(from: [Int], to: Int), Never>()
155186
private let backgroundLayer = ControlBackgroundLayer.animationDisabled()
156187

157188
override func keyDown(with event: NSEvent) {
@@ -254,7 +285,7 @@ extension ImageListView: NSTableViewDelegate, NSTableViewDataSource {
254285

255286
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
256287
guard dropOperation == .above else { return false }
257-
guard let fromRow = info.draggingPasteboard.propertyList(forType: .imageItem) as? Int else { return false }
288+
guard let fromRow = info.draggingPasteboard.pasteboardItems?.map{ $0.propertyList(forType: .imageItem) } as? [Int] else { return false }
258289

259290
movePublisher.send((fromRow, row))
260291

‎DevToys/DevToys/Body/Network/NetworkInfomationView+.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ extension NetworkInfomationViewController: NetworkSpeedProviderDelegate {
6161
}
6262
}
6363

64-
final private class NetworkInfomationView: ToolPage {
64+
final private class NetworkInfomationView: Page {
6565
let ipaddressLabel = NSTextField(labelWithString: "0.0.0.0")
6666
let statusLabel = NSTextField(labelWithString: "fetching...")
6767
let speedLabel = NSTextField(labelWithString: "0Mbps")

‎DevToys/DevToys/Body/Text/RegexTesterView+.swift

+15-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import CoreUtil
1010
final class RegexTesterViewController: ToolPageViewController {
1111
private let cell = RegexTesterView()
1212

13-
@RestorableState("regex.pattern") var pattern = #"\d+.\d"#
14-
@RestorableState("regex.sample") var text = #"100, 3.141, "Hello World""#
13+
@RestorableState("rx.pattern") var pattern = #"(macOS|OS X) \d+\.\d+"#
14+
@RestorableState("rx.sample") var text = defaultText
1515

1616
@Observable var regex: NSRegularExpression? = nil
1717
@Observable var isError = false
@@ -59,7 +59,7 @@ final class RegexTesterViewController: ToolPageViewController {
5959
}
6060
}
6161

62-
final private class RegexTesterView: ToolPage {
62+
final private class RegexTesterView: Page {
6363
let regexField = TextField(showCopyButton: false)
6464
let textView = RegexTextView()
6565

@@ -79,3 +79,15 @@ final private class RegexTesterView: ToolPage {
7979
self.addSection(Section(title: "Text", items: [textView]))
8080
}
8181
}
82+
83+
private let defaultText = """
84+
OS X 10.9 Mavericks
85+
OS X 10.10 Yosemite
86+
OS X 10.11 El Capitan
87+
macOS 10.12 Sierra
88+
macOS 10.13 High Sierra
89+
macOS 10.14 Mojave
90+
macOS 10.15 Catalina
91+
macOS 11.0 Big Sur
92+
macOS 12.0 Monterey
93+
"""

‎DevToys/DevToys/Body/Text/TextInspector.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ private enum ConvertType: String, CaseIterable {
9090
case trainCase = "Traint-Case"
9191
}
9292

93-
final class TextInspectorView: ToolPage {
93+
final class TextInspectorView: Page {
9494
let inputSection = TextViewSection(title: "Input", options: .defaultInput)
9595
let tagCloudView = TagCloudView()
9696
let outputSection = TextViewSection(title: "Output", options: .defaultOutput)

‎DevToys/DevToys/Component/Area.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ final class Area: NSLoadView {
8282
self.stackView.addArrangedSubview(titleStack)
8383
self.titleStack.orientation = .vertical
8484
self.titleStack.spacing = 4
85-
self.titleStack.distribution = .equalSpacing
85+
self.titleStack.distribution = .fillProportionally
8686
self.titleStack.alignment = .left
8787

8888
self.titleStack.addArrangedSubview(titleLabel)

‎DevToys/DevToys/Component/ToolPage.swift ‎DevToys/DevToys/Component/Page.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import CoreUtil
99

10-
class ToolPage: NSLoadView {
10+
class Page: NSLoadView {
1111

1212
var title: String = "Untitled Tool"
1313

@@ -45,7 +45,8 @@ class ToolPage: NSLoadView {
4545
self.scrollView.documentView = stackView
4646

4747
self.stackView.snp.makeConstraints{ make in
48-
make.width.equalToSuperview()
48+
make.top.equalToSuperview()
49+
make.right.left.equalToSuperview()
4950
}
5051
}
5152

@@ -65,6 +66,6 @@ private class FlipClipView: NSClipView {
6566

6667
class ToolPageViewController: NSViewController {
6768
override func viewDidAppear() {
68-
view.window?.title = (view as? ToolPage)?.title ?? "DevToys"
69+
view.window?.title = (view as? Page)?.title ?? "DevToys"
6970
}
7071
}

‎DevToys/DevToys/Component/RegexTextView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ final class RegexTextView: NSLoadView {
2525
textStorage.removeAttribute(NSAttributedString.Key.backgroundColor, range: NSRange(location: 0, length: textStorage.length))
2626

2727
for range in highlightRanges {
28-
textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: NSColor.systemOrange, range: range)
28+
textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: NSColor.systemOrange.withAlphaComponent(0.7), range: range)
2929
}
3030
} catch: { error in
3131
print(error)

‎DevToys/DevToys/Component/Section.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ class Section: NSLoadView {
1313
}
1414
var minTitle: Bool = false {
1515
didSet {
16-
self.titleStackView.snp.remakeConstraints{ make in
17-
make.height.equalTo(minTitle ? 16 : 36)
16+
self.titleStackView.snp.updateConstraints{ make in
17+
make.height.equalTo(minTitle ? 16 : R.Size.controlHeight)
1818
}
1919
}
2020
}

‎DevToys/DevToys/Component/TextFieldSection.swift

+8-12
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,29 @@ struct TextFieldSectionOptions: OptionSet {
1313

1414
final class TextFieldSection: Section {
1515
var string: String {
16-
get { textView.string } set { textView.string = newValue; copyButton.stringContent = newValue }
16+
get { textField.string } set { textField.string = newValue; copyButton.stringContent = newValue }
1717
}
1818
var stringPublisher: AnyPublisher<String, Never> {
19-
textView.changeStringPublisher.merge(with: self.pasteButton.stringPublisher.map{ $0 ?? "" }).eraseToAnyPublisher()
20-
}
21-
22-
func setMinified() {
23-
self.textView.showCopyButton = true
24-
self.minTitle = true
25-
self.removeAllToolbarItem()
19+
textField.changeStringPublisher.merge(with: self.pasteButton.stringPublisher.map{ $0 ?? "" }).eraseToAnyPublisher()
2620
}
2721

2822
convenience init(title: String, isEditable: Bool) {
2923
self.init(title: title)
30-
self.textView.isEditable = isEditable
24+
self.textField.isEditable = isEditable
3125
if !isEditable {
32-
self.setMinified()
26+
self.textField.showCopyButton = true
27+
self.minTitle = true
28+
self.removeAllToolbarItem()
3329
}
3430
}
3531

36-
let textView = TextField(showCopyButton: false)
32+
let textField = TextField(showCopyButton: false)
3733
private let pasteButton = PasteSectionButton()
3834
private let copyButton = CopySectionButton(hasTitle: false)
3935

4036
override func onAwake() {
4137
super.onAwake()
42-
self.addStackItem(textView)
38+
self.addStackItem(textField)
4339
self.addToolbarItem(pasteButton)
4440
self.addToolbarItem(copyButton)
4541
}

‎DevToys/DevToys/Sidebar/ToolMenuView+.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ final class ToolmenuViewController: NSViewController {
3232
.imageCompressor, .pdfGenerator
3333
])
3434
private let networkMenu = ToolMenu(R.Image.Sidebar.network, "Network", "network", [
35-
.networkInfomation, .apiTest
35+
.networkInfomation,
36+
// .apiTest
3637
])
3738

3839
private lazy var toolMenus = [

0 commit comments

Comments
 (0)
This repository has been archived.