From c9f3505d745a785b9eb245c3f4bf6beeb7f406ad Mon Sep 17 00:00:00 2001 From: chanhihi Date: Wed, 9 Aug 2023 14:34:51 +0900 Subject: [PATCH 1/7] =?UTF-8?q?refactor(Architecture=F0=9F=8F=9B):=20MVC?= =?UTF-8?q?=20to=20MVVM=20in=20menubar=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Box42/Box/BoxModel.swift | 15 -- ...ntroller.swift => BoxViewController.swift} | 8 +- Box42/CPU/CPU.swift | 2 +- Box42/Menubar/MenubarViewController.swift | 6 +- Box42/Resources/AppDelegate.swift | 173 ------------------ Box42/Resources/EventMonitor.swift | 34 ---- 6 files changed, 7 insertions(+), 231 deletions(-) rename Box42/Box/{BoxController.swift => BoxViewController.swift} (97%) diff --git a/Box42/Box/BoxModel.swift b/Box42/Box/BoxModel.swift index b188c95..d6f114c 100644 --- a/Box42/Box/BoxModel.swift +++ b/Box42/Box/BoxModel.swift @@ -7,21 +7,6 @@ import WebKit -public extension NSScreen { - static let screenSize = NSScreen.main?.visibleFrame.size - static let screenWidth = screenSize!.width - static let screenHeight = screenSize!.height - static let halfOfScreen = (x: screenWidth / 2, y: screenHeight / 2) - static let customScreenSize = (x: CGFloat(900), y: screenHeight - 132) -} - -public struct BoxViewSize { - var halfSize: (width: CGFloat, height: CGFloat) = (NSScreen.halfOfScreen.x, NSScreen.halfOfScreen.y) - var size: (width: CGFloat, height: CGFloat) = (NSScreen.customScreenSize.x, NSScreen.customScreenSize.y) - var buttonGroupSize: (width: CGFloat, height: CGFloat) = (CGFloat(132), NSScreen.customScreenSize.y) - var viewStack = [NSView()] -} - public class BoxStatus { var isPin: Bool = false } diff --git a/Box42/Box/BoxController.swift b/Box42/Box/BoxViewController.swift similarity index 97% rename from Box42/Box/BoxController.swift rename to Box42/Box/BoxViewController.swift index 5d9cfc9..9d19ef9 100644 --- a/Box42/Box/BoxController.swift +++ b/Box42/Box/BoxViewController.swift @@ -9,7 +9,7 @@ import Cocoa import AppKit import WebKit -class BoxController: NSViewController, WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate { +class BoxViewController: NSViewController, WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate { var ad = NSApplication.shared.delegate as? AppDelegate let url = URLModel() var wvc = WebViewController() @@ -262,12 +262,12 @@ class BoxController: NSViewController, WKScriptMessageHandler, WKUIDelegate, WKN } } -extension BoxController { - static func freshController() -> BoxController { +extension BoxViewController { + static func freshController() -> BoxViewController { let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil) let identifier = NSStoryboard.SceneIdentifier("BoxController") - guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? BoxController else { + guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? BoxViewController else { fatalError("Story Board Not Found") } return viewcontroller diff --git a/Box42/CPU/CPU.swift b/Box42/CPU/CPU.swift index 33c11a9..7f59495 100644 --- a/Box42/CPU/CPU.swift +++ b/Box42/CPU/CPU.swift @@ -54,7 +54,7 @@ public class CPU { return true } - public func processCPU(_ statusBar: StatusBar) -> Bool { + func processCPU(_ statusBar: StatusBar) -> Bool { cpuTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true, block: { _ in self.usageCPU() statusBar.interval = 0.02 * (100 - max(0.0, min(99.0, self.usage.value))) / 6 diff --git a/Box42/Menubar/MenubarViewController.swift b/Box42/Menubar/MenubarViewController.swift index 032e463..7c29ca7 100644 --- a/Box42/Menubar/MenubarViewController.swift +++ b/Box42/Menubar/MenubarViewController.swift @@ -14,9 +14,7 @@ class MenubarViewController: NSWorkspace { lazy var eventMonitor: EventMonitor = self.setupEventMonitor() var statusBarVM: StatusBarViewModel? // 옵셔널로 선언 let menuBarView = MenuBarView() - lazy var keyboardEventMonitor = EventMonitor(mask: [.keyDown]) { [weak self] event in - print("keydown") - } + // 초기화 함수나 다른 적절한 시점에 호출 func initializeStatusBarVM() { statusBarVM = StatusBarViewModel(eventMonitor: eventMonitor) @@ -56,7 +54,7 @@ class MenubarViewController: NSWorkspace { } func popoverHandler() { - popover.contentViewController = BoxController.freshController() + popover.contentViewController = BoxViewController.freshController() } func setupEventMonitor() -> EventMonitor { diff --git a/Box42/Resources/AppDelegate.swift b/Box42/Resources/AppDelegate.swift index 688a686..5eaba15 100644 --- a/Box42/Resources/AppDelegate.swift +++ b/Box42/Resources/AppDelegate.swift @@ -7,179 +7,6 @@ import Cocoa -@main -class AppDelegate: NSObject, NSApplicationDelegate { - let popover = NSPopover() - var eventMonitor: EventMonitor? - var menubarController = MenuBarController() - var boxController = BoxController() - var boxStatus = BoxStatus() - - func applicationWillFinishLaunching(_ notification: Notification) { - menubarController.buttonInit() - } - - func applicationDidFinishLaunching(_ aNotification: Notification) { - menubarController.startRunning() - buttonActionInit() - popoverHandler() - eventMonitorHandler() - } - - func popoverHandler() { - popover.contentViewController = BoxController.freshController() - } - - func buttonActionInit() { - menubarController.statusBar.statusItem.button?.action = #selector(togglePopover(_:)) - } - - func eventMonitorHandler() { - eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in - if let strongSelf = self, strongSelf.popover.isShown { - if self?.boxStatus.isPin == false { - strongSelf.closePopover(sender: event) - } - } - } - } - - @objc func togglePopover(_ sender: Any?) { - if popover.isShown { - closePopover(sender: sender) - } else { - showPopover(sender: sender) - } - } - - func showPopover(sender: Any?) { - if let button = menubarController.statusBar.statusItem.button { - popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY) - } - eventMonitor?.start() - } - - func closePopover(sender: Any?) { - popover.performClose(sender) - eventMonitor?.stop() - } - - func applicationWillTerminate(_ aNotification: Notification) { - // Insert code here to tear down your application - } - - func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { - return true - } - - // MARK: - Core Data stack - - lazy var persistentContainer: NSPersistentContainer = { - /* - The persistent container for the application. This implementation - creates and returns a container, having loaded the store for the - application to it. This property is optional since there are legitimate - error conditions that could cause the creation of the store to fail. - */ - let container = NSPersistentContainer(name: "Box42") - container.loadPersistentStores(completionHandler: { (storeDescription, error) in - if let error = error { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - - /* - Typical reasons for an error here include: - * The parent directory does not exist, cannot be created, or disallows writing. - * The persistent store is not accessible, due to permissions or data protection when the device is locked. - * The device is out of space. - * The store could not be migrated to the current model version. - Check the error message to determine what the actual problem was. - */ - fatalError("Unresolved error \(error)") - } - }) - return container - }() - - // MARK: - Core Data Saving and Undo support - - @IBAction func saveAction(_ sender: AnyObject?) { - // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user. - let context = persistentContainer.viewContext - - if !context.commitEditing() { - NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing before saving") - } - if context.hasChanges { - do { - try context.save() - } catch { - // Customize this code block to include application-specific recovery steps. - let nserror = error as NSError - NSApplication.shared.presentError(nserror) - } - } - } - - func windowWillReturnUndoManager(window: NSWindow) -> UndoManager? { - // Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application. - return persistentContainer.viewContext.undoManager - } - - func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { - // Save changes in the application's managed object context before the application terminates. - let context = persistentContainer.viewContext - - if !context.commitEditing() { - NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing to terminate") - return .terminateCancel - } - - if !context.hasChanges { - return .terminateNow - } - - do { - try context.save() - } catch { - let nserror = error as NSError - - // Customize this code block to include application-specific recovery steps. - let result = sender.presentError(nserror) - if (result) { - return .terminateCancel - } - - let question = NSLocalizedString("Could not save changes while quitting. Quit anyway?", comment: "Quit without saves error question message") - let info = NSLocalizedString("Quitting now will lose any changes you have made since the last successful save", comment: "Quit without saves error question info"); - let quitButton = NSLocalizedString("Quit anyway", comment: "Quit anyway button title") - let cancelButton = NSLocalizedString("Cancel", comment: "Cancel button title") - let alert = NSAlert() - alert.messageText = question - alert.informativeText = info - alert.addButton(withTitle: quitButton) - alert.addButton(withTitle: cancelButton) - - let answer = alert.runModal() - if answer == .alertSecondButtonReturn { - return .terminateCancel - } - } - // If we got here, it is time to quit. - return .terminateNow - } - -} - -// -// AppDelegate.swift -// Box42 -// -// Created by Chan on 2023/03/15. -// - -import Cocoa - @main class AppDelegate: NSObject, NSApplicationDelegate { var menubarController = MenubarViewController() diff --git a/Box42/Resources/EventMonitor.swift b/Box42/Resources/EventMonitor.swift index e56d895..ca7722b 100644 --- a/Box42/Resources/EventMonitor.swift +++ b/Box42/Resources/EventMonitor.swift @@ -7,40 +7,6 @@ import Cocoa -public class EventMonitor { - private var monitor: Any? - private let mask: NSEvent.EventTypeMask - private let handler: (NSEvent?) -> Void - - public init(monitor: Any? = nil, mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> Void) { - self.mask = mask - self.handler = handler - } - - deinit { - stop() - } - - public func start() { - monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler) - } - - public func stop() { - if monitor != nil { - NSEvent.removeMonitor(monitor!) - monitor = nil - } - } -} -// -// EventMonitor.swift -// Box42 -// -// Created by Chan on 2023/03/16. -// - -import Cocoa - public class EventMonitor { private var monitor: Any? private let mask: NSEvent.EventTypeMask From 13676b1f0ccfa039e4396a54e3eb949d2599c82b Mon Sep 17 00:00:00 2001 From: chanhihi Date: Fri, 11 Aug 2023 04:04:38 +0900 Subject: [PATCH 2/7] =?UTF-8?q?refactor:=20extension=EC=9D=98=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EA=B5=AC=EC=A1=B0=EB=A5=BC=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=95=98=EC=98=80=EC=8A=B5=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Box42/Extensions/NSScreen.swift | 16 ++++++++++++++++ .../StringExtension.swift | 0 2 files changed, 16 insertions(+) create mode 100644 Box42/Extensions/NSScreen.swift rename Box42/{Resources => Extensions}/StringExtension.swift (100%) diff --git a/Box42/Extensions/NSScreen.swift b/Box42/Extensions/NSScreen.swift new file mode 100644 index 0000000..778e4fd --- /dev/null +++ b/Box42/Extensions/NSScreen.swift @@ -0,0 +1,16 @@ +// +// NSScreen.swift +// Box42 +// +// Created by Chanhee Kim on 8/11/23. +// + +import Cocoa + +extension NSScreen { + static let screenSize = NSScreen.main?.visibleFrame.size + static let screenWidth = screenSize!.width + static let screenHeight = screenSize!.height + static let halfOfScreen = (x: screenWidth / 2, y: screenHeight / 2) + static let customScreenSize = (x: CGFloat(900), y: screenHeight - 132) +} diff --git a/Box42/Resources/StringExtension.swift b/Box42/Extensions/StringExtension.swift similarity index 100% rename from Box42/Resources/StringExtension.swift rename to Box42/Extensions/StringExtension.swift From e6cd5b0b77978c4a666a98cc14420868a7773740 Mon Sep 17 00:00:00 2001 From: chanhihi Date: Fri, 11 Aug 2023 04:05:14 +0900 Subject: [PATCH 3/7] =?UTF-8?q?refactor:=20Menubar=EB=A5=BC=20mvc=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=EC=97=90=EC=84=9C=20mvvm=EA=B5=AC=EC=A1=B0=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=ED=95=98=EC=98=80=EC=8A=B5=EB=8B=88?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Box42/Menubar/MenubarModel.swift | 2 + Box42/Menubar/MenubarViewController.swift | 81 ++++++++++++++++------- Box42/Menubar/MenubarViewModel.swift | 37 ++++++++--- 3 files changed, 88 insertions(+), 32 deletions(-) diff --git a/Box42/Menubar/MenubarModel.swift b/Box42/Menubar/MenubarModel.swift index 4e920ba..24a9a73 100644 --- a/Box42/Menubar/MenubarModel.swift +++ b/Box42/Menubar/MenubarModel.swift @@ -8,6 +8,8 @@ import AppKit class StatusBar { + static let shared = StatusBar() + var statusItem: NSStatusItem var frames: [NSImage] var cnt: Int diff --git a/Box42/Menubar/MenubarViewController.swift b/Box42/Menubar/MenubarViewController.swift index 7c29ca7..b381e1c 100644 --- a/Box42/Menubar/MenubarViewController.swift +++ b/Box42/Menubar/MenubarViewController.swift @@ -11,17 +11,13 @@ import AppKit class MenubarViewController: NSWorkspace { var popover = NSPopover() - lazy var eventMonitor: EventMonitor = self.setupEventMonitor() - var statusBarVM: StatusBarViewModel? // 옵셔널로 선언 + var statusBarVM = StatusBarViewModel() let menuBarView = MenuBarView() - - // 초기화 함수나 다른 적절한 시점에 호출 - func initializeStatusBarVM() { - statusBarVM = StatusBarViewModel(eventMonitor: eventMonitor) - } - + lazy var eventMonitor: EventMonitor = self.setupEventMonitor() + var boxWindowController: BoxWindowController? + + func menubarViewControllerInit() { - self.initializeStatusBarVM() self.buttonInit() } @@ -29,38 +25,43 @@ class MenubarViewController: NSWorkspace { self.menubarStartRunning() self.buttonActionInit() self.popoverHandler() + self.startEventMonitoring() + } + + func startEventMonitoring() { + eventMonitor.start() + } + + func stopEventMonitoring() { + eventMonitor.stop() } func menubarStartRunning() { - statusBarVM?.startRunning() + statusBarVM.startRunning() } func menubarStopRunning() { - statusBarVM?.stopRunning() + statusBarVM.stopRunning() } func buttonInit() { - buttonImageChange("box") - statusBarVM?.statusButtonAppear() + buttonImageChange("Cat") + statusBarVM.statusButtonAppear() } func buttonImageChange(_ img: String) { - statusBarVM?.changeStatusBarIcon(img) + statusBarVM.changeStatusBarIcon(img) } func buttonActionInit() { - statusBarVM?.statusBar.statusItem.button?.action = #selector(togglePopover(_:)) - statusBarVM?.statusBar.statusItem.button?.target = self - } - - func popoverHandler() { - popover.contentViewController = BoxViewController.freshController() + statusBarVM.statusBar.statusItem.button?.action = #selector(togglePopover(_:)) + statusBarVM.statusBar.statusItem.button?.target = self } func setupEventMonitor() -> EventMonitor { return EventMonitor(mask: [.leftMouseDown, .rightMouseDown, .otherMouseDown]) { [weak self] event in if let strongSelf = self, strongSelf.popover.isShown { - if StateManager.shared.isPin == false && event?.buttonNumber != 2 { + if StateManager.shared.getIsPin() == false && event?.buttonNumber != 2 { strongSelf.closePopover(sender: event) } } else if let strongSelf = self, !strongSelf.popover.isShown { @@ -79,15 +80,47 @@ class MenubarViewController: NSWorkspace { } } + func popoverHandler() { + popover.contentViewController = BoxViewController.freshController() + } + func showPopover(sender: Any?) { - if let button = statusBarVM?.statusBar.statusItem.button { + if let event = sender as? NSEvent { + if event.type == .otherMouseDown { + self.showWindow(sender: sender) + } + } else if let button = statusBarVM.statusBar.statusItem.button { popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY) } - eventMonitor.start() } + + func showWindow(sender: Any?) { + boxWindowController?.close() + + boxWindowController = BoxWindowController(windowNibName: NSNib.Name("BoxWindowController")) + + // status bar 버튼의 위치를 얻어옵니다. + if let button = statusBarVM.statusBar.statusItem.button, + let screenFrame = NSScreen.main?.frame, + let window = boxWindowController?.window { + + let buttonFrame = button.window?.convertToScreen(button.frame) ?? NSZeroRect + + // 버튼 위치 아래에 윈도우를 표시하려면 + let desiredPosition = NSPoint(x: buttonFrame.origin.x, y: buttonFrame.origin.y - window.frame.height) + + // 혹은, 버튼 위치의 중앙에 윈도우를 표시하려면 +// let desiredPosition = NSPoint(x: buttonFrame.midX - window.frame.width / 2, y: buttonFrame.origin.y - window.frame.height) + + // 윈도우의 위치를 설정 + window.setFrameOrigin(desiredPosition) + } + boxWindowController?.contentViewController = BoxViewController.freshController() + boxWindowController?.showWindow(sender) + } + func closePopover(sender: Any?) { popover.performClose(sender) -// eventMonitor.stop() } } diff --git a/Box42/Menubar/MenubarViewModel.swift b/Box42/Menubar/MenubarViewModel.swift index 6f0b12c..d131b10 100644 --- a/Box42/Menubar/MenubarViewModel.swift +++ b/Box42/Menubar/MenubarViewModel.swift @@ -9,13 +9,12 @@ import AppKit class StatusBarViewModel { let cpu: CPU - let eventMonitor: EventMonitor let statusBar: StatusBar + private var currentAnimationWorkItem: DispatchWorkItem? - init (eventMonitor: EventMonitor) { - self.statusBar = StatusBar() + init () { + self.statusBar = StatusBar.shared self.cpu = CPU() - self.eventMonitor = eventMonitor } func statusButtonAppear() { @@ -24,12 +23,14 @@ class StatusBarViewModel { func changeStatusBarIcon(_ imgName: String) { statusBar.frames.removeAll() + switch imgName { - case "cat": for i in (0...4) {statusBar.frames.append(NSImage(imageLiteralResourceName: "cat_page\(i)"))} + case "Cat": for i in (0...4) {statusBar.frames.append(NSImage(imageLiteralResourceName: "cat_page\(i)"))} case "gon": for i in (1...5) {statusBar.frames.append(NSImage(imageLiteralResourceName: "gon_\(i)"))} case "gun": for i in (1...5) {statusBar.frames.append(NSImage(imageLiteralResourceName: "gun_\(i)"))} - case "lee": for i in (1...5) {statusBar.frames.append(NSImage(imageLiteralResourceName: "gam_\(i)"))} - case "box": for i in (1...4) {statusBar.frames.append(NSImage(imageLiteralResourceName: "42box_\(i)"))} + case "gam": for i in (1...5) {statusBar.frames.append(NSImage(imageLiteralResourceName: "gam_\(i)"))} + case "lee": for i in (1...5) {statusBar.frames.append(NSImage(imageLiteralResourceName: "lee_\(i)"))} + case "Box": for i in (1...4) {statusBar.frames.append(NSImage(imageLiteralResourceName: "42box_\(i)"))} case "box_oc": for i in (1...2) {statusBar.frames.append(NSImage(imageLiteralResourceName: "42box_oc\(i)"))} default : for i in (1...11) {statusBar.frames.append(NSImage(imageLiteralResourceName: "42flip_0\(i)"))} } @@ -37,19 +38,39 @@ class StatusBarViewModel { func startRunning() { statusBar.isRunning = cpu.processCPU(statusBar) - self.animate() +// self.animate() + scheduleAnimation() } func animate() { statusBar.statusItem.button?.image = statusBar.frames[statusBar.cnt] statusBar.cnt = (statusBar.cnt + 1) % statusBar.frames.count if !statusBar.isRunning { return } + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + statusBar.interval) { self.animate() } } + func scheduleAnimation() { + currentAnimationWorkItem?.cancel() + + let workItem = DispatchWorkItem { [weak self] in + self?.statusBar.statusItem.button?.image = self?.statusBar.frames[self?.statusBar.cnt ?? 0] + self?.statusBar.cnt = ((self?.statusBar.cnt)! + 1) % (self?.statusBar.frames.count ?? 1) + + if self?.statusBar.isRunning ?? false { + self?.scheduleAnimation() + } + } + + currentAnimationWorkItem = workItem + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + statusBar.interval, execute: workItem) + } + func stopRunning() { statusBar.isRunning = cpu.StopCPU() + currentAnimationWorkItem?.cancel() + statusBar.cnt = 0 } } From dea779efdc475b7a567f4ace52be44262ffee5ef Mon Sep 17 00:00:00 2001 From: chanhihi Date: Fri, 11 Aug 2023 04:06:11 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refactor:=20URLModel=EC=9D=84=20mvvm?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=EC=97=90=20=EB=A7=9E=EC=B6=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=98=EC=98=80=EC=8A=B5=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Box42/URL/URLModel.swift | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Box42/URL/URLModel.swift b/Box42/URL/URLModel.swift index 209828e..9763fce 100644 --- a/Box42/URL/URLModel.swift +++ b/Box42/URL/URLModel.swift @@ -8,20 +8,21 @@ import Foundation struct URLModel { - let URLdict: [String: URL] = [ - "home": URL(string: "https://42box.github.io/front-end/")!, - // "home": URL(string: "http://127.0.0.1:3000/")!, - "Box 42": URL(string: "https://42box.github.io/front-end/#/box")!, - "Intra 42": URL(string: "https://intra.42.fr")!, - "Jiphyeonjeon" : URL(string:"https://42library.kr")!, - "E-Library": URL(string:"https://42seoul.dkyobobook.co.kr/main.ink")!, - "24Hane": URL(string:"https://24hoursarenotenough.42seoul.kr")!, - "80000Coding": URL(string:"https://80000coding.oopy.io")!, - "where42": URL(string:"https://www.where42.kr")!, - "cabi": URL(string:"https://cabi.42seoul.io/")!, - "42gg": URL(string:"https://42gg.kr/")!, - ] + var id: UUID + var name: String + var url: String + init(name: String, url: String) { + self.id = UUID() + self.name = name + self.url = url + } +} + +struct URLModels { + var info: [URLModel] + + // Network logic let URLstring: [(String, String)] = [ ("home", "https://42box.github.io/front-end/"), // ("home", "http://127.0.0.1:3000/"), @@ -34,5 +35,12 @@ struct URLModel { ("where42", "https://www.where42.kr"), ("cabi", "https://cabi.42seoul.io/"), ("42gg", "https://42gg.kr/"), + ("textart", "https://textart.sh/") ] + + mutating func urlSetup() { + URLstring.forEach { (name, url) in + info.append(URLModel(name: name, url: url)) + } + } } From ec0aa903156a4ad73d2c920dd766ca861c2442ba Mon Sep 17 00:00:00 2001 From: chanhihi Date: Fri, 11 Aug 2023 04:06:58 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=EC=A0=84=EC=97=AD=EA=B4=80?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Box42/CPU/CPU.swift | 10 ++-------- Box42/Preferences/Icon.swift | 13 ++++++++++++- Box42/Resources/AppDelegate.swift | 2 +- Box42/Resources/Box42.entitlements | 2 ++ Box42/Resources/Info.plist | 21 +++++++++++++++++++++ Box42/Shared/StateManager.swift | 30 +++++++++++++++++++++++++++++- 6 files changed, 67 insertions(+), 11 deletions(-) diff --git a/Box42/CPU/CPU.swift b/Box42/CPU/CPU.swift index 7f59495..1d31df6 100644 --- a/Box42/CPU/CPU.swift +++ b/Box42/CPU/CPU.swift @@ -10,7 +10,6 @@ import Foundation public class CPU { var cpuTimer: Timer? = nil - var isShow: Bool = false var usage: (value: Double, description: String) = (0.0, "") private let loadInfoCount: mach_msg_type_number_t! @@ -49,22 +48,17 @@ public class CPU { self.usage = (value, description) } - public func isShowUsage() -> Bool { - if isShow == false { return false } - return true - } - func processCPU(_ statusBar: StatusBar) -> Bool { cpuTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true, block: { _ in self.usageCPU() statusBar.interval = 0.02 * (100 - max(0.0, min(99.0, self.usage.value))) / 6 - statusBar.statusItem.button?.title = self.isShowUsage() ? self.usage.description : "" + statusBar.statusItem.button?.title = StateManager.shared.getIsShowCPUUsage() ? self.usage.description : "" }) self.cpuTimer?.fire() return true } - public func StopCPU() -> Bool { + func StopCPU() -> Bool { self.cpuTimer?.invalidate() return false } diff --git a/Box42/Preferences/Icon.swift b/Box42/Preferences/Icon.swift index faca984..d256436 100644 --- a/Box42/Preferences/Icon.swift +++ b/Box42/Preferences/Icon.swift @@ -5,4 +5,15 @@ // Created by Chanhee Kim on 7/8/23. // -import Foundation +struct iconModel { + var icon: [String] = [ + "Cat", + "gam", + "gon", + "gun", + "lee", + "Box", + "box_oc", + "42" + ] +} diff --git a/Box42/Resources/AppDelegate.swift b/Box42/Resources/AppDelegate.swift index 5eaba15..358a56c 100644 --- a/Box42/Resources/AppDelegate.swift +++ b/Box42/Resources/AppDelegate.swift @@ -10,7 +10,7 @@ import Cocoa @main class AppDelegate: NSObject, NSApplicationDelegate { var menubarController = MenubarViewController() - + func applicationWillFinishLaunching(_ notification: Notification) { menubarController.menubarViewControllerInit() } diff --git a/Box42/Resources/Box42.entitlements b/Box42/Resources/Box42.entitlements index 40b639e..aa078cb 100644 --- a/Box42/Resources/Box42.entitlements +++ b/Box42/Resources/Box42.entitlements @@ -4,6 +4,8 @@ com.apple.security.app-sandbox + com.apple.security.automation.apple-events + com.apple.security.files.user-selected.read-only com.apple.security.network.client diff --git a/Box42/Resources/Info.plist b/Box42/Resources/Info.plist index a6a98a7..6529386 100644 --- a/Box42/Resources/Info.plist +++ b/Box42/Resources/Info.plist @@ -28,5 +28,26 @@ Main NSPrincipalClass NSApplication + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + + UISceneDelegateClassName + + UISceneConfigurationName + + UISceneStoryboardFile + + + + + diff --git a/Box42/Shared/StateManager.swift b/Box42/Shared/StateManager.swift index e544e11..c5a0fec 100644 --- a/Box42/Shared/StateManager.swift +++ b/Box42/Shared/StateManager.swift @@ -8,9 +8,37 @@ class StateManager { static let shared = StateManager() - var isPin: Bool! + private var isPin: Bool! + private var isShowCPUUsage: Bool! + private var isShowWindow: Bool! private init() { isPin = false + isShowCPUUsage = false + isShowWindow = false + } + + func getIsPin() -> Bool { + return isPin + } + + func setToggleIsPin() { + isPin.toggle() + } + + func getIsShowCPUUsage() -> Bool { + return isShowCPUUsage + } + + func setToggleIsShowCPUUsage() { + isShowCPUUsage.toggle() + } + + func getToggleIsShowWindow() -> Bool { + return isShowWindow + } + + func setToggleIsShowWindow() { + isShowWindow.toggle() } } From 1be7520b127e9200188513a4163f53f7d371ad54 Mon Sep 17 00:00:00 2001 From: chanhihi Date: Fri, 11 Aug 2023 04:07:45 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20mvc=EA=B5=AC=EC=A1=B0=EB=A5=BC?= =?UTF-8?q?=20mvvm=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=95=B4=EA=B0=80=EB=8A=94=20=EA=B3=BC=EC=A0=95=EC=97=90=20?= =?UTF-8?q?=EC=9E=88=EC=8A=B5=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Box42/Box/BoxButtonHandler.swift | 16 ++ Box42/Box/BoxModel.swift | 18 +- Box42/Box/BoxView.swift | 27 --- Box42/Box/BoxViewController.swift | 262 +++++++------------------ Box42/Box/BoxViewModel.swift | 29 +++ Box42/Box/View/BoxButtonView.swift | 123 ++++++++++++ Box42/Box/View/BoxView.swift | 101 ++++++++++ Box42/WebView/WebViewController.swift | 9 +- Box42/Window/BoxWindowController.swift | 26 +++ 9 files changed, 387 insertions(+), 224 deletions(-) create mode 100644 Box42/Box/BoxButtonHandler.swift delete mode 100644 Box42/Box/BoxView.swift create mode 100644 Box42/Box/BoxViewModel.swift create mode 100644 Box42/Box/View/BoxButtonView.swift create mode 100644 Box42/Box/View/BoxView.swift create mode 100644 Box42/Window/BoxWindowController.swift diff --git a/Box42/Box/BoxButtonHandler.swift b/Box42/Box/BoxButtonHandler.swift new file mode 100644 index 0000000..f79d076 --- /dev/null +++ b/Box42/Box/BoxButtonHandler.swift @@ -0,0 +1,16 @@ +// +// BoxButtonHandler.swift +// Box42 +// +// Created by Chanhee Kim on 8/11/23. +// + +import Cocoa + +class BoxButtonHandler { + var clickAction: ((NSButton) -> Void)? + + @objc func buttonClicked(_ sender: NSButton) { + clickAction?(sender) + } +} diff --git a/Box42/Box/BoxModel.swift b/Box42/Box/BoxModel.swift index d6f114c..686ced2 100644 --- a/Box42/Box/BoxModel.swift +++ b/Box42/Box/BoxModel.swift @@ -2,11 +2,21 @@ // BoxViewModel.swift // Box42 // -// Created by Chan on 2023/03/16. +// Created by Chan on 2023/08/11. // -import WebKit +import Cocoa -public class BoxStatus { - var isPin: Bool = false +struct BoxViewSize { + var halfSize: (width: CGFloat, height: CGFloat)! + var size: (width: CGFloat, height: CGFloat)! + var buttonGroupSize: (width: CGFloat, height: CGFloat)! + var viewStack: [NSView?] + + init() { + halfSize = (NSScreen.halfOfScreen.x, NSScreen.halfOfScreen.y) + size = (NSScreen.customScreenSize.x, NSScreen.customScreenSize.y) + buttonGroupSize = (CGFloat(132), NSScreen.customScreenSize.y) + viewStack = [NSView()] + } } diff --git a/Box42/Box/BoxView.swift b/Box42/Box/BoxView.swift deleted file mode 100644 index 9e5ba10..0000000 --- a/Box42/Box/BoxView.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// BoxView.swift -// Box42 -// -// Created by Chan on 2023/03/17. -// - -import Cocoa - -class BoxView: NSView { - -} - -struct BoxViewSize { - var halfSize: (width: CGFloat, height: CGFloat) = (NSScreen.halfOfScreen.x, NSScreen.halfOfScreen.y) - var size: (width: CGFloat, height: CGFloat) = (NSScreen.customScreenSize.x, NSScreen.customScreenSize.y) - var buttonGroupSize: (width: CGFloat, height: CGFloat) = (CGFloat(132), NSScreen.customScreenSize.y) - var viewStack = [NSView()] -} - -extension NSScreen { - static let screenSize = NSScreen.main?.visibleFrame.size - static let screenWidth = screenSize!.width - static let screenHeight = screenSize!.height - static let halfOfScreen = (x: screenWidth / 2, y: screenHeight / 2) - static let customScreenSize = (x: CGFloat(900), y: screenHeight - 132) -} diff --git a/Box42/Box/BoxViewController.swift b/Box42/Box/BoxViewController.swift index 9d19ef9..784840c 100644 --- a/Box42/Box/BoxViewController.swift +++ b/Box42/Box/BoxViewController.swift @@ -10,175 +10,28 @@ import AppKit import WebKit class BoxViewController: NSViewController, WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate { - var ad = NSApplication.shared.delegate as? AppDelegate - let url = URLModel() + var boxView: BoxButtonView! + var boxVM: BoxViewModel! var wvc = WebViewController() let preferencesVC = PreferencesViewController() - var topAnchorDistance: CGFloat = 0 - - @IBOutlet var divider: NSBox! - @IBOutlet weak var boxView: NSView! - @IBOutlet weak var buttonViewGroup: NSView! - @IBOutlet weak var hostingViewGroup: NSView! - @IBOutlet var pinSwitch: NSSwitch! - private var webView: WKWebView! - private var buttonBoxGroup: NSView! - override func keyDown(with event: NSEvent) { - print(event.keyCode) - if event.keyCode == 53 { // Escape 키의 keyCode는 53입니다. - print("escape") - } else { - super.keyDown(with: event) // 기타 키를 처리하기 위해 상위 클래스에게 전달 - } - - if (event.modifierFlags.contains(.command) && event.charactersIgnoringModifiers == "c") || - (event.modifierFlags.contains(.command) && event.charactersIgnoringModifiers == "ㅊ") { - // 복사 동작 처리 - webView.evaluateJavaScript("document.execCommand('copy')") { (_, error) in - if let error = error { - print("Copy error: \(error)") - } else { - print("Copy success") - } - } - } else if (event.modifierFlags.contains(.command) && event.charactersIgnoringModifiers == "v") || - (event.modifierFlags.contains(.command) && event.charactersIgnoringModifiers == "ㅍ") { - // 붙여넣기 동작 처리 - let pasteboard = NSPasteboard.general - if let pasteString = pasteboard.string(forType: .string) { - let escapedPasteString = pasteString.escapedForJavaScript - webView.evaluateJavaScript("document.execCommand('insertText', false, '\(escapedPasteString)')") { (_, error) in - if let error = error { - print("Paste error: \(error)") - } else { - print("Paste success") - } - } - } - } else { - super.keyDown(with: event) // 기본 키 이벤트 처리 - } + override func loadView() { + boxView = BoxButtonView() + boxVM = BoxViewModel() } override func viewDidLoad() { - view.window?.makeFirstResponder(self) super.viewDidLoad() - buttonBoxGroupInit() - boxViewSizeInit() + view.window?.styleMask.insert(.resizable) + view.window?.makeFirstResponder(self) + webViewInit() wvc.loadWebViewInit() configureButton() } - - func createButton(_ title :String) { - let button = NSButton() - - button.title = title - button.setButtonType(.momentaryLight) - - button.translatesAutoresizingMaskIntoConstraints = false - button.action = #selector(self.clickBtn(sender:)) - - button.isBordered = true - button.bezelStyle = .roundRect - button.showsBorderOnlyWhileMouseInside = true - - buttonBoxGroup.addSubview(button) - - button.leadingAnchor.constraint(equalTo: buttonBoxGroup.leadingAnchor, constant: 20).isActive = true - button.topAnchor.constraint(equalTo: buttonBoxGroup.topAnchor, constant: topAnchorDistance).isActive = true - topAnchorDistance += 30 - button.trailingAnchor.constraint(equalTo: buttonBoxGroup.trailingAnchor, constant: -20).isActive = true - } - - func createHomeButton() { - let button = NSButton(title: "home", image: NSImage(imageLiteralResourceName: "42box_logo"), target: (Any).self, action: #selector(self.clickBtn(sender:))) - button.translatesAutoresizingMaskIntoConstraints = false - button.isBordered = false - button.imagePosition = .imageOnly - - buttonBoxGroup.addSubview(button) - - button.leadingAnchor.constraint(equalTo: buttonBoxGroup.leadingAnchor, constant: 20).isActive = true - button.topAnchor.constraint(equalTo: buttonBoxGroup.topAnchor, constant: topAnchorDistance).isActive = true - topAnchorDistance += 80 - button.trailingAnchor.constraint(equalTo: buttonBoxGroup.trailingAnchor, constant: -20).isActive = true - } - - func createQuitButton() { - let button = NSButton() - button.title = "Quit Box" - button.setButtonType(.momentaryLight) - - button.translatesAutoresizingMaskIntoConstraints = false - button.action = #selector(NSApplication.terminate(_:)) - button.isBordered = true - button.bezelStyle = .roundRect - button.showsBorderOnlyWhileMouseInside = true - - buttonBoxGroup.addSubview(button) - - button.leadingAnchor.constraint(equalTo: buttonBoxGroup.leadingAnchor, constant: 20).isActive = true - button.bottomAnchor.constraint(equalTo: buttonBoxGroup.bottomAnchor, constant: -10).isActive = true - button.trailingAnchor.constraint(equalTo: buttonBoxGroup.trailingAnchor, constant: -20).isActive = true - } - - func divide() { - divider.translatesAutoresizingMaskIntoConstraints = false - divider.bottomAnchor.constraint(equalTo: buttonBoxGroup.bottomAnchor, constant: -40).isActive = true - } - - func createPinButton() { - let button = NSButton() - button.title = "Pin Box" - button.setButtonType(.toggle) - button.contentTintColor = .orange - button.translatesAutoresizingMaskIntoConstraints = false - button.action = #selector(self.pin) - button.isBordered = true - button.bezelStyle = .roundRect - button.showsBorderOnlyWhileMouseInside = true - - buttonBoxGroup.addSubview(button) - - button.leadingAnchor.constraint(equalTo: buttonBoxGroup.leadingAnchor, constant: 20).isActive = true - button.bottomAnchor.constraint(equalTo: buttonBoxGroup.bottomAnchor, constant: -50).isActive = true - button.trailingAnchor.constraint(equalTo: buttonBoxGroup.trailingAnchor, constant: -20).isActive = true - } - - func preferencesButton() { - let button = NSButton() - button.title = "Preferences" - button.setButtonType(.momentaryLight) - - button.translatesAutoresizingMaskIntoConstraints = false - button.action = #selector(self.clickBtn(sender:)) - button.isBordered = true - button.bezelStyle = .roundRect - button.showsBorderOnlyWhileMouseInside = true - - buttonBoxGroup.addSubview(button) - - button.leadingAnchor.constraint(equalTo: buttonBoxGroup.leadingAnchor, constant: 20).isActive = true - button.bottomAnchor.constraint(equalTo: buttonBoxGroup.bottomAnchor, constant: -90).isActive = true - button.trailingAnchor.constraint(equalTo: buttonBoxGroup.trailingAnchor, constant: -20).isActive = true - } - - func configureButton() { - createHomeButton() - for i in 1.. 2 { - let rqURL = URLRequest(url: URLModel().URLdict[sender.title]!) + let rqURL = URLRequest(url: boxVM.URLdict[sender.title]!) WebViewList.shared.list[sender.title]!.load(rqURL) print("Triple Click") } else if clickCount < 2 { - hostingViewGroup.subviews.removeAll() - hostingViewGroup.addSubview(WebViewList.shared.list[sender.title]!) + boxView.hostingViewGroup.subviews.removeAll() + boxView.hostingViewGroup.addSubview(WebViewList.shared.list[sender.title]!) WebViewList.shared.list[sender.title]!.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true WebViewList.shared.list[sender.title]!.configuration.preferences.javaScriptEnabled = true WebViewList.shared.list[sender.title]?.viewDidMoveToSuperview() - setAutoLayout(from: WebViewList.shared.list[sender.title]!, to: hostingViewGroup) + setAutoLayout(from: WebViewList.shared.list[sender.title]!, to: boxView.hostingViewGroup) } } @objc func pin(_ sender: NSSwitch) { - StateManager.shared.isPin.toggle() + StateManager.shared.setToggleIsPin() print(sender.state) } - func boxViewSizeInit() { - boxView.frame.size.width = BoxViewSize().size.width - boxView.frame.size.height = BoxViewSize().size.height - - hostingViewGroup.frame.size.width = BoxViewSize().size.width - BoxViewSize().buttonGroupSize.width - hostingViewGroup.frame.size.height = BoxViewSize().size.height - - buttonViewGroup.frame.size.width = BoxViewSize().buttonGroupSize.width - buttonViewGroup.frame.size.height = BoxViewSize().buttonGroupSize.height - } - - func buttonBoxGroupInit() { - buttonBoxGroup = NSView(frame: NSRect(x: 0, y: 0, width: BoxViewSize().buttonGroupSize.width, height: BoxViewSize().buttonGroupSize.height)) - buttonBoxGroup.frame.size.width = BoxViewSize().buttonGroupSize.width - buttonBoxGroup.frame.size.height = BoxViewSize().buttonGroupSize.height - buttonViewGroup.addSubview(buttonBoxGroup) - - setAutoLayout(from: buttonBoxGroup, to: buttonViewGroup) - } - func webViewInit() { + boxVM.setUpURLdict() webView = wvc.addWebView() - hostingViewGroup.addSubview(webView) - setAutoLayout(from: webView, to: hostingViewGroup) - let request = URLRequest(url: url.URLdict["home"]!) + boxView.hostingViewGroup.addSubview(webView) + setAutoLayout(from: webView, to: boxView.hostingViewGroup) + let request = URLRequest(url: boxVM.URLdict["home"]!) DispatchQueue.main.async { self.webView.load(request) } @@ -260,16 +94,66 @@ class BoxViewController: NSViewController, WKScriptMessageHandler, WKUIDelegate, func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { print(message.name) } + + func configureButton() { + boxView.createHomeButton(clickAction: clickBtn) + for (name, _) in boxVM.webViewURL.URLstring { + boxView.createButton(name, clickAction: clickBtn) + } + boxView.divide() + boxView.preferencesButton(clickAction: clickBtn) + boxView.createQuitButton() + boxView.createPinButton(clickAction: clickBtn) + } } extension BoxViewController { static func freshController() -> BoxViewController { let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil) - let identifier = NSStoryboard.SceneIdentifier("BoxController") - + let identifier = NSStoryboard.SceneIdentifier("Main") + guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? BoxViewController else { fatalError("Story Board Not Found") } return viewcontroller } } + +extension BoxViewController { + override func keyDown(with event: NSEvent) { + print(event.keyCode) + if event.keyCode == 53 { // Escape 키의 keyCode는 53입니다. + print("escape") + } else { + super.keyDown(with: event) // 기타 키를 처리하기 위해 상위 클래스에게 전달 + } + + if (event.modifierFlags.contains(.command) && event.charactersIgnoringModifiers == "c") || + (event.modifierFlags.contains(.command) && event.charactersIgnoringModifiers == "ㅊ") { + // 복사 동작 처리 + webView.evaluateJavaScript("document.execCommand('copy')") { (_, error) in + if let error = error { + print("Copy error: \(error)") + } else { + print("Copy success") + } + } + } else if (event.modifierFlags.contains(.command) && event.charactersIgnoringModifiers == "v") || + (event.modifierFlags.contains(.command) && event.charactersIgnoringModifiers == "ㅍ") { + // 붙여넣기 동작 처리 + let pasteboard = NSPasteboard.general + if let pasteString = pasteboard.string(forType: .string) { + let escapedPasteString = pasteString.escapedForJavaScript + webView.evaluateJavaScript("document.execCommand('insertText', false, '\(escapedPasteString)')") { (_, error) in + if let error = error { + print("Paste error: \(error)") + } else { + print("Paste success") + } + } + } + } else { + super.keyDown(with: event) // 기본 키 이벤트 처리 + } + } +} diff --git a/Box42/Box/BoxViewModel.swift b/Box42/Box/BoxViewModel.swift new file mode 100644 index 0000000..3fbd9ff --- /dev/null +++ b/Box42/Box/BoxViewModel.swift @@ -0,0 +1,29 @@ +// +// URLViewModel.swift +// Box42 +// +// Created by Chanhee Kim on 8/9/23. +// + +import Foundation +import Combine + +// CRUD 4가지 형태의 데이터 가공 create, read, update, delete +class BoxViewModel: ObservableObject { + var webViewURL: URLModels + @Published var URLdict: [String: URL] + + init() { + self.webViewURL = URLModels(info: [URLModel(name: "home", url: "https://42box.github.io/front-end/")]) + self.URLdict = [String: URL]() + } + + func setUpURLdict() { +// for urlModel in webViewURL.info { +// URLdict[urlModel.name] = URL(string: urlModel.url) +// } + for urlModel in webViewURL.URLstring { + URLdict[urlModel.0] = URL(string: urlModel.1) + } + } +} diff --git a/Box42/Box/View/BoxButtonView.swift b/Box42/Box/View/BoxButtonView.swift new file mode 100644 index 0000000..37a9a6f --- /dev/null +++ b/Box42/Box/View/BoxButtonView.swift @@ -0,0 +1,123 @@ +// +// BoxButtonView.swift +// Box42 +// +// Created by Chanhee Kim on 8/11/23. +// + +import Cocoa + +class BoxButtonView: BoxView { + var topAnchorDistance: CGFloat = 0 + + func createButton(_ title: String, clickAction: @escaping (NSButton) -> Void) { + let button = NSButton() + + button.title = title + button.setButtonType(.momentaryLight) + + button.translatesAutoresizingMaskIntoConstraints = false + + let handler = BoxButtonHandler() + handler.clickAction = clickAction + + button.target = handler + button.action = #selector(handler.buttonClicked(_:)) + + button.isBordered = true + button.bezelStyle = .roundRect + button.showsBorderOnlyWhileMouseInside = true + + super.buttonBoxGroup.addSubview(button) + + button.leadingAnchor.constraint(equalTo: super.buttonBoxGroup.leadingAnchor, constant: 20).isActive = true + button.topAnchor.constraint(equalTo: super.buttonBoxGroup.topAnchor, constant: topAnchorDistance).isActive = true + topAnchorDistance += 30 + button.trailingAnchor.constraint(equalTo: super.buttonBoxGroup.trailingAnchor, constant: -20).isActive = true + } + + func createHomeButton(clickAction: @escaping (NSButton) -> Void) { + let handler = BoxButtonHandler() + handler.clickAction = clickAction + + let button = NSButton(title: "home", image: NSImage(imageLiteralResourceName: "42box_logo"), target: handler, action: #selector(handler.buttonClicked(_:))) + + button.translatesAutoresizingMaskIntoConstraints = false + button.isBordered = false + button.imagePosition = .imageOnly + + super.buttonBoxGroup.addSubview(button) + + button.leadingAnchor.constraint(equalTo: super.buttonBoxGroup.leadingAnchor, constant: 20).isActive = true + button.topAnchor.constraint(equalTo: super.buttonBoxGroup.topAnchor, constant: topAnchorDistance).isActive = true + topAnchorDistance += 80 + button.trailingAnchor.constraint(equalTo: super.buttonBoxGroup.trailingAnchor, constant: -20).isActive = true + } + + func createQuitButton() { + let button = NSButton() + button.title = "Quit Box" + button.setButtonType(.momentaryLight) + + button.translatesAutoresizingMaskIntoConstraints = false + button.action = #selector(NSApplication.terminate(_:)) + button.isBordered = true + button.bezelStyle = .roundRect + button.showsBorderOnlyWhileMouseInside = true + + super.buttonBoxGroup.addSubview(button) + + button.leadingAnchor.constraint(equalTo: super.buttonBoxGroup.leadingAnchor, constant: 20).isActive = true + button.bottomAnchor.constraint(equalTo: super.buttonBoxGroup.bottomAnchor, constant: -10).isActive = true + button.trailingAnchor.constraint(equalTo: super.buttonBoxGroup.trailingAnchor, constant: -20).isActive = true + } + + func createPinButton(clickAction: @escaping (NSButton) -> Void) { + let button = NSButton() + button.title = "Pin Box" + button.setButtonType(.toggle) + button.contentTintColor = .orange + button.translatesAutoresizingMaskIntoConstraints = false + + let handler = BoxButtonHandler() + handler.clickAction = clickAction + + button.target = handler + button.action = #selector(handler.buttonClicked(_:)) + + button.isBordered = true + button.bezelStyle = .roundRect + button.showsBorderOnlyWhileMouseInside = true + + super.buttonBoxGroup.addSubview(button) + + button.leadingAnchor.constraint(equalTo: super.buttonBoxGroup.leadingAnchor, constant: 20).isActive = true + button.bottomAnchor.constraint(equalTo: super.buttonBoxGroup.bottomAnchor, constant: -50).isActive = true + button.trailingAnchor.constraint(equalTo: super.buttonBoxGroup.trailingAnchor, constant: -20).isActive = true + } + + func preferencesButton(clickAction: @escaping (NSButton) -> Void) { + let button = NSButton() + button.title = "Preferences" + button.setButtonType(.momentaryLight) + + button.translatesAutoresizingMaskIntoConstraints = false + + let handler = BoxButtonHandler() + handler.clickAction = clickAction + + button.target = handler + button.action = #selector(handler.buttonClicked(_:)) + + button.isBordered = true + button.bezelStyle = .roundRect + button.showsBorderOnlyWhileMouseInside = true + + super.buttonBoxGroup.addSubview(button) + + button.leadingAnchor.constraint(equalTo: super.buttonBoxGroup.leadingAnchor, constant: 20).isActive = true + button.bottomAnchor.constraint(equalTo: super.buttonBoxGroup.bottomAnchor, constant: -90).isActive = true + button.trailingAnchor.constraint(equalTo: super.buttonBoxGroup.trailingAnchor, constant: -20).isActive = true + } + +} diff --git a/Box42/Box/View/BoxView.swift b/Box42/Box/View/BoxView.swift new file mode 100644 index 0000000..c20ca54 --- /dev/null +++ b/Box42/Box/View/BoxView.swift @@ -0,0 +1,101 @@ +// +// BoxView.swift +// Box42 +// +// Created by Chan on 2023/03/17. +// + +import Cocoa + +class BoxView: NSView { + var buttonBoxGroup : NSView! + var buttonViewGroup : NSView! + var hostingViewGroup : NSView! + var divider : NSBox! + var pinSwitch : NSSwitch! + var boxModel = BoxViewSize() + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + commonInit() + viewInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + viewInit() + } + + private func commonInit() { + buttonBoxGroup = NSView() + buttonViewGroup = NSView() + hostingViewGroup = NSView() + divider = NSBox() + pinSwitch = NSSwitch() + + // Pan Gesture Recognizer를 추가합니다. + let panRecognizer = NSPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) + addGestureRecognizer(panRecognizer) + } + + func viewInit() { + self.boxViewSizeInit() + self.buttonBoxGroupInit() + self.divide() + } + + func boxViewSizeInit() { + self.frame.size.width = boxModel.size.width + self.frame.size.height = boxModel.size.height + + hostingViewGroup.frame.size.width = boxModel.size.width - boxModel.buttonGroupSize.width + hostingViewGroup.frame.size.height = boxModel.size.height + + buttonViewGroup.frame.size.width = boxModel.buttonGroupSize.width + buttonViewGroup.frame.size.height = boxModel.buttonGroupSize.height + } + + func divide() { + divider.translatesAutoresizingMaskIntoConstraints = false + divider.bottomAnchor.constraint(equalTo: buttonBoxGroup.bottomAnchor, constant: -40).isActive = true + } + + func buttonBoxGroupInit() { + self.buttonBoxGroup = NSView(frame: NSRect(x: 0, y: 0, width: boxModel.buttonGroupSize.width, height: boxModel.buttonGroupSize.height)) + self.buttonBoxGroup.frame.size.width = boxModel.buttonGroupSize.width + self.buttonBoxGroup.frame.size.height = boxModel.buttonGroupSize.height + self.buttonViewGroup.addSubview(self.buttonBoxGroup) + + setAutoLayout(from: self.buttonBoxGroup, to: self.buttonViewGroup) + } + + func setAutoLayout(from: NSView, to: NSView) { + from.translatesAutoresizingMaskIntoConstraints = false + to.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.init(item: from, attribute: .leading, relatedBy: .equal, toItem: to, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true + NSLayoutConstraint.init(item: from, attribute: .trailing, relatedBy: .equal, toItem: to, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true + NSLayoutConstraint.init(item: from, attribute: .top, relatedBy: .equal, toItem: to, attribute: .top, multiplier: 1.0, constant: 0).isActive = true + NSLayoutConstraint.init(item: from, attribute: .bottom, relatedBy: .equal, toItem: to, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true + self.layout() + } +} + +extension BoxView { + @objc private func handlePanGesture(_ recognizer: NSPanGestureRecognizer) { + guard let view = recognizer.view else { return } + + // 사용자가 드래그한 변화량을 가져옵니다. + let translation = recognizer.translation(in: view) + + // 드래그로 인한 크기 변화를 계산합니다. + let newWidth = view.frame.width + translation.x + let newHeight = view.frame.height + translation.y + + // 크기를 적용합니다. + view.setFrameSize(NSSize(width: newWidth, height: newHeight)) + + // 변화량을 리셋합니다. + recognizer.setTranslation(.zero, in: view) + } +} diff --git a/Box42/WebView/WebViewController.swift b/Box42/WebView/WebViewController.swift index 13909a4..984f888 100644 --- a/Box42/WebView/WebViewController.swift +++ b/Box42/WebView/WebViewController.swift @@ -9,12 +9,13 @@ import Cocoa import WebKit class WebViewController: NSViewController, WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate { - + var URLVM = BoxViewModel() + func loadWebViewInit() { - for i in 0.. Date: Fri, 11 Aug 2023 04:09:21 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20xib=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Box42/Window/BoxWindowController.xib | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Box42/Window/BoxWindowController.xib diff --git a/Box42/Window/BoxWindowController.xib b/Box42/Window/BoxWindowController.xib new file mode 100644 index 0000000..30df79a --- /dev/null +++ b/Box42/Window/BoxWindowController.xib @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +