Add info window to show time tracked on a document
This commit is contained in:
parent
21392bd1cf
commit
a57fbf2fab
6 changed files with 136 additions and 19 deletions
|
@ -23,6 +23,7 @@
|
|||
036AFC11241800350075400A /* ThumbnailProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036AFC10241800350075400A /* ThumbnailProvider.swift */; };
|
||||
036AFC16241800350075400A /* Thumbnail.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 036AFC0B241800350075400A /* Thumbnail.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
036AFC1B241800850075400A /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 036AFC1A241800850075400A /* ZIPFoundation */; };
|
||||
037B4042241821D200392452 /* InfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037B4041241821D200392452 /* InfoViewController.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -77,6 +78,7 @@
|
|||
036AFC10241800350075400A /* ThumbnailProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailProvider.swift; sourceTree = "<group>"; };
|
||||
036AFC12241800350075400A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
036AFC13241800350075400A /* Thumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Thumbnail.entitlements; sourceTree = "<group>"; };
|
||||
037B4041241821D200392452 /* InfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoViewController.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -141,6 +143,7 @@
|
|||
030F6FFD2415C5E400A43F01 /* Info.plist */,
|
||||
030F6FFE2415C5E400A43F01 /* ProcreateViewer.entitlements */,
|
||||
036AFBB924168C030075400A /* ViewController.swift */,
|
||||
037B4041241821D200392452 /* InfoViewController.swift */,
|
||||
);
|
||||
path = ProcreateViewer;
|
||||
sourceTree = "<group>";
|
||||
|
@ -319,6 +322,7 @@
|
|||
files = (
|
||||
036AFBBA24168C030075400A /* ViewController.swift in Sources */,
|
||||
030F6FF42415C5E300A43F01 /* Document.swift in Sources */,
|
||||
037B4042241821D200392452 /* InfoViewController.swift in Sources */,
|
||||
030F6FF22415C5E300A43F01 /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
import Cocoa
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations {
|
||||
@IBAction func showInfoAction(_ sender: Any) {
|
||||
NSApplication.shared.keyWindow?.contentViewController?.performSegue(withIdentifier: "showInfo", sender: self)
|
||||
}
|
||||
|
||||
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
if(item.tag == 67) {
|
||||
return NSApplication.shared.keyWindow != nil
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,25 +90,13 @@
|
|||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
|
||||
<menuItem title="Show Info" tag="67" id="Fkh-F1-E0e">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
|
||||
<action selector="showInfoAction:" target="Voe-Tx-rLC" id="3A0-GP-3ZY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
|
||||
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
|
@ -164,9 +152,56 @@
|
|||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="ProcreateViewer" customModuleProvider="target"/>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
<userDefaultsController representsSharedInstance="YES" id="JdS-Pg-8N9"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="0.0"/>
|
||||
</scene>
|
||||
<!--Info View Controller-->
|
||||
<scene sceneID="nJy-a4-E0d">
|
||||
<objects>
|
||||
<viewController showSeguePresentationStyle="single" id="wda-Mt-beD" customClass="InfoViewController" customModule="ProcreateViewer" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="3vu-Kd-l73">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="85"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="u9u-el-oA4">
|
||||
<rect key="frame" x="18" y="49" width="444" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" placeholderString="Time Spent" id="UBd-vS-gNL">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2oe-to-Rcz">
|
||||
<rect key="frame" x="407" y="13" width="59" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="BnS-yc-3ej">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
DQ
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="dismissController:" target="wda-Mt-beD" id="LJb-SK-4qT"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="2oe-to-Rcz" firstAttribute="top" secondItem="u9u-el-oA4" secondAttribute="bottom" constant="8" symbolic="YES" id="BYm-qe-WxD"/>
|
||||
<constraint firstItem="u9u-el-oA4" firstAttribute="top" secondItem="3vu-Kd-l73" secondAttribute="top" constant="20" symbolic="YES" id="HaC-Tk-Y3y"/>
|
||||
<constraint firstAttribute="trailing" secondItem="u9u-el-oA4" secondAttribute="trailing" constant="20" symbolic="YES" id="Qfn-KK-5Ya"/>
|
||||
<constraint firstItem="u9u-el-oA4" firstAttribute="leading" secondItem="3vu-Kd-l73" secondAttribute="leading" constant="20" symbolic="YES" id="TpT-D9-UgA"/>
|
||||
<constraint firstItem="2oe-to-Rcz" firstAttribute="trailing" secondItem="u9u-el-oA4" secondAttribute="trailing" id="ZND-Qt-9wv"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="timeSpentLabel" destination="u9u-el-oA4" id="LeI-31-Z8r"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="Tam-yi-Bux" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="786" y="286.5"/>
|
||||
</scene>
|
||||
<!--Window Controller-->
|
||||
<scene sceneID="R2V-B0-nI4">
|
||||
<objects>
|
||||
|
@ -210,11 +245,12 @@
|
|||
</view>
|
||||
<connections>
|
||||
<outlet property="imageView" destination="ipM-NH-wUM" id="RT5-09-Hwj"/>
|
||||
<segue destination="wda-Mt-beD" kind="sheet" identifier="showInfo" id="obo-Yt-yny"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="2Tp-Fl-jBw" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="655"/>
|
||||
<point key="canvasLocation" x="59" y="761"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
import Cocoa
|
||||
import ZIPFoundation
|
||||
|
||||
struct DocumentInfo {
|
||||
var tracked_time: Int = 0
|
||||
}
|
||||
|
||||
class Document: NSDocument {
|
||||
var image: NSImage?
|
||||
var info = DocumentInfo()
|
||||
|
||||
var thumbnail: NSImage? = nil
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
@ -19,6 +25,7 @@ class Document: NSDocument {
|
|||
return
|
||||
}
|
||||
|
||||
// load thumbnail
|
||||
guard let entry = archive["QuickLook/Thumbnail.png"] else {
|
||||
return
|
||||
}
|
||||
|
@ -33,7 +40,39 @@ class Document: NSDocument {
|
|||
Swift.print("Extracting entry from archive failed with error:\(error)")
|
||||
}
|
||||
|
||||
image = NSImage(data: top_data)
|
||||
thumbnail = NSImage(data: top_data)
|
||||
|
||||
// load doc info
|
||||
guard let document_entry = archive["Document.archive"] else {
|
||||
return
|
||||
}
|
||||
|
||||
var doc_data = Data()
|
||||
|
||||
do {
|
||||
try archive.extract(document_entry, consumer: { (d) in
|
||||
doc_data.append(d)
|
||||
})
|
||||
} catch {
|
||||
Swift.print("Extracting entry from archive failed with error:\(error)")
|
||||
}
|
||||
|
||||
// Document.archive is a binary plist (specifically a NSKeyedArchive), luckily swift has a built-in solution to decode it
|
||||
var plistFormat = PropertyListSerialization.PropertyListFormat.binary
|
||||
let plistBinary = doc_data
|
||||
|
||||
guard let propertyList = try? PropertyListSerialization.propertyList(from: plistBinary, options: [], format: &plistFormat) else {
|
||||
fatalError("failed to deserialize")
|
||||
}
|
||||
|
||||
// this is temporary, as we're just hoping that the keyed archive fits our requirements...
|
||||
let dict = (propertyList as! NSDictionary);
|
||||
|
||||
let objects = dict["$objects"] as! NSArray
|
||||
|
||||
let tracked_time = (objects[1] as! NSDictionary)["SilicaDocumentTrackedTimeKey"]
|
||||
|
||||
info.tracked_time = (tracked_time as! NSNumber).intValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
20
ProcreateViewer/InfoViewController.swift
Normal file
20
ProcreateViewer/InfoViewController.swift
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class InfoViewController: NSViewController {
|
||||
var document: Document?
|
||||
|
||||
@IBOutlet weak var timeSpentLabel: NSTextField!
|
||||
|
||||
override func viewDidAppear() {
|
||||
super.viewDidAppear()
|
||||
|
||||
let formatter = DateComponentsFormatter()
|
||||
formatter.allowedUnits = [.hour, .minute, .second]
|
||||
formatter.unitsStyle = .full
|
||||
|
||||
let formattedString = formatter.string(from: TimeInterval(document!.info.tracked_time))!
|
||||
|
||||
timeSpentLabel.stringValue = "Time Spent: " + formattedString
|
||||
}
|
||||
}
|
|
@ -7,6 +7,13 @@ class ViewController: NSViewController {
|
|||
override func viewWillAppear() {
|
||||
let document = self.view.window?.windowController?.document as? Document
|
||||
|
||||
imageView.image = document?.image
|
||||
imageView.image = document?.thumbnail
|
||||
}
|
||||
|
||||
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
|
||||
if(segue.identifier == "showInfo") {
|
||||
// TODO: there HAS to be a better way to pass the Document class along...
|
||||
(segue.destinationController as! InfoViewController).document = self.view.window?.windowController?.document as? Document
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue