Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Images/case1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/case2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/case3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/case4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 13 additions & 5 deletions Pretty.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@
C2756DF120765C8100FF160B /* Pretty */ = {
isa = PBXGroup;
children = (
C2756DF220765C8100FF160B /* AppDelegate.swift */,
C2756DFB20765C8200FF160B /* Info.plist */,
C2756DFC20765C8200FF160B /* Pretty.entitlements */,
C2D8BC08207722160093FC8E /* Resource */,
C2D8BBFE20771FF80093FC8E /* Extension */,
C2756E0220765CBA00FF160B /* RandomColors */,
Expand All @@ -130,9 +133,6 @@
C253827B207681CA008BDB09 /* Model */,
C2D8BC07207721D90093FC8E /* View */,
C2D8BC09207722260093FC8E /* Controller */,
C2756DF220765C8100FF160B /* AppDelegate.swift */,
C2756DFB20765C8200FF160B /* Info.plist */,
C2756DFC20765C8200FF160B /* Pretty.entitlements */,
);
path = Pretty;
sourceTree = "<group>";
Expand Down Expand Up @@ -434,15 +434,19 @@
CODE_SIGN_ENTITLEMENTS = Pretty/Pretty.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 7A5GV3DH33;
CURRENT_PROJECT_VERSION = 1000;
DEVELOPMENT_TEAM = TKRM4J3XJV;
INFOPLIST_FILE = Pretty/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = me.octree.Pretty;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
SYMROOT = "$(PROJECT_DIR)/build";
};
name = Debug;
};
Expand All @@ -453,15 +457,19 @@
CODE_SIGN_ENTITLEMENTS = Pretty/Pretty.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 7A5GV3DH33;
CURRENT_PROJECT_VERSION = 1000;
DEVELOPMENT_TEAM = TKRM4J3XJV;
INFOPLIST_FILE = Pretty/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = me.octree.Pretty;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
SYMROOT = "$(PROJECT_DIR)/build";
};
name = Release;
};
Expand Down
21 changes: 19 additions & 2 deletions Pretty/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Cocoa

let OCTOpenFileNotification = "OCTOpenFileNotification"
let OCTSaveFileNotification = "OCTSaveFileNotification"
public var FileName = ""

@NSApplicationMain
Expand All @@ -23,9 +24,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

func application(_ sender: NSApplication, openFile filename: String) -> Bool {

NotificationCenter.default.post(name:NSNotification.Name(rawValue: OCTOpenFileNotification) , object: filename)
FileName = filename
NotificationCenter.default.post(name:NSNotification.Name(rawValue: OCTOpenFileNotification) , object: filename)
return true
}
//
Expand All @@ -34,6 +34,23 @@ class AppDelegate: NSObject, NSApplicationDelegate {
//
// }

@IBAction func openDocument(_ sender: Any) {
let openPanel = NSOpenPanel()
openPanel.canChooseFiles = true
openPanel.canChooseDirectories = false
openPanel.allowsMultipleSelection = false
if openPanel.runModal() == .OK {
if let url = openPanel.url {
FileName = url.path
NotificationCenter.default.post(name:NSNotification.Name(rawValue: OCTOpenFileNotification) , object: url.path)
}
}
}


@IBAction func saveDepencyTreeFile(_ sender: Any) {
NotificationCenter.default.post(name:NSNotification.Name(rawValue: OCTSaveFileNotification) , object: nil)
}
}


