Skip to content

Commit 223232e

Browse files
authored
feat: journal dropdown (#5)
1 parent 569fced commit 223232e

File tree

8 files changed

+260
-126
lines changed

8 files changed

+260
-126
lines changed

Horizon.xcodeproj/project.pbxproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
E276483825C50824007ECF9B /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = E276483725C50824007ECF9B /* Store.swift */; };
1313
E276484125C5CB74007ECF9B /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = E276484025C5CB74007ECF9B /* KeychainAccess */; };
1414
E283259825C109980021BD90 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E283259725C109980021BD90 /* Constants.swift */; };
15+
E2943D1725DB2EFE0012CDCB /* Dropdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2943D1625DB2EFE0012CDCB /* Dropdown.swift */; };
1516
E2AEB10B25BDF413003BD251 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2AEB10A25BDF413003BD251 /* AppDelegate.swift */; };
1617
E2AEB10F25BDF413003BD251 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2AEB10E25BDF413003BD251 /* Assets.xcassets */; };
1718
E2AEB11225BDF413003BD251 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2AEB11125BDF413003BD251 /* Preview Assets.xcassets */; };
@@ -60,6 +61,7 @@
6061
E275D2B225BFA63E00320CA5 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
6162
E276483725C50824007ECF9B /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
6263
E283259725C109980021BD90 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
64+
E2943D1625DB2EFE0012CDCB /* Dropdown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dropdown.swift; sourceTree = "<group>"; };
6365
E2AEB10725BDF413003BD251 /* Horizon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Horizon.app; sourceTree = BUILT_PRODUCTS_DIR; };
6466
E2AEB10A25BDF413003BD251 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
6567
E2AEB10E25BDF413003BD251 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -242,6 +244,7 @@
242244
isa = PBXGroup;
243245
children = (
244246
E275D2B225BFA63E00320CA5 /* TextView.swift */,
247+
E2943D1625DB2EFE0012CDCB /* Dropdown.swift */,
245248
);
246249
path = Components;
247250
sourceTree = "<group>";
@@ -449,6 +452,7 @@
449452
E2AEB10B25BDF413003BD251 /* AppDelegate.swift in Sources */,
450453
E2AEB15B25BDF4A7003BD251 /* Journal.swift in Sources */,
451454
E2AEB17F25BDF537003BD251 /* StatusBarMenuItem.swift in Sources */,
455+
E2943D1725DB2EFE0012CDCB /* Dropdown.swift in Sources */,
452456
E283259825C109980021BD90 /* Constants.swift in Sources */,
453457
);
454458
runOnlyForDeploymentPostprocessing = 0;
@@ -620,7 +624,7 @@
620624
CODE_SIGN_IDENTITY = "Apple Development";
621625
CODE_SIGN_STYLE = Automatic;
622626
COMBINE_HIDPI_IMAGES = YES;
623-
CURRENT_PROJECT_VERSION = 111;
627+
CURRENT_PROJECT_VERSION = 112;
624628
DEVELOPMENT_ASSET_PATHS = "\"Horizon/Preview Content\"";
625629
DEVELOPMENT_TEAM = LCFAPK739L;
626630
ENABLE_HARDENED_RUNTIME = YES;
@@ -631,7 +635,7 @@
631635
"@executable_path/../Frameworks",
632636
);
633637
MACOSX_DEPLOYMENT_TARGET = 11.0;
634-
MARKETING_VERSION = 1.1.1;
638+
MARKETING_VERSION = 1.1.2;
635639
PRODUCT_BUNDLE_IDENTIFIER = co.meagher.Horizon;
636640
PRODUCT_NAME = "$(TARGET_NAME)";
637641
SWIFT_VERSION = 5.0;
@@ -647,7 +651,7 @@
647651
CODE_SIGN_IDENTITY = "Apple Development";
648652
CODE_SIGN_STYLE = Automatic;
649653
COMBINE_HIDPI_IMAGES = YES;
650-
CURRENT_PROJECT_VERSION = 111;
654+
CURRENT_PROJECT_VERSION = 112;
651655
DEVELOPMENT_ASSET_PATHS = "\"Horizon/Preview Content\"";
652656
DEVELOPMENT_TEAM = LCFAPK739L;
653657
ENABLE_HARDENED_RUNTIME = YES;
@@ -658,7 +662,7 @@
658662
"@executable_path/../Frameworks",
659663
);
660664
MACOSX_DEPLOYMENT_TARGET = 11.0;
661-
MARKETING_VERSION = 1.1.1;
665+
MARKETING_VERSION = 1.1.2;
662666
PRODUCT_BUNDLE_IDENTIFIER = co.meagher.Horizon;
663667
PRODUCT_NAME = "$(TARGET_NAME)";
664668
SWIFT_VERSION = 5.0;

Horizon/AppDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import UserNotifications
1111
@NSApplicationMain
1212
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate, SUUpdaterDelegate {
1313
var store = Store()
14-
14+
1515
lazy var panel = PublishPanel(
1616
store: store,
1717
onClose: closePanel

Horizon/Components/Dropdown.swift

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// By Tom Meagher on 2/15/21 at 17:36
2+
3+
import Foundation
4+
import SwiftUI
5+
6+
struct Dropdown<T: Hashable>: NSViewRepresentable {
7+
@Binding
8+
var selectedValue: T?
9+
10+
@Binding
11+
var items: [T]
12+
13+
private var disabled: Bool
14+
15+
private let getItemTitle: ((T) -> String)
16+
private let onChange: ((T) -> Void)?
17+
18+
init(
19+
selectedValue: Binding<T?>,
20+
items: Binding<[T]>,
21+
disabled: Bool,
22+
getItemTitle: @escaping ((T) -> String),
23+
onChange: ((T) -> Void)? = nil
24+
) {
25+
self._selectedValue = selectedValue
26+
self._items = items
27+
self.disabled = disabled
28+
self.getItemTitle = getItemTitle
29+
self.onChange = onChange
30+
}
31+
32+
func makeCoordinator() -> Coordinator {
33+
return Coordinator(self)
34+
}
35+
36+
func makeNSView(context: Context) -> NSPopUpButton {
37+
let button = NSPopUpButton(frame: .zero, pullsDown: false)
38+
39+
// Add local shortcut `⌘ j` for opening dropdown
40+
NSEvent.addLocalMonitorForEvents(matching: [.keyDown]) { event in
41+
if event.keyCode == 38 && event.modifierFlags.contains(.command) {
42+
button.performClick(nil)
43+
}
44+
return event
45+
}
46+
47+
return button
48+
}
49+
50+
func updateNSView(_ view: NSPopUpButton, context: Context) {
51+
view.removeAllItems()
52+
53+
for (index, element) in items.enumerated() {
54+
let menuItem = NSMenuItem(
55+
title: self.getItemTitle(element),
56+
action: #selector(Coordinator.valueChanged(_:)),
57+
keyEquivalent: "\(index + 1)"
58+
)
59+
menuItem.target = context.coordinator
60+
view.menu?.insertItem(menuItem, at: index)
61+
}
62+
63+
if let selectedValue = self.selectedValue {
64+
let index = self.items.firstIndex(of: selectedValue) ?? 0
65+
view.selectItem(at: index)
66+
}
67+
68+
view.isEnabled = !disabled
69+
}
70+
}
71+
72+
extension Dropdown {
73+
final class Coordinator: NSObject {
74+
var parent: Dropdown<T>
75+
76+
init(_ parent: Dropdown<T>) {
77+
self.parent = parent
78+
}
79+
80+
@objc
81+
func valueChanged(_ sender: NSMenuItem) {
82+
guard let index = sender.menu?.index(of: sender) else { return }
83+
let item = self.parent.items[index]
84+
self.parent._selectedValue.wrappedValue = item
85+
self.parent.onChange?(item)
86+
}
87+
}
88+
}

Horizon/Components/TextView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct TextView: NSViewRepresentable {
7575
}
7676

7777
func updateNSView(_ view: CustomTextView, context: Context) {
78+
// TODO: Check out https://www.markusbodner.com/til/2021/02/08/multi-line-text-field-with-swiftui-on-macos/
7879
view.text = text
7980

8081
view.isEditable = isEditable

Horizon/Models/Journal.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
import Foundation
44

5-
struct Journal: Codable, Identifiable, Equatable {
5+
struct Journal: Codable, Equatable, Hashable, Identifiable {
66
var id: Int
77
var entryTemplate: String?
88
var entryTemplateActive: Bool
99
var isPrivate: Bool
1010
var lastEntryAt: Date?
1111
var slug: String
1212
var title: String
13+
14+
func hash(into hasher: inout Hasher) {
15+
hasher.combine(id)
16+
}
1317

1418
enum CodingKeys: String, CodingKey {
1519
case id

Horizon/Publish/PublishPanel.swift

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,45 @@ class PublishPanel: NSPanel {
1414
) {
1515
super.init(
1616
contentRect: NSRect(x: 0, y: 0, width: 440, height: 300),
17-
styleMask: [.nonactivatingPanel],
17+
styleMask: [.nonactivatingPanel, .titled, .fullSizeContentView],
1818
backing: .buffered,
1919
defer: false
2020
)
2121

22-
backgroundColor = .clear
2322
center()
23+
isMovableByWindowBackground = true
24+
hasShadow = true
25+
setFrameAutosaveName("main")
26+
2427
collectionBehavior = [
2528
.canJoinAllSpaces,
2629
.fullScreenAuxiliary
2730
]
28-
hasShadow = true
2931
hidesOnDeactivate = false
30-
isMovableByWindowBackground = true
3132
isExcludedFromWindowsMenu = false
33+
3234
isFloatingPanel = true
33-
level = .popUpMenu
35+
level = .floating
36+
37+
titleVisibility = .hidden
38+
titlebarAppearsTransparent = true
3439

3540
let rootView = PublishView(
3641
viewModel: PublishViewModel(
3742
store: store,
3843
onClose: onClose
39-
)
40-
).environmentObject(store)
41-
contentView = NSHostingView(rootView: rootView)
44+
),
45+
parent: self
46+
)
47+
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
48+
.edgesIgnoringSafeArea(.all)
49+
.environmentObject(store)
50+
51+
contentView = NSHostingView(rootView: rootView)
52+
resize()
53+
}
54+
55+
func resize() {
56+
setContentSize(contentView!.fittingSize)
4257
}
4358
}

0 commit comments

Comments
 (0)