ObservedObjext
This commit is contained in:
parent
226dd0593b
commit
bead05837b
@ -15,6 +15,8 @@
|
||||
EC13306F24DD687F008063CF /* YapsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC13306E24DD687F008063CF /* YapsFile.swift */; };
|
||||
EC13307124DDB3F4008063CF /* FinderHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC13307024DDB3F4008063CF /* FinderHelper.swift */; };
|
||||
EC13307424DF2B2D008063CF /* YapsFileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC13307324DF2B2D008063CF /* YapsFileCell.swift */; };
|
||||
EC62160025160F7100F285FC /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6215FF25160F7100F285FC /* Toolbar.swift */; };
|
||||
EC6216062517CB8000F285FC /* ObservableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC6216052517CB8000F285FC /* ObservableArray.swift */; };
|
||||
ECF5A75824E070710010A11D /* RawFileExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF5A75724E070710010A11D /* RawFileExtensions.swift */; };
|
||||
ECF5A76024E41AAC0010A11D /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF5A75F24E41AAC0010A11D /* Shared.swift */; };
|
||||
ECF5A76224E93C080010A11D /* MiscHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF5A76124E93C080010A11D /* MiscHelper.swift */; };
|
||||
@ -32,6 +34,8 @@
|
||||
EC13306E24DD687F008063CF /* YapsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YapsFile.swift; sourceTree = "<group>"; };
|
||||
EC13307024DDB3F4008063CF /* FinderHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinderHelper.swift; sourceTree = "<group>"; };
|
||||
EC13307324DF2B2D008063CF /* YapsFileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YapsFileCell.swift; sourceTree = "<group>"; };
|
||||
EC6215FF25160F7100F285FC /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = "<group>"; };
|
||||
EC6216052517CB8000F285FC /* ObservableArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableArray.swift; sourceTree = "<group>"; };
|
||||
ECF5A75724E070710010A11D /* RawFileExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawFileExtensions.swift; sourceTree = "<group>"; };
|
||||
ECF5A75F24E41AAC0010A11D /* Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = "<group>"; };
|
||||
ECF5A76124E93C080010A11D /* MiscHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiscHelper.swift; sourceTree = "<group>"; };
|
||||
@ -79,6 +83,7 @@
|
||||
EC13307024DDB3F4008063CF /* FinderHelper.swift */,
|
||||
ECF5A75F24E41AAC0010A11D /* Shared.swift */,
|
||||
ECF5A76124E93C080010A11D /* MiscHelper.swift */,
|
||||
EC6215FF25160F7100F285FC /* Toolbar.swift */,
|
||||
);
|
||||
path = YAPS;
|
||||
sourceTree = "<group>";
|
||||
@ -95,6 +100,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EC13306E24DD687F008063CF /* YapsFile.swift */,
|
||||
EC6216052517CB8000F285FC /* ObservableArray.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@ -179,6 +185,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
EC62160025160F7100F285FC /* Toolbar.swift in Sources */,
|
||||
ECF5A76024E41AAC0010A11D /* Shared.swift in Sources */,
|
||||
EC13305D24DAE92D008063CF /* ContentView.swift in Sources */,
|
||||
EC13307124DDB3F4008063CF /* FinderHelper.swift in Sources */,
|
||||
@ -186,6 +193,7 @@
|
||||
EC13306F24DD687F008063CF /* YapsFile.swift in Sources */,
|
||||
EC13307424DF2B2D008063CF /* YapsFileCell.swift in Sources */,
|
||||
EC13305B24DAE92D008063CF /* AppDelegate.swift in Sources */,
|
||||
EC6216062517CB8000F285FC /* ObservableArray.swift in Sources */,
|
||||
ECF5A76224E93C080010A11D /* MiscHelper.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@ -12,6 +12,7 @@ import SwiftUI
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@ObservedObject var observedFileList: ObservableArray<YapsFile> = Shared.shared.fileList
|
||||
var window: NSWindow!
|
||||
|
||||
|
||||
@ -19,11 +20,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
// Create the SwiftUI view that provides the window contents.
|
||||
let contentView = ContentView()
|
||||
|
||||
// Toolbar **needs** a delegate
|
||||
NSToolbar.yapsToolbar.delegate = self
|
||||
|
||||
// Create the window and set the content view.
|
||||
window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
|
||||
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
|
||||
backing: .buffered, defer: false)
|
||||
window.toolbar = .yapsToolbar
|
||||
window.center()
|
||||
window.setFrameAutosaveName("YAPS Main Window")
|
||||
window.contentView = NSHostingView(rootView: contentView)
|
||||
@ -49,11 +54,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
*/
|
||||
|
||||
@IBAction func openFolder(_ sender: Any) {
|
||||
if !Shared.shared.destinationDefined {
|
||||
Shared.shared.destination = FinderHelper.shared.selectFolder(modalTitle: "Choose destination folder.")
|
||||
Shared.shared.destinationDefined = true
|
||||
}
|
||||
FinderHelper.shared.iLikeThisImage(yapsFile: Shared.shared.currentFile, destination: Shared.shared.destination)
|
||||
self.observedFileList.array.removeAll()
|
||||
self.observedFileList.array.append(contentsOf: FinderHelper.shared.askForFolderAndGetFiles())
|
||||
}
|
||||
|
||||
@IBAction func getIt(_ sender: Any) {
|
||||
@ -76,3 +78,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
//scrollView.magnify(toFit: imageView.frame)
|
||||
}
|
||||
}
|
||||
|
||||
struct AppDelegate_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
/*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/
|
||||
}
|
||||
}
|
||||
|
||||
21
YAPS/YAPS/Assets.xcassets/toolbar_folder.imageset/Contents.json
vendored
Normal file
21
YAPS/YAPS/Assets.xcassets/toolbar_folder.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Image.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
YAPS/YAPS/Assets.xcassets/toolbar_folder.imageset/Image.png
vendored
Normal file
BIN
YAPS/YAPS/Assets.xcassets/toolbar_folder.imageset/Image.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@ -9,12 +9,13 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@State var fileList = [YapsFile]()
|
||||
@ObservedObject var observedFileList: ObservableArray<YapsFile> = Shared.shared.fileList
|
||||
@State var previewImg = Image("placeholder-image")
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
/*
|
||||
HStack {
|
||||
Image.init("folder")
|
||||
|
||||
@ -31,9 +32,10 @@ struct ContentView: View {
|
||||
Text(" Select Folder")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
List {
|
||||
ForEach(self.fileList, id: \.self) { yapsFile in
|
||||
List() {
|
||||
ForEach(self.observedFileList.array, id: \.self) { yapsFile in
|
||||
YapsFileCell(focused: self.checkCurrentFile(yapsFile: yapsFile), yapsFile: yapsFile)
|
||||
.onTapGesture(perform: {
|
||||
print("pressed \(yapsFile.name)")
|
||||
@ -46,10 +48,12 @@ struct ContentView: View {
|
||||
.focusable()
|
||||
.onMoveCommand { (direction) in
|
||||
var newIndex = Shared.shared.currentFile.index
|
||||
|
||||
print("\(direction)")
|
||||
|
||||
switch direction {
|
||||
case MoveCommandDirection.down:
|
||||
let max = self.fileList.count
|
||||
let max = self.observedFileList.array.count
|
||||
if (newIndex + 1) < max {
|
||||
newIndex += 1
|
||||
}
|
||||
@ -61,7 +65,7 @@ struct ContentView: View {
|
||||
newIndex += 0
|
||||
}
|
||||
|
||||
self.setCurrentFile(newCurrent: self.fileList[newIndex], at: newIndex)
|
||||
self.setCurrentFile(newCurrent: self.observedFileList.array[newIndex], at: newIndex)
|
||||
}
|
||||
}
|
||||
.frame(width: 260.0)
|
||||
@ -85,14 +89,14 @@ struct ContentView: View {
|
||||
|
||||
|
||||
func setCurrentFile(newCurrent: YapsFile, at: Int) {
|
||||
let currentFile = self.fileList[at]
|
||||
let currentFile = self.observedFileList.array[at]
|
||||
|
||||
Shared.shared.currentFile = currentFile
|
||||
|
||||
self.resetCurrentFile()
|
||||
self.fileList[at].current = true
|
||||
self.observedFileList.array[at].isCurrent = true
|
||||
|
||||
self.previewImg = FinderHelper.shared.getImageByURL(source: self.fileList[at].file)
|
||||
self.previewImg = FinderHelper.shared.getImageByURL(source: self.observedFileList.array[at].file)
|
||||
}
|
||||
|
||||
func checkCurrentFile(yapsFile: YapsFile) -> Bool {
|
||||
@ -104,8 +108,8 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
func resetCurrentFile() {
|
||||
for yapsFile in self.fileList {
|
||||
self.fileList[yapsFile.index].current = false
|
||||
for yapsFile in self.observedFileList.array {
|
||||
self.observedFileList.array[yapsFile.index].isCurrent = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ class FinderHelper {
|
||||
|
||||
var index = 0
|
||||
for item in items {
|
||||
let yapsFile: YapsFile = YapsFile(index: -1, name: item.lastPathComponent, file: item, current: false)
|
||||
let yapsFile: YapsFile = YapsFile(index: -1, name: item.lastPathComponent, file: item, isCurrent: false)
|
||||
fileList.append(yapsFile)
|
||||
index += 1
|
||||
}
|
||||
@ -61,6 +61,8 @@ class FinderHelper {
|
||||
// failed to read directory – bad permissions, perhaps?
|
||||
}
|
||||
|
||||
print("Found \(fileList.count) file\(fileList.count != 1 ? "s" : "") to show")
|
||||
|
||||
return indexify(list: fileList.sorted {
|
||||
let f01: YapsFile = $0
|
||||
let f02: YapsFile = $1
|
||||
@ -77,7 +79,7 @@ class FinderHelper {
|
||||
var index = 0
|
||||
|
||||
for item in list {
|
||||
let yapsFile: YapsFile = YapsFile(index: index, name: item.file.lastPathComponent, file: item.file, current: false)
|
||||
let yapsFile: YapsFile = YapsFile(index: index, name: item.file.lastPathComponent, file: item.file, isCurrent: false)
|
||||
indexifiedFileList.append(yapsFile)
|
||||
index += 1
|
||||
}
|
||||
|
||||
33
YAPS/YAPS/Model/ObservableArray.swift
Normal file
33
YAPS/YAPS/Model/ObservableArray.swift
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// ObservableArray.swift
|
||||
// YAPS
|
||||
//
|
||||
// Created by Gerrit Linnemann on 20.09.20.
|
||||
// Copyright © 2020 Adawim UG (haftungsbeschränkt). All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
class ObservableArray<T>: ObservableObject {
|
||||
|
||||
@Published var array:[T] = []
|
||||
var cancellables = [AnyCancellable]()
|
||||
|
||||
init(array: [T]) {
|
||||
self.array = array
|
||||
}
|
||||
|
||||
func observeChildrenChanges<T: ObservableObject>() -> ObservableArray<T> {
|
||||
let array2 = array as! [T]
|
||||
array2.forEach({
|
||||
let c = $0.objectWillChange.sink(receiveValue: { _ in self.objectWillChange.send() })
|
||||
|
||||
// Important: You have to keep the returned value allocated,
|
||||
// otherwise the sink subscription gets cancelled
|
||||
self.cancellables.append(c)
|
||||
})
|
||||
return self as! ObservableArray<T>
|
||||
}
|
||||
}
|
||||
@ -9,10 +9,18 @@
|
||||
import Foundation
|
||||
|
||||
struct YapsFile: Identifiable, Hashable {
|
||||
|
||||
let id = UUID()
|
||||
|
||||
var index: Int
|
||||
var name: String
|
||||
var file: URL
|
||||
var current: Bool
|
||||
var isCurrent: Bool
|
||||
|
||||
init(index: Int, name: String, file: URL, isCurrent: Bool) {
|
||||
self.index = index
|
||||
self.name = name
|
||||
self.file = file
|
||||
self.isCurrent = isCurrent
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,11 +12,15 @@ import SwiftUI
|
||||
class Shared: ObservableObject {
|
||||
static let shared = Shared()
|
||||
|
||||
@Published var fileList = [YapsFile]()
|
||||
@Published var currentFile: YapsFile = YapsFile(index: -1, name: "EMPTY", file: .init(fileURLWithPath: "Y"), current: false)
|
||||
var fileList: ObservableArray<YapsFile>
|
||||
var currentFile: YapsFile
|
||||
var destination: URL
|
||||
var destinationDefined: Bool
|
||||
|
||||
var destination: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
var destinationDefined: Bool = false
|
||||
|
||||
private init() { }
|
||||
init() {
|
||||
self.fileList = ObservableArray(array: [])
|
||||
self.currentFile = YapsFile(index: -1, name: "EMPTY", file: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0], isCurrent: false)
|
||||
self.destination = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
self.destinationDefined = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,8 +11,7 @@ import SwiftUI
|
||||
|
||||
struct YapsFileCell: View {
|
||||
@State var focused: Bool
|
||||
|
||||
var yapsFile : YapsFile
|
||||
@State var yapsFile : YapsFile
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
@ -42,26 +41,3 @@ struct YapsFileCell: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct YapsFileCell_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
VStack(alignment: .leading) {
|
||||
// Dateiname
|
||||
Text("dateiname.xyz")
|
||||
.foregroundColor(.primary)
|
||||
.multilineTextAlignment(.leading)
|
||||
.lineLimit(1)
|
||||
.aspectRatio(contentMode: .fill)
|
||||
|
||||
// Details
|
||||
Text("xyz")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.leading)
|
||||
.lineLimit(1)
|
||||
.aspectRatio(contentMode: .fill)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
96
YAPS/YAPS/Toolbar.swift
Normal file
96
YAPS/YAPS/Toolbar.swift
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// Toolbar.swift
|
||||
// SUIToolbarPlay
|
||||
//
|
||||
// Created by Bill So on 4/23/20.
|
||||
// Copyright © 2020 Bill So. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
|
||||
extension NSImage.Name {
|
||||
static let folder = "toolbar_folder"
|
||||
}
|
||||
|
||||
extension NSToolbarItem.Identifier {
|
||||
static let calendar = NSToolbarItem.Identifier(rawValue: "ShowCalendar")
|
||||
static let today = NSToolbarItem.Identifier(rawValue: "GoToToday")
|
||||
}
|
||||
|
||||
extension NSToolbar {
|
||||
static let yapsToolbar: NSToolbar = {
|
||||
let toolbar = NSToolbar(identifier: "YapsToolbar")
|
||||
toolbar.displayMode = .iconOnly
|
||||
|
||||
return toolbar
|
||||
}()
|
||||
}
|
||||
|
||||
extension AppDelegate: NSToolbarDelegate {
|
||||
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
[.today, .calendar]
|
||||
}
|
||||
|
||||
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
[.today, .calendar]
|
||||
}
|
||||
|
||||
@objc
|
||||
func toolbarOpenSourceAction() {
|
||||
self.observedFileList.array.removeAll()
|
||||
self.observedFileList.array.append(contentsOf: FinderHelper.shared.askForFolderAndGetFiles())
|
||||
}
|
||||
|
||||
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
|
||||
switch itemIdentifier {
|
||||
case NSToolbarItem.Identifier.calendar:
|
||||
let button = NSButton(image: NSImage(named: .folder)!, target: nil, action: #selector(toolbarOpenSourceAction))
|
||||
button.bezelStyle = .texturedRounded
|
||||
return customToolbarItem(itemIdentifier: .calendar, label: "Open", paletteLabel: "Open", toolTip: "Open source folder", itemContent: button)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Mostly base on Apple sample code: https://developer.apple.com/documentation/appkit/touch_bar/integrating_a_toolbar_and_touch_bar_into_your_app
|
||||
*/
|
||||
func customToolbarItem(
|
||||
itemIdentifier: NSToolbarItem.Identifier,
|
||||
label: String,
|
||||
paletteLabel: String,
|
||||
toolTip: String,
|
||||
itemContent: NSButton) -> NSToolbarItem? {
|
||||
|
||||
let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)
|
||||
|
||||
toolbarItem.label = label
|
||||
toolbarItem.paletteLabel = paletteLabel
|
||||
toolbarItem.toolTip = toolTip
|
||||
/**
|
||||
You don't need to set a `target` if you know what you are doing.
|
||||
|
||||
In this example, AppDelegate is also the toolbar delegate.
|
||||
|
||||
Since AppDelegate is not a responder, implementing an IBAction in the AppDelegate class has no effect. Try using a subclass of NSWindow or NSWindowController to implement your action methods and use them as the toolbar delegate instead.
|
||||
|
||||
Ref: https://developer.apple.com/documentation/appkit/nstoolbaritem/1525982-target
|
||||
|
||||
From doc:
|
||||
|
||||
If target is nil, the toolbar will call action and attempt to invoke the action on the first responder and, failing that, pass the action up the responder chain.
|
||||
*/
|
||||
//toolbarItem.target = itemContent.target
|
||||
//toolbarItem.action = itemContent.action
|
||||
|
||||
toolbarItem.view = itemContent
|
||||
|
||||
// We actually need an NSMenuItem here, so we construct one.
|
||||
let menuItem: NSMenuItem = NSMenuItem()
|
||||
menuItem.submenu = nil
|
||||
menuItem.title = label
|
||||
toolbarItem.menuFormRepresentation = menuItem
|
||||
|
||||
return toolbarItem
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user