173 changes: 134 additions & 39 deletions Pretty/Controller/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,36 @@ import Cocoa
class ViewController: NSViewController {

@IBOutlet weak var scrollView: NSScrollView!
@IBOutlet weak var maginficationLabel: NSTextField!
@IBOutlet weak var abilityView: NSView!

private let relationView = RelationView()
private var treeMode = 0
private var treeReversed = true
private var dependency: [String: [String]]?
private var searchModuleName = ""
private var magnification = 1.0

override func viewDidLoad() {
super.viewDidLoad()

scrollView.hasVerticalScroller = true
scrollView.hasHorizontalScroller = true
scrollView.documentView = relationView
scrollView.maxMagnification = 3
scrollView.minMagnification = 0.2
scrollView.magnification = magnification

abilityView.layer?.backgroundColor = NSColor.selectedContentBackgroundColor.cgColor
abilityView.layer?.cornerRadius = 10
abilityView.alphaValue = 0.6

NotificationCenter.default.addObserver(self, selector: #selector(handleOpenFile(notification:)), name: NSNotification.Name(rawValue: OCTOpenFileNotification), object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(handleSaveFile(notification:)), name: NSNotification.Name(rawValue: OCTSaveFileNotification), object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(self.handleOpenFile(notification:)), name: NSNotification.Name(rawValue: OCTOpenFileNotification), object: nil)

if FileName.count > 0 {

updateRelationView(filename: FileName)
}
}
Expand All @@ -43,73 +59,152 @@ class ViewController: NSViewController {
width: max(size.width, parentSize.width),
height: max(size.height, parentSize.height))
}


@objc func handleOpenFile(notification: Notification) {

guard let filename = notification.object as? String else {
return
}

updateRelationView(filename: filename)
}

func updateRelationView(filename: String) {

}

// MARK: 文件处理相关代码,私有
extension ViewController {
private func updateRelationView(filename: String) {
view.window?.title = filename
if filename.hasSuffix(".lock") {
updateWithLockFile(filename: filename)
} else {
}
else if filename.hasSuffix(".json") {
updateWithPrettyFile(filename: filename)
}
}

func updateWithLockFile(filename: String) {

private func updateWithLockFile(filename: String) {
do {

let string = try String(contentsOfFile: filename, encoding: .utf8)

if let (dependency, _) = PodLockFileParser.parse(Substring(string)) {

relationView.prettyRelation = PrettyRelation(dependency: dependency)
alert(title: "Info", msg: "loaded \(filename)")
self.dependency = dependency
relationView.prettyRelation = PrettyRelation(dependency: dependency, treeMode: treeMode, treeReversed: treeReversed)
} else {

alert(title: "Error", msg: "Parse Error: Wrong Format")
}
} catch {

alert(title: "Error", msg: error.localizedDescription)
}
}


func updateWithPrettyFile(filename: String) {
private func updateWithPrettyFile(filename: String) {
let url = URL(fileURLWithPath: filename)
guard let data = try? Data(contentsOf: url), let dependency = try? JSONSerialization.jsonObject(with: data) as? [String: [String]] else {
alert(title: "Error", msg: "数据格式不对,请检查")
return
}

do {

let url = URL(fileURLWithPath: filename)
let data = try Data(contentsOf: url)
let relation = try JSONDecoder().decode(PrettyRelation.self, from: data)
relationView.prettyRelation = relation
alert(title: "Info", msg: "loaded \(filename)")
} catch {

alert(title: "Error", msg: error.localizedDescription)
if let dependency = dependency {
self.dependency = dependency
relationView.prettyRelation = PrettyRelation(dependency: dependency, treeMode: treeMode, treeReversed: treeReversed)
}
else {
alert(title: "Error", msg: "数据格式不对,请检查")
}
}


func alert(title: String, msg: String) {

private func alert(title: String, msg: String) {
let alert = NSAlert()
alert.addButton(withTitle: "Ok")
alert.messageText = title
alert.informativeText = msg
alert.alertStyle = .warning
alert.beginSheetModal(for: self.view.window!, completionHandler: nil)
}
}

// MARK: Storyboard相关代码
extension ViewController {
@IBAction func treeMode(_ sender: NSComboBox) {
if sender.stringValue == "被依赖树图" {
treeMode = 0
}
else if sender.stringValue == "依赖树图" {
treeMode = 1
}

if let dependency = dependency {
relationView.prettyRelation = PrettyRelation(dependency: dependency, treeMode: treeMode, treeReversed: treeReversed)
}
}

@IBAction func treeReversed(_ sender: NSComboBox) {
if sender.stringValue == "正向树" {
treeReversed = true
}
else if sender.stringValue == "逆向树" {
treeReversed = false
}

if let dependency = dependency {
relationView.prettyRelation = PrettyRelation(dependency: dependency, treeMode: treeMode, treeReversed: treeReversed)
}
}


@IBAction func searchModule(_ sender: NSSearchField) {
debugPrint("\(sender.stringValue)")
}


@IBAction func viewScale(_ sender: NSStepper) {
maginficationLabel.stringValue = "缩放比例:\(sender.floatValue)"
scrollView.magnification = CGFloat(sender.floatValue)
}
}

// MARK: 通知相关代码
extension ViewController {
@objc func handleOpenFile(notification: Notification) {
guard let filename = notification.object as? String else {
return
}

updateRelationView(filename: filename)
}

@objc func handleSaveFile(notification: Notification) {
if FileName.hasSuffix(".lock") == false {
return
}

var filename = "dependency.json"
let temp1 = FileName.components(separatedBy: ".lock")
if temp1.count == 2 {
let temp2 = temp1.first
if let temp3 = temp2?.components(separatedBy: "/"), let temp4 = temp3.last {
filename = temp4
}
}

let panel = NSSavePanel()
panel.title = "保存文件"
panel.message = "请选择文件保存地址"
panel.directoryURL = URL(string: "\(NSHomeDirectory())/Downloads")
panel.nameFieldStringValue = "\(filename)_Dependency.json"
panel.allowsOtherFileTypes = true
panel.isExtensionHidden = false
panel.canCreateDirectories = true
panel.begin { (response) in
if response == .OK {
if let url = panel.url, let dependency = self.dependency {
let data = try? JSONSerialization.data(withJSONObject: dependency)
_ = try? data?.write(to: url)
}
}
}
}
}

// MARK: 代理相关代码
extension ViewController: NSTextFieldDelegate {
func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
debugPrint(control.stringValue)
if control.stringValue.count > 0 {
searchModuleName = control.stringValue
relationView.findModule(name: searchModuleName)
}
return true
}
}
17 changes: 11 additions & 6 deletions Pretty/Extension/PrettyRelation+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@

import Foundation

private let kRelationHorizentalSpacing = 30
private let kRelationHorizentalSpacing = 50
private let kRelationVerticalSpacing = 100
private let kRelationItemHeight = 28
private let kRelationItemPerRow = 6
private let kRelationViewPadding = 40

private func widthForItem(_ text: String) -> Int {

return 10 + 9 * text.count
return max(10 + 9 * text.count, 120)
}


Expand Down Expand Up @@ -59,9 +58,15 @@ private func nodesForGroups(_ groups: [[String: [String]]]) -> [DependencyNode]

extension PrettyRelation {

convenience init(dependency: [String: [String]]) {

let groups = Array(groupPodDependency(dependency).reversed())
convenience init(dependency: [String: [String]], treeMode: Int, treeReversed: Bool) {
var groups = [[String: [String]]]()
if treeMode == 0 {
groups = Array(treeReversed ? groupPodDependencyReversed(dependency).reversed() : groupPodDependencyReversed(dependency))
}
else if treeMode == 1 {
groups = Array(treeReversed ? groupPodDependency(dependency).reversed() : groupPodDependency(dependency))
}
// let groups = Array(groupPodDependency(dependency))
// let size = preferredSize(groups)
self.init(nodes: nodesForGroups(groups))
}
Expand Down
2 changes: 1 addition & 1 deletion Pretty/Model/DependencyNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct NodeFrame: Codable {
extension NodeFrame {

var rect: NSRect {
return NSRect(x: x, y: y, width: width, height: height)
return NSRect(x: x, y: y, width: width + 20, height: height + 20)
}
}

Expand Down
13 changes: 13 additions & 0 deletions Pretty/Parser/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,16 @@ struct Parser<Result> {
let parse: (Stream) -> (Result, Stream)?
}

// Parser作为解析结构体,接受<Int> 类型的输入,返回 (Int, Substring) 元组
func test() {
// 对parser的实现
let parser = Parser<Int> { input in
return (input.count, input)
}

// 调用parser进行解析
let input = "123abc"
if let (result1, result2) = parser.parse(input[...]) {
print("\(result1)、\(result2)")
}
}
Loading