Skip to content

Commit 9775c6c

Browse files
committed
Enhance ContentView layout and styling; add CustomWindow and AppDelegate for improved app structure
1 parent af67c00 commit 9775c6c

File tree

3 files changed

+126
-21
lines changed

3 files changed

+126
-21
lines changed

VocabularyApp/ContentView.swift

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
//
2-
// ContentView.swift
3-
// VocabularyApp
4-
//
5-
// Created by Parth Desai on 1/10/25.
6-
//
7-
81
import SwiftUI
92
import AppKit
103

@@ -18,8 +11,11 @@ struct ContentView: View {
1811
VStack(spacing: 16) {
1912
// Word Section
2013
Text(word)
21-
.font(.system(size: 24, weight: .bold))
14+
.font(.system(size: 28, weight: .bold))
2215
.foregroundColor(.primary)
16+
.padding()
17+
.background(Color.blue.opacity(0.1))
18+
.cornerRadius(8)
2319
.onTapGesture {
2420
openGoogleSearch(for: word)
2521
}
@@ -41,8 +37,13 @@ struct ContentView: View {
4137
.font(.body)
4238
.foregroundColor(.primary)
4339
.multilineTextAlignment(.leading)
40+
.fixedSize(horizontal: false, vertical: true) // Add this
41+
.padding()
42+
.background(Color.gray.opacity(0.1))
43+
.cornerRadius(8)
4444
}
4545
.padding(.horizontal)
46+
.frame(maxWidth: .infinity, alignment: .leading)
4647

4748
// Example Section
4849
VStack(alignment: .leading, spacing: 8) {
@@ -54,18 +55,23 @@ struct ContentView: View {
5455
.italic()
5556
.foregroundColor(.primary)
5657
.multilineTextAlignment(.leading)
58+
.fixedSize(horizontal: false, vertical: true) // Add this
59+
.padding()
60+
.background(Color.gray.opacity(0.1))
61+
.cornerRadius(8)
5762
}
5863
.padding(.horizontal)
64+
.frame(maxWidth: .infinity, alignment: .leading)
5965

60-
Divider() // Separator for buttons
66+
Divider()
6167

6268
// Buttons Section
6369
HStack {
6470
Button("Memorized it") {
6571
markAsMemorized()
6672
}
6773
.buttonStyle(.borderedProminent)
68-
.keyboardShortcut("m", modifiers: []) // Keyboard shortcut for convenience
74+
.keyboardShortcut("m", modifiers: [])
6975

7076
Button("Next Word") {
7177
loadRandomWord()
@@ -76,7 +82,7 @@ struct ContentView: View {
7682
.frame(maxWidth: .infinity, alignment: .center)
7783
}
7884
.padding()
79-
.frame(width: 360, height: 260)
85+
.frame(width: 360)
8086
.background(Color(NSColor.windowBackgroundColor))
8187
.cornerRadius(12)
8288
.shadow(radius: 8)
@@ -85,13 +91,11 @@ struct ContentView: View {
8591
}
8692
}
8793

88-
// Open Google search for the word
8994
func openGoogleSearch(for query: String) {
9095
guard let url = URL(string: "https://www.google.com/search?q=\(query)") else { return }
9196
NSWorkspace.shared.open(url)
9297
}
9398

94-
// Fetch a random word from Google Sheets
9599
func loadRandomWord() {
96100
let sheetID = "1U66wi1O42CeuC_7QuMTbF2hlewJinCAmgTOJpZFbV1k"
97101
let apiKey = "AIzaSyBjNi7cYMOO_RL_qPqI4wuVP71UZPs72Jg"
@@ -129,7 +133,6 @@ struct ContentView: View {
129133
}.resume()
130134
}
131135

132-
// Mark the current word as memorized
133136
func markAsMemorized() {
134137
guard let wordID = wordID else { return }
135138

@@ -164,4 +167,4 @@ struct ContentView: View {
164167
}
165168
}.resume()
166169
}
167-
}
170+
}

VocabularyApp/CustomWindow.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import SwiftUI
2+
import AppKit
3+
4+
class CustomWindow: NSWindow {
5+
override var canBecomeKey: Bool {
6+
return true
7+
}
8+
}
9+
10+
class CustomWindowController: NSWindowController, NSWindowDelegate {
11+
convenience init(contentView: NSView) {
12+
let window = CustomWindow(
13+
contentRect: NSRect(x: 0, y: 0, width: 360, height: 400),
14+
styleMask: [.titled, .closable, .resizable],
15+
backing: .buffered, defer: false)
16+
window.contentView = contentView
17+
self.init(window: window)
18+
window.delegate = self
19+
}
20+
21+
func windowWillClose(_ notification: Notification) {
22+
NSApp.terminate(nil)
23+
}
24+
}

VocabularyApp/VocabularyAppApp.swift

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,92 @@ import SwiftUI
99

1010
@main
1111
struct VocabularyApp: App {
12-
@State private var isMenuOpen = true // Track whether the menu is open
12+
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
1313

1414
var body: some Scene {
15-
MenuBarExtra("GRE Word", systemImage: "book", isInserted: $isMenuOpen) {
16-
ContentView()
17-
.onDisappear {
18-
isMenuOpen = true // Reopen the menu after disappearing
19-
}
15+
Settings {
16+
EmptyView()
17+
}
18+
}
19+
}
20+
21+
class AppDelegate: NSObject, NSApplicationDelegate {
22+
var statusItem: NSStatusItem?
23+
var popover: NSPopover?
24+
var eventMonitor: EventMonitor?
25+
26+
func applicationDidFinishLaunching(_ notification: Notification) {
27+
let contentView = ContentView()
28+
29+
popover = NSPopover()
30+
popover?.contentSize = NSSize(width: 360, height: 0)
31+
popover?.behavior = .transient
32+
33+
let hostingController = NSHostingController(rootView: contentView)
34+
hostingController.view.setFrameSize(NSSize(width: 360, height: 1))
35+
36+
hostingController.view.autoresizingMask = [.height]
37+
38+
popover?.contentViewController = hostingController
39+
40+
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
41+
if let button = statusItem?.button {
42+
button.image = NSImage(systemSymbolName: "book", accessibilityDescription: "Vocabulary App")
43+
button.action = #selector(togglePopover(_:))
44+
}
45+
46+
eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in
47+
if let strongSelf = self, strongSelf.popover?.isShown == true {
48+
strongSelf.closePopover(event)
49+
}
50+
}
51+
}
52+
53+
@objc func togglePopover(_ sender: Any?) {
54+
if let popover = popover {
55+
if popover.isShown {
56+
closePopover(sender)
57+
} else {
58+
showPopover(sender)
59+
}
60+
}
61+
}
62+
63+
func showPopover(_ sender: Any?) {
64+
if let button = statusItem?.button {
65+
popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
66+
eventMonitor?.start()
67+
}
68+
}
69+
70+
func closePopover(_ sender: Any?) {
71+
popover?.performClose(sender)
72+
eventMonitor?.stop()
73+
}
74+
}
75+
76+
class EventMonitor {
77+
private var monitor: Any?
78+
private let mask: NSEvent.EventTypeMask
79+
private let handler: (NSEvent?) -> Void
80+
81+
init(mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> Void) {
82+
self.mask = mask
83+
self.handler = handler
84+
}
85+
86+
deinit {
87+
stop()
88+
}
89+
90+
func start() {
91+
monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler)
92+
}
93+
94+
func stop() {
95+
if monitor != nil {
96+
NSEvent.removeMonitor(monitor!)
97+
monitor = nil
2098
}
2199
}
22100
}

0 commit comments

Comments
 (0)