diff --git a/Box42.xcodeproj/project.pbxproj b/Box42.xcodeproj/project.pbxproj index 229e011..24e1b4d 100644 --- a/Box42.xcodeproj/project.pbxproj +++ b/Box42.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 64D699FE2AA5F69900EEF7BC /* BookmarkModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D699FD2AA5F69900EEF7BC /* BookmarkModel.swift */; }; + 64D69A012AA5F97B00EEF7BC /* BookmarkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D699F72AA5F5C100EEF7BC /* BookmarkViewModel.swift */; }; + 7E9B46922AA5C564009EB900 /* BookmarkEditorTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E9B46912AA5C564009EB900 /* BookmarkEditorTableView.swift */; }; DE018BB32A5099F900FF0AA3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE018BB22A5099F900FF0AA3 /* AppDelegate.swift */; }; DE018BB82A5099F900FF0AA3 /* Box42.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DE018BB62A5099F900FF0AA3 /* Box42.xcdatamodeld */; }; DE018BDD2A509AEB00FF0AA3 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE018BDC2A509AEB00FF0AA3 /* EventMonitor.swift */; }; @@ -58,7 +61,7 @@ DE4408052A923EC00091937A /* QuitButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4408042A923EC00091937A /* QuitButtonView.swift */; }; DE4408082A9240300091937A /* BoxFunctionButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4408072A9240300091937A /* BoxFunctionButtonView.swift */; }; DE44080C2A924B520091937A /* BoxFunctionViewGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE44080B2A924B520091937A /* BoxFunctionViewGroup.swift */; }; - DE4408152A92750D0091937A /* keyDown+BoxBaseContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4408142A92750D0091937A /* keyDown+BoxBaseContainerViewController.swift */; }; + DE4408152A92750D0091937A /* keyDown+Appdelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE4408142A92750D0091937A /* keyDown+Appdelegate.swift */; }; DE44081D2A928F760091937A /* Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE44081C2A928F760091937A /* Divider.swift */; }; DE62BE5A2A9BA31700D97E06 /* QuickSlotButtonCollectionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DE62BE582A9BA31700D97E06 /* QuickSlotButtonCollectionViewController.xib */; }; DE62BE672A9BA92E00D97E06 /* QuickSlotButtonViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE7F9D432A9B7A4700F8ACAE /* QuickSlotButtonViewItem.swift */; }; @@ -114,16 +117,37 @@ DE98E83B2A98DB6000F8744A /* RotateImage+NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE98E83A2A98DB6000F8744A /* RotateImage+NSImage.swift */; }; DE98E8432A98DDFD00F8744A /* QuickSlotViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE98E8422A98DDFD00F8744A /* QuickSlotViewController.swift */; }; DE98E8552A98EA7900F8744A /* WindowButtonUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE98E8542A98EA7900F8744A /* WindowButtonUI.swift */; }; + DE9B57562AA62723007B796C /* BookmarkTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B57552AA62723007B796C /* BookmarkTableView.swift */; }; + DE9B575A2AA629E4007B796C /* DraggableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B57592AA629E4007B796C /* DraggableButton.swift */; }; + DE9B575E2AA62A54007B796C /* ButtonTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B575D2AA62A54007B796C /* ButtonTableCellView.swift */; }; + DE9B57672AA65B87007B796C /* PutUserMeUrlList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B57662AA65B87007B796C /* PutUserMeUrlList.swift */; }; + DE9B576C2AA6647F007B796C /* BookmarkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B576B2AA6647F007B796C /* BookmarkCell.swift */; }; + DE9B576F2AA664D1007B796C /* BookmarkCellManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B576E2AA664D1007B796C /* BookmarkCellManager.swift */; }; + DE9B57782AA66DCD007B796C /* BookmarkUpdateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B57772AA66DCD007B796C /* BookmarkUpdateButton.swift */; }; + DE9B577C2AA66DEA007B796C /* BookmarkDeleteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B577B2AA66DEA007B796C /* BookmarkDeleteButton.swift */; }; + DE9B577F2AA66DF9007B796C /* BookmarkCreateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9B577E2AA66DF9007B796C /* BookmarkCreateButton.swift */; }; DE9DA8142A97F20E001C0D3B /* ButtonGroupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9DA8132A97F20E001C0D3B /* ButtonGroupViewController.swift */; }; DEB862D42A85124500278FCD /* cleanCache.sh in Resources */ = {isa = PBXBuildFile; fileRef = DEB862D32A85124500278FCD /* cleanCache.sh */; }; DEB862D92A852C4500278FCD /* brewInGoinfre.sh in Resources */ = {isa = PBXBuildFile; fileRef = DEB862D82A852C4500278FCD /* brewInGoinfre.sh */; }; DEB862EB2A853F7F00278FCD /* BoxWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEB862E92A853F7F00278FCD /* BoxWindowController.swift */; }; DEE0FA962A9A554F00085A65 /* FunctionButtonUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEE0FA952A9A554F00085A65 /* FunctionButtonUI.swift */; }; DEF0761B2AA33671005700E5 /* DeleteUserMeScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF0761A2AA33671005700E5 /* DeleteUserMeScript.swift */; }; + DEF076262AA3B0C1005700E5 /* QuickSlotItemButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF076252AA3B0C1005700E5 /* QuickSlotItemButton.swift */; }; + DEF0762A2AA3B955005700E5 /* PutUserMeQuickSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF076292AA3B955005700E5 /* PutUserMeQuickSlot.swift */; }; + DEF0762D2AA3C34C005700E5 /* GetUserMeQuickSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF0762C2AA3C34C005700E5 /* GetUserMeQuickSlot.swift */; }; + DEF076302AA3CF8A005700E5 /* QuickSlotItemLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF0762F2AA3CF8A005700E5 /* QuickSlotItemLabel.swift */; }; + DEF076362AA47FDB005700E5 /* QuickSlotManagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF076352AA47FDB005700E5 /* QuickSlotManagerViewController.swift */; }; + DEF0763A2AA480C6005700E5 /* QuickSlotTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF076392AA480C6005700E5 /* QuickSlotTableView.swift */; }; + DEF0763D2AA48125005700E5 /* QuickSlotCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF0763C2AA48125005700E5 /* QuickSlotCell.swift */; }; + DEF076402AA4845E005700E5 /* QuickSlotCellManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF0763F2AA4845E005700E5 /* QuickSlotCellManager.swift */; }; + DEF076432AA48AF0005700E5 /* QuickSlotCellDeleteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF076422AA48AF0005700E5 /* QuickSlotCellDeleteButton.swift */; }; DEF749322A85657600D987C8 /* NSScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEF749312A85657600D987C8 /* NSScreen.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 64D699F72AA5F5C100EEF7BC /* BookmarkViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkViewModel.swift; sourceTree = ""; }; + 64D699FD2AA5F69900EEF7BC /* BookmarkModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkModel.swift; sourceTree = ""; }; + 7E9B46912AA5C564009EB900 /* BookmarkEditorTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkEditorTableView.swift; sourceTree = ""; }; DE018BAF2A5099F900FF0AA3 /* Box42.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Box42.app; sourceTree = BUILT_PRODUCTS_DIR; }; DE018BB22A5099F900FF0AA3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; DE018BB72A5099F900FF0AA3 /* Box42.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Box42.xcdatamodel; sourceTree = ""; }; @@ -154,7 +178,7 @@ DE1F1A112A8B506600A88DD8 /* importMacOSInfo.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = importMacOSInfo.sh; sourceTree = ""; }; DE1F1A122A8B506600A88DD8 /* exportMacOSInfo.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = exportMacOSInfo.sh; sourceTree = ""; }; DE1F1A132A8B506600A88DD8 /* keyMapping.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = keyMapping.sh; sourceTree = ""; }; - DE1F1A192A8B50C500A88DD8 /* BoxBaseContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BoxBaseContainerViewController.swift; path = Main/BoxBaseContainerViewController.swift; sourceTree = ""; }; + DE1F1A192A8B50C500A88DD8 /* BoxBaseContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoxBaseContainerViewController.swift; sourceTree = ""; }; DE1F1A1A2A8B50C500A88DD8 /* BoxContentsViewGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoxContentsViewGroup.swift; sourceTree = ""; }; DE1F1A1B2A8B50C500A88DD8 /* BoxButtonViewGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoxButtonViewGroup.swift; sourceTree = ""; }; DE1F1A282A8B50E200A88DD8 /* BoxSizeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoxSizeManager.swift; sourceTree = ""; }; @@ -178,7 +202,7 @@ DE4408042A923EC00091937A /* QuitButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuitButtonView.swift; sourceTree = ""; }; DE4408072A9240300091937A /* BoxFunctionButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxFunctionButtonView.swift; sourceTree = ""; }; DE44080B2A924B520091937A /* BoxFunctionViewGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxFunctionViewGroup.swift; sourceTree = ""; }; - DE4408142A92750D0091937A /* keyDown+BoxBaseContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "keyDown+BoxBaseContainerViewController.swift"; path = "Main/keyDown+BoxBaseContainerViewController.swift"; sourceTree = ""; }; + DE4408142A92750D0091937A /* keyDown+Appdelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "keyDown+Appdelegate.swift"; sourceTree = ""; }; DE44081C2A928F760091937A /* Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Divider.swift; sourceTree = ""; }; DE62BE312A9B869900D97E06 /* QuickSlotButtonCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotButtonCollectionViewController.swift; sourceTree = ""; }; DE62BE582A9BA31700D97E06 /* QuickSlotButtonCollectionViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QuickSlotButtonCollectionViewController.xib; sourceTree = ""; }; @@ -233,12 +257,30 @@ DE98E83A2A98DB6000F8744A /* RotateImage+NSImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RotateImage+NSImage.swift"; sourceTree = ""; }; DE98E8422A98DDFD00F8744A /* QuickSlotViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotViewController.swift; sourceTree = ""; }; DE98E8542A98EA7900F8744A /* WindowButtonUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowButtonUI.swift; sourceTree = ""; }; + DE9B57552AA62723007B796C /* BookmarkTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkTableView.swift; sourceTree = ""; }; + DE9B57592AA629E4007B796C /* DraggableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableButton.swift; sourceTree = ""; }; + DE9B575D2AA62A54007B796C /* ButtonTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTableCellView.swift; sourceTree = ""; }; + DE9B57662AA65B87007B796C /* PutUserMeUrlList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PutUserMeUrlList.swift; sourceTree = ""; }; + DE9B576B2AA6647F007B796C /* BookmarkCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkCell.swift; sourceTree = ""; }; + DE9B576E2AA664D1007B796C /* BookmarkCellManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkCellManager.swift; sourceTree = ""; }; + DE9B57772AA66DCD007B796C /* BookmarkUpdateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkUpdateButton.swift; sourceTree = ""; }; + DE9B577B2AA66DEA007B796C /* BookmarkDeleteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkDeleteButton.swift; sourceTree = ""; }; + DE9B577E2AA66DF9007B796C /* BookmarkCreateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkCreateButton.swift; sourceTree = ""; }; DE9DA8132A97F20E001C0D3B /* ButtonGroupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonGroupViewController.swift; sourceTree = ""; }; DEB862D32A85124500278FCD /* cleanCache.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = cleanCache.sh; sourceTree = ""; }; DEB862D82A852C4500278FCD /* brewInGoinfre.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = brewInGoinfre.sh; sourceTree = ""; }; DEB862E92A853F7F00278FCD /* BoxWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxWindowController.swift; sourceTree = ""; }; DEE0FA952A9A554F00085A65 /* FunctionButtonUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionButtonUI.swift; sourceTree = ""; }; DEF0761A2AA33671005700E5 /* DeleteUserMeScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteUserMeScript.swift; sourceTree = ""; }; + DEF076252AA3B0C1005700E5 /* QuickSlotItemButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotItemButton.swift; sourceTree = ""; }; + DEF076292AA3B955005700E5 /* PutUserMeQuickSlot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PutUserMeQuickSlot.swift; sourceTree = ""; }; + DEF0762C2AA3C34C005700E5 /* GetUserMeQuickSlot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetUserMeQuickSlot.swift; sourceTree = ""; }; + DEF0762F2AA3CF8A005700E5 /* QuickSlotItemLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotItemLabel.swift; sourceTree = ""; }; + DEF076352AA47FDB005700E5 /* QuickSlotManagerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotManagerViewController.swift; sourceTree = ""; }; + DEF076392AA480C6005700E5 /* QuickSlotTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotTableView.swift; sourceTree = ""; }; + DEF0763C2AA48125005700E5 /* QuickSlotCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotCell.swift; sourceTree = ""; }; + DEF0763F2AA4845E005700E5 /* QuickSlotCellManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotCellManager.swift; sourceTree = ""; }; + DEF076422AA48AF0005700E5 /* QuickSlotCellDeleteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSlotCellDeleteButton.swift; sourceTree = ""; }; DEF7492E2A85603700D987C8 /* nodeInstall.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = nodeInstall.sh; sourceTree = ""; }; DEF749312A85657600D987C8 /* NSScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSScreen.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -255,6 +297,32 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 64D699F62AA5F5AA00EEF7BC /* Bookmark */ = { + isa = PBXGroup; + children = ( + 64D699FF2AA5F69D00EEF7BC /* Model */, + 64D69A002AA5F6A100EEF7BC /* ViewModel */, + DE9B57542AA62704007B796C /* View */, + ); + path = Bookmark; + sourceTree = ""; + }; + 64D699FF2AA5F69D00EEF7BC /* Model */ = { + isa = PBXGroup; + children = ( + 64D699FD2AA5F69900EEF7BC /* BookmarkModel.swift */, + ); + path = Model; + sourceTree = ""; + }; + 64D69A002AA5F6A100EEF7BC /* ViewModel */ = { + isa = PBXGroup; + children = ( + 64D699F72AA5F5C100EEF7BC /* BookmarkViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; DE018BA62A5099F900FF0AA3 = { isa = PBXGroup; children = ( @@ -281,6 +349,7 @@ DE018C0E2A509C0C00FF0AA3 /* Menubar */, DE9DA8122A97F1E2001C0D3B /* ButtonGroup */, DE1F1A202A8B50CA00A88DD8 /* Main */, + 64D699F62AA5F5AA00EEF7BC /* Bookmark */, DE874F512A591EC600FC3B77 /* Preferences */, DEB862D22A8511D600278FCD /* Scripts */, DE018C0C2A509BDF00FF0AA3 /* Resources */, @@ -386,10 +455,9 @@ DE0A916B2A8E7DC700D1D6F1 /* UI */, DE4408202A9297EE0091937A /* View */, DE1F1A192A8B50C500A88DD8 /* BoxBaseContainerViewController.swift */, - DE4408142A92750D0091937A /* keyDown+BoxBaseContainerViewController.swift */, DEB862E92A853F7F00278FCD /* BoxWindowController.swift */, ); - name = Main; + path = Main; sourceTree = ""; }; DE3FF36F2A978A6E009C88EF /* View */ = { @@ -464,6 +532,9 @@ DE9457272A9F6E4400B0B768 /* GetUserMeScripts.swift */, DE94572B2A9F75D500B0B768 /* API.swift */, DEF0761A2AA33671005700E5 /* DeleteUserMeScript.swift */, + DEF076292AA3B955005700E5 /* PutUserMeQuickSlot.swift */, + DEF0762C2AA3C34C005700E5 /* GetUserMeQuickSlot.swift */, + DE9B57662AA65B87007B796C /* PutUserMeUrlList.swift */, ); path = API; sourceTree = ""; @@ -552,6 +623,8 @@ children = ( DE7F9D432A9B7A4700F8ACAE /* QuickSlotButtonViewItem.swift */, DE7F9D442A9B7A4700F8ACAE /* QuickSlotButtonViewItem.xib */, + DEF076252AA3B0C1005700E5 /* QuickSlotItemButton.swift */, + DEF0762F2AA3CF8A005700E5 /* QuickSlotItemLabel.swift */, ); path = "Vertical Item"; sourceTree = ""; @@ -646,6 +719,7 @@ DE98E8412A98DDEB00F8744A /* View */ = { isa = PBXGroup; children = ( + DEF076382AA480B7005700E5 /* Table */, DE44081C2A928F760091937A /* Divider.swift */, DE97CA782A9A6F6A001073DE /* QuickSlotHeaderView.swift */, DE97CA7B2A9A7199001073DE /* QuickSlotGroupView.swift */, @@ -679,6 +753,38 @@ path = Model; sourceTree = ""; }; + DE9B57542AA62704007B796C /* View */ = { + isa = PBXGroup; + children = ( + DE9B57732AA666D3007B796C /* Button */, + DE9B57742AA666DD007B796C /* Editor */, + DE9B57552AA62723007B796C /* BookmarkTableView.swift */, + DE9B57592AA629E4007B796C /* DraggableButton.swift */, + DE9B575D2AA62A54007B796C /* ButtonTableCellView.swift */, + ); + path = View; + sourceTree = ""; + }; + DE9B57732AA666D3007B796C /* Button */ = { + isa = PBXGroup; + children = ( + DE9B57772AA66DCD007B796C /* BookmarkUpdateButton.swift */, + DE9B577B2AA66DEA007B796C /* BookmarkDeleteButton.swift */, + DE9B577E2AA66DF9007B796C /* BookmarkCreateButton.swift */, + ); + path = Button; + sourceTree = ""; + }; + DE9B57742AA666DD007B796C /* Editor */ = { + isa = PBXGroup; + children = ( + 7E9B46912AA5C564009EB900 /* BookmarkEditorTableView.swift */, + DE9B576B2AA6647F007B796C /* BookmarkCell.swift */, + DE9B576E2AA664D1007B796C /* BookmarkCellManager.swift */, + ); + path = Editor; + sourceTree = ""; + }; DE9DA8122A97F1E2001C0D3B /* ButtonGroup */ = { isa = PBXGroup; children = ( @@ -727,6 +833,7 @@ children = ( DE98E8422A98DDFD00F8744A /* QuickSlotViewController.swift */, DE6332F12A9BCA2C00DCFAF6 /* QuickSlotScriptsLogicController.swift */, + DEF076352AA47FDB005700E5 /* QuickSlotManagerViewController.swift */, ); path = Controller; sourceTree = ""; @@ -739,6 +846,17 @@ path = Model; sourceTree = ""; }; + DEF076382AA480B7005700E5 /* Table */ = { + isa = PBXGroup; + children = ( + DEF076392AA480C6005700E5 /* QuickSlotTableView.swift */, + DEF0763C2AA48125005700E5 /* QuickSlotCell.swift */, + DEF076422AA48AF0005700E5 /* QuickSlotCellDeleteButton.swift */, + DEF0763F2AA4845E005700E5 /* QuickSlotCellManager.swift */, + ); + path = Table; + sourceTree = ""; + }; DEF749302A85655E00D987C8 /* Extensions */ = { isa = PBXGroup; children = ( @@ -750,6 +868,7 @@ DE98E83A2A98DB6000F8744A /* RotateImage+NSImage.swift */, DE97CA682A9A6364001073DE /* PixelConversion+CGFloat.swift */, DE94571B2A9EFB7800B0B768 /* Associated+NSButton.swift */, + DE4408142A92750D0091937A /* keyDown+Appdelegate.swift */, ); path = Extensions; sourceTree = ""; @@ -842,9 +961,12 @@ DE018BB82A5099F900FF0AA3 /* Box42.xcdatamodeld in Sources */, DEF0761B2AA33671005700E5 /* DeleteUserMeScript.swift in Sources */, DE62BE672A9BA92E00D97E06 /* QuickSlotButtonViewItem.swift in Sources */, + DEF076302AA3CF8A005700E5 /* QuickSlotItemLabel.swift in Sources */, DE874F542A591F1400FC3B77 /* PreferencesView.swift in Sources */, DE98E83B2A98DB6000F8744A /* RotateImage+NSImage.swift in Sources */, + DE9B575E2AA62A54007B796C /* ButtonTableCellView.swift in Sources */, DE0A91982A8F977F00D1D6F1 /* ToolbarViewController.swift in Sources */, + DEF076432AA48AF0005700E5 /* QuickSlotCellDeleteButton.swift in Sources */, DE9DA8142A97F20E001C0D3B /* ButtonGroupViewController.swift in Sources */, DE4408082A9240300091937A /* BoxFunctionButtonView.swift in Sources */, DE3FF3742A978AB8009C88EF /* WindowMaximizeButton.swift in Sources */, @@ -866,18 +988,25 @@ DE77BBF02A9E38C6006CC98B /* GetUserProfile.swift in Sources */, DEF749322A85657600D987C8 /* NSScreen.swift in Sources */, DE018BF02A509B2F00FF0AA3 /* MenubarViewController.swift in Sources */, + DEF0763A2AA480C6005700E5 /* QuickSlotTableView.swift in Sources */, + DEF076362AA47FDB005700E5 /* QuickSlotManagerViewController.swift in Sources */, DE94571C2A9EFB7800B0B768 /* Associated+NSButton.swift in Sources */, DE6332E42A9BB8F800DCFAF6 /* QuickSlotButtonCollectionViewController.swift in Sources */, DE9457062A9E69C100B0B768 /* ScriptNameLabel.swift in Sources */, DE77BA512A82580400713683 /* MenubarViewModel.swift in Sources */, + DE9B577F2AA66DF9007B796C /* BookmarkCreateButton.swift in Sources */, DE44080C2A924B520091937A /* BoxFunctionViewGroup.swift in Sources */, DE97CA7C2A9A7199001073DE /* QuickSlotGroupView.swift in Sources */, + DEF0762A2AA3B955005700E5 /* PutUserMeQuickSlot.swift in Sources */, DE018BE42A509B1700FF0AA3 /* CPU.swift in Sources */, DE77BBA22A9DDC40006CC98B /* ScriptsFileManager.swift in Sources */, DE874F5F2A5935CC00FC3B77 /* String.swift in Sources */, DE0A91862A8F889F00D1D6F1 /* RefreshPageViaToolbar.swift in Sources */, + DE9B577C2AA66DEA007B796C /* BookmarkDeleteButton.swift in Sources */, + DE9B575A2AA629E4007B796C /* DraggableButton.swift in Sources */, DE874F4E2A591DEA00FC3B77 /* Hotkey.swift in Sources */, DE77BBA62A9DDF2B006CC98B /* WebView.swift in Sources */, + 64D69A012AA5F97B00EEF7BC /* BookmarkViewModel.swift in Sources */, DE0A91832A8F889000D1D6F1 /* GoHomePageViaToolbar().swift in Sources */, DE6332F22A9BCA2C00DCFAF6 /* QuickSlotScriptsLogicController.swift in Sources */, DE9457512AA0BE0F00B0B768 /* NotificationSettingView.swift in Sources */, @@ -885,7 +1014,9 @@ DE018BB32A5099F900FF0AA3 /* AppDelegate.swift in Sources */, DE78860C2A9C770300FE21DD /* ScriptsViewModel.swift in Sources */, DE0A91632A8E6A5400D1D6F1 /* Constants.swift in Sources */, + 7E9B46922AA5C564009EB900 /* BookmarkEditorTableView.swift in Sources */, DE7886172A9CCB3B00FE21DD /* UserProfile.swift in Sources */, + DE9B57782AA66DCD007B796C /* BookmarkUpdateButton.swift in Sources */, DE0A91902A8F88CA00D1D6F1 /* DisplayURLInToolbar.swift in Sources */, DE77BBCD2A9E0568006CC98B /* ExecuteScripts.swift in Sources */, DE9456F82A9E44FD00B0B768 /* IconController.swift in Sources */, @@ -906,22 +1037,28 @@ DE3FF3772A978AB8009C88EF /* WindowMinimizeButton.swift in Sources */, DE9457542AA0BF5200B0B768 /* ShortcutSettingView.swift in Sources */, DE4408022A923EB60091937A /* PinButtonView.swift in Sources */, + DE9B57672AA65B87007B796C /* PutUserMeUrlList.swift in Sources */, DE77BBE22A9E0F70006CC98B /* Scripts.swift in Sources */, DE78862D2A9D1ADE00FE21DD /* PreferencesCell.swift in Sources */, + DE9B576C2AA6647F007B796C /* BookmarkCell.swift in Sources */, DE9457572AA0C5C600B0B768 /* IconSettingView.swift in Sources */, DE0A91672A8E6CA700D1D6F1 /* WebViewManager.swift in Sources */, DE77BBF32A9E38DC006CC98B /* UserManager.swift in Sources */, + DEF076402AA4845E005700E5 /* QuickSlotCellManager.swift in Sources */, DE94570C2A9E69EB00B0B768 /* ScriptExcuteButton.swift in Sources */, DE9457162A9E6D3000B0B768 /* ScriptQuickSlotButton.swift in Sources */, DE97CA7F2A9A73A9001073DE /* QuickSlotUI.swift in Sources */, - DE4408152A92750D0091937A /* keyDown+BoxBaseContainerViewController.swift in Sources */, + DE4408152A92750D0091937A /* keyDown+Appdelegate.swift in Sources */, DE018BED2A509B2600FF0AA3 /* URLModel.swift in Sources */, DE97CA692A9A6364001073DE /* PixelConversion+CGFloat.swift in Sources */, DE7886282A9D186700FE21DD /* ScriptsViewController.swift in Sources */, + DEF0763D2AA48125005700E5 /* QuickSlotCell.swift in Sources */, + DE9B57562AA62723007B796C /* BookmarkTableView.swift in Sources */, DE4408052A923EC00091937A /* QuitButtonView.swift in Sources */, DE0A918A2A8F88A900D1D6F1 /* GoForwardInToolbar.swift in Sources */, DE1F1A1E2A8B50C500A88DD8 /* BoxButtonViewGroup.swift in Sources */, DEB862EB2A853F7F00278FCD /* BoxWindowController.swift in Sources */, + 64D699FE2AA5F69900EEF7BC /* BookmarkModel.swift in Sources */, DE0A918D2A8F88BC00D1D6F1 /* GoBackInToolbar.swift in Sources */, DE94574E2AA0B56200B0B768 /* CPUView.swift in Sources */, DE018BDD2A509AEB00FF0AA3 /* EventMonitor.swift in Sources */, @@ -934,10 +1071,13 @@ DE24E6382A8FE10400E29F5D /* BoxBaseSplitView.swift in Sources */, DE44081D2A928F760091937A /* Divider.swift in Sources */, DE98E8432A98DDFD00F8744A /* QuickSlotViewController.swift in Sources */, + DEF0762D2AA3C34C005700E5 /* GetUserMeQuickSlot.swift in Sources */, DE94574B2AA0A70500B0B768 /* NetworkView.swift in Sources */, + DE9B576F2AA664D1007B796C /* BookmarkCellManager.swift in Sources */, DE1F1A2E2A8BCC9800A88DD8 /* Storage.swift in Sources */, DE3FF36B2A978A57009C88EF /* WindowButtonViewController.swift in Sources */, DE1F1A312A8BD68F00A88DD8 /* Double.swift in Sources */, + DEF076262AA3B0C1005700E5 /* QuickSlotItemButton.swift in Sources */, DE0A917F2A8F865400D1D6F1 /* BoxToolbarViewGroup.swift in Sources */, DE018BEA2A509B2100FF0AA3 /* WebViewModel.swift in Sources */, ); diff --git a/Box42.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Box42.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 19d4e8c..0000000 --- a/Box42.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,16 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "SnapKit", - "repositoryURL": "https://github.com/SnapKit/SnapKit.git", - "state": { - "branch": null, - "revision": "f222cbdf325885926566172f6f5f06af95473158", - "version": "5.6.0" - } - } - ] - }, - "version": 1 -} diff --git a/Box42.xcodeproj/project.xcworkspace/xcuserdata/daskim.xcuserdatad/UserInterfaceState.xcuserstate b/Box42.xcodeproj/project.xcworkspace/xcuserdata/daskim.xcuserdatad/UserInterfaceState.xcuserstate index 8f50922..d3fbbb4 100644 Binary files a/Box42.xcodeproj/project.xcworkspace/xcuserdata/daskim.xcuserdatad/UserInterfaceState.xcuserstate and b/Box42.xcodeproj/project.xcworkspace/xcuserdata/daskim.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Box42/Bookmark/Model/BookmarkModel.swift b/Box42/Bookmark/Model/BookmarkModel.swift new file mode 100644 index 0000000..0f8bdd5 --- /dev/null +++ b/Box42/Bookmark/Model/BookmarkModel.swift @@ -0,0 +1,21 @@ +// +// BookmarkModel.swift +// Box42 +// +// Created by Chan on 2023/09/04. +// + +struct URLList: Codable { + let urlList: [URLItem] +} + +struct URLItem: Codable { + let name: String + let url: String +} + +extension URLItem: Equatable { + static func ==(lhs: URLItem, rhs: URLItem) -> Bool { + return lhs.name == rhs.name && lhs.url == rhs.url + } +} diff --git a/Box42/Bookmark/View/BookmarkTableView.swift b/Box42/Bookmark/View/BookmarkTableView.swift new file mode 100644 index 0000000..7cdad6a --- /dev/null +++ b/Box42/Bookmark/View/BookmarkTableView.swift @@ -0,0 +1,196 @@ +//// +//// BookmarkTableView.swift +//// Box42 +//// +//// Created by Chanhee Kim on 9/4/23. +//// +// +//import AppKit +//import SnapKit +//import Combine +// +//class BookmarkTableView: NSTableView { +// var onButtonClicked: ((DraggableButton) -> Void)? +// +// var buttonTitleArray: [String] { +// return BookmarkViewModel.shared.bookMarkList.map { $0.name } +// } +// +// var urlArray: [String] { +// return BookmarkViewModel.shared.bookMarkList.map { $0.url } +// } +// +// var viewModel: BookmarkViewModel? { +// didSet { +// print("ViewModel has been set.") +// setupBindings() +// } +// } +// +// var cancellables: Set = [] +// +// private func setupBindings() { +// print("Setting up bindings...") // 디버깅 로그 +// viewModel?.$bookMarkList.sink(receiveValue: { [weak self] newScripts in +// print("Received new scripts: \(newScripts)") // 디버깅 로그 +// DispatchQueue.main.async { +// self?.reloadData() +// } +// }).store(in: &cancellables) +// } +// +// func setup() { +// self.delegate = self +// self.dataSource = self +// self.registerForDraggedTypes([NSPasteboard.PasteboardType.string]) +// +// self.wantsLayer = true +// self.backgroundColor = NSColor(hex: "#E7E7E7") +// self.focusRingType = .none +// self.headerView = nil +// self.autoresizingMask = [.width, .height] +// self.selectionHighlightStyle = .none +// self.intercellSpacing = NSSize(width: 0, height: 0) +// self.setDraggingSourceOperationMask(.move, forLocal: true) +// +// let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Bookmark")) +// column.title = "" +// // column.width = 100 +// column.resizingMask = .autoresizingMask +// +// self.addTableColumn(column) +// } +//} +// +//extension BookmarkTableView: NSTableViewDelegate, NSTableViewDataSource { +// +// func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation { +// if dropOperation == .above { +// return .move +// } else { +// return [] +// } +// } +// +// func tableView(_ aTableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool { +// guard let str = info.draggingPasteboard.string(forType: .string), let from = Int(str) else { +// return false +// } +// +// let to = (from < row) ? row - 1 : row +// let item = BookmarkViewModel.shared.bookMarkList[from] +// BookmarkViewModel.shared.bookMarkList.remove(at: from) +// BookmarkViewModel.shared.bookMarkList.insert(item, at: to) +// self.reloadData() +// +// for (_, subview) in self.subviews.enumerated() { +// guard let cellView = subview as? CustomTableCellView else { +// continue +// } +// +// cellView.button.title = buttonTitleArray[cellView.rowIndex] +// } +// +// return true +// } +// +// func numberOfRows(in tableView: NSTableView) -> Int { +// return BookmarkViewModel.shared.bookMarkList.count +// } +// +// func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { +// let cellView = ButtonTableCellView() +// cellView.rowIndex = row +// +// let button = DraggableButton(frame: NSRect(x: 0, y: 0, width: 300, height: 44)) +// button.tag = row +// button.bezelStyle = .inline +// button.isBordered = false +// button.title = "" +// button.registerForDraggedTypes([NSPasteboard.PasteboardType.string]) +// button.target = self +// button.action = #selector(buttonClicked(_:)) +// button.delegate = self +// +// let label = NSTextField(frame: NSRect(x: 26 + 21 + 8, y: 25 / 2, width: button.bounds.width, height: button.bounds.height)) +// +// label.stringValue = buttonTitleArray[row] +// label.backgroundColor = .clear +// label.isBordered = false +// label.isEditable = false +// +// let attributes : [NSAttributedString.Key : Any] = +// [ +// NSAttributedString.Key.font : NSFont.systemFont(ofSize:18.0, weight: .light), +// NSAttributedString.Key.foregroundColor : NSColor.black, +// ] +// let attributedStringTitle = NSAttributedString(string: label.stringValue , attributes: +// attributes) +// label.attributedStringValue=attributedStringTitle +// button.addSubview(label) +// +// +// // let image = NSImage(named: NSImage.Name("bookmark-default")) +// // image?.size = NSSize(width: 21, height: 21) +// // button.image = image +// // button.imagePosition = .imageLeading +// // button.image?.alignmentRect = NSRect(x: 0, y: 0, width: 21, height: 21) +// +// let imageView = NSImageView(frame: NSRect(x: 26, y: 25 / 2, width: 21, height: 21)) +// imageView.image = NSImage(named: NSImage.Name("bookmark-default")) +// imageView.imageScaling = .scaleProportionallyUpOrDown +// imageView.imageAlignment = .alignCenter +// button.addSubview(imageView) +// +// +// +// cellView.addSubview(button) +// +// button.snp.makeConstraints { make in +// make.top.equalToSuperview().offset(2) +// make.leading.equalToSuperview() +// make.trailing.equalToSuperview() +// // make.width.equalTo(268) +// make.width.lessThanOrEqualTo(268) +// make.height.equalTo(44) +// } +// +// tableView.rowHeight = 50 +// +// if row == selectedRow { +// button.wantsLayer = true +// button.layer?.cornerRadius = 12 +// button.layer?.backgroundColor = NSColor.white.cgColor +// } else { +// button.wantsLayer = true +// button.layer?.cornerRadius = 12 +// button.layer?.backgroundColor = NSColor.clear.cgColor +// } +// +// return cellView +// } +// +// func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? { +// let pasteboardItem = NSPasteboardItem() +// pasteboardItem.setString(String(row), forType: .string) +// return pasteboardItem +// } +// +// func sendUpdatedDataToServer() { +// let urlList = zip(buttonTitleArray, urlArray).map { ["name": $0.0, "url": $0.1] } +// let jsonData = try? JSONSerialization.data(withJSONObject: ["urlList": urlList]) +// +// var request = URLRequest(url: URL(string:"https://api.42box.kr/user-service/users/me/url-list")!) +// request.httpMethod = "POST" +// request.httpBody = jsonData +// +// URLSession.shared.dataTask(with:request) { (data, response, error) in +// if error != nil{ +// print(error!.localizedDescription) +// } +// else{ +// print("Data posted successfully") +// } +// }.resume() +// } +//} diff --git a/Box42/Bookmark/View/Button/BookmarkCreateButton.swift b/Box42/Bookmark/View/Button/BookmarkCreateButton.swift new file mode 100644 index 0000000..2c246de --- /dev/null +++ b/Box42/Bookmark/View/Button/BookmarkCreateButton.swift @@ -0,0 +1,68 @@ +// +// BookmarkCreateButton.swift +// Box42 +// +// Created by Chanhee Kim on 9/5/23. +// + +import AppKit + +class BookmarkCreateButton: NSButton { + + init() { + super.init(frame: NSRect(x: 0, y: 0, width: 70, height: 40)) + + self.title = "북마크 추가" + self.isBordered = false + self.wantsLayer = true + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + + let trackingArea = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .activeAlways], owner: self, userInfo: nil) + self.addTrackingArea(trackingArea) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func mouseEntered(with event: NSEvent) { + super.mouseEntered(with: event) + + let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") + bgColorAnimation.fromValue = WindowButtonUI.color.opacityWhite + bgColorAnimation.toValue = WindowButtonUI.color.maximize + bgColorAnimation.duration = WindowButtonUI.animation.duration + + let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") + cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius + cornerAnimation.toValue = WindowButtonUI.size.cornerRadius / 2 + cornerAnimation.duration = WindowButtonUI.animation.duration + + self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") + self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") + + self.layer?.backgroundColor = WindowButtonUI.color.maximize + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius / 2 + } + + override func mouseExited(with event: NSEvent) { + super.mouseExited(with: event) + + let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") + bgColorAnimation.fromValue = WindowButtonUI.color.maximize + bgColorAnimation.toValue = WindowButtonUI.color.opacityWhite + bgColorAnimation.duration = WindowButtonUI.animation.duration + + let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") + cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius / 2 + cornerAnimation.toValue = WindowButtonUI.size.cornerRadius + cornerAnimation.duration = WindowButtonUI.animation.duration + + self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") + self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") + + self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + } +} diff --git a/Box42/Bookmark/View/Button/BookmarkDeleteButton.swift b/Box42/Bookmark/View/Button/BookmarkDeleteButton.swift new file mode 100644 index 0000000..9d94da9 --- /dev/null +++ b/Box42/Bookmark/View/Button/BookmarkDeleteButton.swift @@ -0,0 +1,68 @@ +// +// BookmarkDeleteButton.swift +// Box42 +// +// Created by Chanhee Kim on 9/5/23. +// + +import AppKit + +class BookmarkDeleteButton: NSButton { + + init() { + super.init(frame: NSRect(x: 0, y: 0, width: 53, height: 40)) + + self.title = "삭제" + self.isBordered = false + self.wantsLayer = true + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + + let trackingArea = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .activeAlways], owner: self, userInfo: nil) + self.addTrackingArea(trackingArea) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func mouseEntered(with event: NSEvent) { + super.mouseEntered(with: event) + + let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") + bgColorAnimation.fromValue = WindowButtonUI.color.opacityWhite + bgColorAnimation.toValue = WindowButtonUI.color.close + bgColorAnimation.duration = WindowButtonUI.animation.duration + + let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") + cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius + cornerAnimation.toValue = WindowButtonUI.size.cornerRadius / 2 + cornerAnimation.duration = WindowButtonUI.animation.duration + + self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") + self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") + + self.layer?.backgroundColor = WindowButtonUI.color.close + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius / 2 + } + + override func mouseExited(with event: NSEvent) { + super.mouseExited(with: event) + + let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") + bgColorAnimation.fromValue = WindowButtonUI.color.close + bgColorAnimation.toValue = WindowButtonUI.color.opacityWhite + bgColorAnimation.duration = WindowButtonUI.animation.duration + + let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") + cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius / 2 + cornerAnimation.toValue = WindowButtonUI.size.cornerRadius + cornerAnimation.duration = WindowButtonUI.animation.duration + + self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") + self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") + + self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + } +} diff --git a/Box42/Bookmark/View/Button/BookmarkUpdateButton.swift b/Box42/Bookmark/View/Button/BookmarkUpdateButton.swift new file mode 100644 index 0000000..f621e96 --- /dev/null +++ b/Box42/Bookmark/View/Button/BookmarkUpdateButton.swift @@ -0,0 +1,94 @@ +// +// BookmarkUpdateButton.swift +// Box42 +// +// Created by Chanhee Kim on 9/5/23. +// + +import AppKit + +class BookmarkUpdateButton: NSButton { + + init() { + super.init(frame: NSRect(x: 0, y: 0, width: 53, height: 40)) + + self.title = "변경하기" + self.isBordered = false + self.wantsLayer = true + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + + let trackingArea = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .activeAlways], owner: self, userInfo: nil) + self.addTrackingArea(trackingArea) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func mouseEntered(with event: NSEvent) { + super.mouseEntered(with: event) + + let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") + bgColorAnimation.fromValue = WindowButtonUI.color.opacityWhite + bgColorAnimation.toValue = WindowButtonUI.color.minimize + bgColorAnimation.duration = WindowButtonUI.animation.duration + + let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") + cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius + cornerAnimation.toValue = WindowButtonUI.size.cornerRadius / 2 + cornerAnimation.duration = WindowButtonUI.animation.duration + + self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") + self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") + + self.layer?.backgroundColor = WindowButtonUI.color.minimize + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius / 2 + } + + override func mouseExited(with event: NSEvent) { + super.mouseExited(with: event) + + let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") + bgColorAnimation.fromValue = WindowButtonUI.color.minimize + bgColorAnimation.toValue = WindowButtonUI.color.opacityWhite + bgColorAnimation.duration = WindowButtonUI.animation.duration + + let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") + cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius / 2 + cornerAnimation.toValue = WindowButtonUI.size.cornerRadius + cornerAnimation.duration = WindowButtonUI.animation.duration + + self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") + self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") + + self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + } + + override func mouseDown(with event: NSEvent) { + super.mouseDown(with: event) + + // 윈도우의 크기와 위치 정보를 가져옴 + guard self.window != nil else { + return + } + if self.title == "퀵슬롯" { return } + // 현재 버튼의 위치를 윈도우 기준으로 변환 + let initialLocation = self.frame.origin + + // 윈도우의 왼쪽 아래 모서리를 최종 목표 위치로 설정 + let finalLocation = NSPoint(x: 0, y: 0) + + // 애니메이션 블록 + NSAnimationContext.runAnimationGroup({ context in + context.duration = 1 // 애니메이션 지속 시간 + + // 애니메이션 적용 + self.animator().setFrameOrigin(finalLocation) + }, completionHandler: { + // 애니메이션 완료 후, 버튼을 원래 위치로 되돌림 + self.setFrameOrigin(initialLocation) + }) + } +} diff --git a/Box42/Bookmark/View/ButtonTableCellView.swift b/Box42/Bookmark/View/ButtonTableCellView.swift new file mode 100644 index 0000000..1a4b2e1 --- /dev/null +++ b/Box42/Bookmark/View/ButtonTableCellView.swift @@ -0,0 +1,21 @@ +//// +//// ButtonTableCellView.swift +//// Box42 +//// +//// Created by Chanhee Kim on 9/5/23. +//// +// +//import AppKit +// +//class ButtonTableCellView: NSTableCellView { +// var button: NSButton! +// var deleteButton: NSButton! +// var rowIndex: Int! +// +// override func viewWillDraw() { +// super.viewWillDraw() +// self.frame.size.width = 268.0 +// self.frame = NSRect(x: self.frame.origin.x - 3, y: self.frame.origin.y, +// width: self.frame.size.width, height: self.frame.size.height) +// } +//} diff --git a/Box42/Bookmark/View/DraggableButton.swift b/Box42/Bookmark/View/DraggableButton.swift new file mode 100644 index 0000000..cf9b0ec --- /dev/null +++ b/Box42/Bookmark/View/DraggableButton.swift @@ -0,0 +1,105 @@ +// +// DraggableButton.swift +// Box42 +// +// Created by Chanhee Kim on 9/5/23. +// + +import AppKit + +class DraggableButton: NSButton, NSDraggingSource { + weak var delegate: BoxBaseContainerViewController? + var mouseDownEvent: NSEvent? + + func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor context: NSDraggingContext) -> NSDragOperation { + return .move + } + var initialMouseDownPoint: CGPoint? + + override func mouseDown(with event: NSEvent) { + print("mouseDown") + self.initialMouseDownPoint = event.locationInWindow + } + + override func mouseDragged(with event: NSEvent) { + guard let initialPoint = self.initialMouseDownPoint else { return } + + let distance = hypot( + initialPoint.x - event.locationInWindow.x, + initialPoint.y - event.locationInWindow.y) + + if distance > 3 { // 드래그로 판단하는 최소 거리. 필요에 따라 조절 가능합니다. + let pasteboardItem = NSPasteboardItem() + pasteboardItem.setString("\(self.tag)", forType: .string) + + let draggingItem = NSDraggingItem(pasteboardWriter: pasteboardItem) + + // Create a snapshot of the button + let snapshot = self.snapshot() + + // Set the dragging frame and contents + draggingItem.setDraggingFrame(self.bounds, contents: snapshot) + + beginDraggingSession(with: [draggingItem], event: event, source: self) + + self.initialMouseDownPoint = nil + } + } + + override func mouseUp(with event: NSEvent) { + guard let initialPoint = self.initialMouseDownPoint else { return } + + let distance = hypot( + initialPoint.x - event.locationInWindow.x, + initialPoint.y - event.locationInWindow.y) + + if distance < 3 { // 클릭으로 판단하는 최대 거리. 필요에 따라 조절 가능합니다. + self.target?.perform(self.action, with: self) + } + + self.initialMouseDownPoint = nil + } + + func snapshot() -> NSImage? { + guard let bitmapRep = bitmapImageRepForCachingDisplay(in: bounds) else { return nil } + cacheDisplay(in: bounds, to: bitmapRep) + let image = NSImage(size: bounds.size) + image.addRepresentation(bitmapRep) + return image + } + + override func viewDidMoveToWindow() { + super.viewDidMoveToWindow() + + let trackingArea = NSTrackingArea( + rect: bounds, + options: [.mouseEnteredAndExited, .activeAlways], + owner: self, + userInfo: nil + ) + + addTrackingArea(trackingArea) + } + + override func mouseEntered(with event: NSEvent) { + super.mouseEntered(with: event) + + if self != delegate?.selectedButton { + wantsLayer = true + layer?.frame.size = CGSize(width: 268.0, height: 44.0) + layer?.cornerRadius = 12 + layer?.backgroundColor = NSColor(red: 0.848, green: 0.848, blue: 0.848, alpha: 1).cgColor + } + } + + override func mouseExited(with event: NSEvent) { + super.mouseExited(with: event) + + if self != delegate?.selectedButton { + wantsLayer = true + layer?.frame.size = CGSize(width: 268.0, height: 44.0) + layer?.cornerRadius = 12 + layer?.backgroundColor = NSColor.clear.cgColor + } + } +} diff --git a/Box42/Bookmark/View/Editor/BookmarkCell.swift b/Box42/Bookmark/View/Editor/BookmarkCell.swift new file mode 100644 index 0000000..7e0fc27 --- /dev/null +++ b/Box42/Bookmark/View/Editor/BookmarkCell.swift @@ -0,0 +1,96 @@ +// +// BookmarkCell.swift +// Box42 +// +// Created by Chanhee Kim on 9/5/23. +// + +import AppKit +import SnapKit + +class BookmarkCell: NSTableCellView { + var nameLabel: NSTextField = NSTextField() + var descriptionLabel: NSTextField = NSTextField() + var deleteButton: BookmarkDeleteButton = BookmarkDeleteButton() + var quickSlotButton: BookmarkUpdateButton = BookmarkUpdateButton() + + var viewModel: BookmarkViewModel? + var urlitem: URLItem? + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupUI() { + addSubview(nameLabel) + addSubview(descriptionLabel) + addSubview(quickSlotButton) + addSubview(deleteButton) + + nameLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(16) + make.width.lessThanOrEqualTo(200).priority(.high) + } + + deleteButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-16) + make.width.equalTo(53) + make.height.equalTo(40) + } + + quickSlotButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalTo(deleteButton.snp.left).offset(-8) + make.width.equalTo(53) + make.height.equalTo(40) + } + + descriptionLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(nameLabel.snp.right).offset(8) + make.right.lessThanOrEqualTo(quickSlotButton.snp.left).offset(-8) + make.width.greaterThanOrEqualTo(100).priority(.low) // 최소 너비와 낮은 우선순위 설정 + } + } + + + + func configure(with urlitem: URLItem, viewModel: BookmarkViewModel?) { + self.urlitem = urlitem + self.viewModel = viewModel + nameLabel.stringValue = urlitem.name + descriptionLabel.stringValue = urlitem.url + + deleteButton.target = self + deleteButton.action = #selector(deleteButtonClicked) + + quickSlotButton.target = self + quickSlotButton.action = #selector(quickSlotButtonclicked) + + } + + @objc func deleteButtonClicked() { + if let deleteItem = urlitem { + BookmarkViewModel.shared.deleteBookmark(item: deleteItem) + } + } + + @objc func quickSlotButtonclicked() { + if let tableView = self.superview as? NSTableView { + let rowIndex = tableView.row(for: self) + print("현재 셀의 index: \(rowIndex)") + + if let updateItem = urlitem { + print(rowIndex, updateItem) +// BookmarkViewModel.shared.updateBookmark(rowIndex, item: updateItem) + } + } + } +} diff --git a/Box42/Bookmark/View/Editor/BookmarkCellManager.swift b/Box42/Bookmark/View/Editor/BookmarkCellManager.swift new file mode 100644 index 0000000..a312395 --- /dev/null +++ b/Box42/Bookmark/View/Editor/BookmarkCellManager.swift @@ -0,0 +1,67 @@ +// +// BookmarkCellManager.swift +// Box42 +// +// Created by Chanhee Kim on 9/5/23. +// + +import AppKit +import SnapKit + +class BookmarkCellManager: NSTableCellView { + var nameLabel: NSTextField = NSTextField() + var descriptionLabel: NSTextField = NSTextField() + var excuteButton: BookmarkCreateButton = BookmarkCreateButton() + + var viewModel: BookmarkViewModel? + var urlitem: URLItem? + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupUI() { + addSubview(nameLabel) + addSubview(descriptionLabel) + addSubview(excuteButton) + + nameLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(16) + make.width.lessThanOrEqualTo(200).priority(.high) // 최대 너비와 우선순위 설정 + } + + excuteButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-16) + make.width.equalTo(150) + make.height.equalTo(40) + } + + descriptionLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(nameLabel.snp.right).offset(8) + make.right.lessThanOrEqualTo(excuteButton.snp.left).offset(-8) + make.width.greaterThanOrEqualTo(100).priority(.low) // 최소 너비와 낮은 우선순위 설정 + } + } + + func configure(with urlitem: URLItem, viewModel: BookmarkViewModel?) { + self.urlitem = urlitem + self.viewModel = viewModel + nameLabel.stringValue = urlitem.name + descriptionLabel.stringValue = urlitem.url + + excuteButton.target = self + excuteButton.action = #selector(excuteButtonClicked) + } + + @objc func excuteButtonClicked() { + BookmarkViewModel.shared.addBookmark(item: URLItem(name: nameLabel.stringValue, url: descriptionLabel.stringValue)) + } +} diff --git a/Box42/Bookmark/View/Editor/BookmarkEditorTableView.swift b/Box42/Bookmark/View/Editor/BookmarkEditorTableView.swift new file mode 100644 index 0000000..e0db761 --- /dev/null +++ b/Box42/Bookmark/View/Editor/BookmarkEditorTableView.swift @@ -0,0 +1,75 @@ +// +// BookmarkEditorTableView.swift +// Box42 +// +// Created by Dasol on 2023/09/04. +// + +import AppKit +import SnapKit +import Combine + +class BookmarkEditorTableView: NSTableView { + var viewModel: BookmarkViewModel? = BookmarkViewModel.shared + var cancellables: Set = [] + + func setup() { + self.delegate = self + self.dataSource = self + + let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("BookmarkEditorTableView")) + column1.width = 100.0 + column1.title = "BookmarkEditorTableView" + self.addTableColumn(column1) + + self.wantsLayer = true + self.layer?.backgroundColor = NSColor.lightGray.cgColor + self.layer?.cornerRadius = 20 + } + + func setupBindings() { + viewModel?.$bookMarkList + .sink { [weak self] _ in + self?.reloadData() + } + .store(in: &cancellables) + } +} + +extension BookmarkEditorTableView: NSTableViewDelegate, NSTableViewDataSource { + func getCellForRow(at row: Int) -> NSView { + guard let viewModel = viewModel else { + return NSView() + } + + if row < viewModel.bookMarkList.count { + return getBookmarkCell(for: viewModel.bookMarkList[row], viewModel: viewModel) + } else { + // MARK: - 다음 버전에 추가 예정 + return getBookmarkCellManager() + } + } + + private func getBookmarkCell(for urlitem: URLItem, viewModel: BookmarkViewModel) -> BookmarkCell { + let cell = BookmarkCell(frame: .zero) + cell.configure(with: urlitem, viewModel: viewModel) + return cell + } + + private func getBookmarkCellManager() -> BookmarkCellManager { + let scriptCellManger = BookmarkCellManager(frame: .zero) + return scriptCellManger + } + + func numberOfRows(in tableView: NSTableView) -> Int { + return (viewModel?.bookMarkList.count ?? 0) + 1 + } + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + getCellForRow(at: row) + } + + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + return 44.0 + } +} diff --git a/Box42/Bookmark/ViewModel/BookmarkViewModel.swift b/Box42/Bookmark/ViewModel/BookmarkViewModel.swift new file mode 100644 index 0000000..b2c452e --- /dev/null +++ b/Box42/Bookmark/ViewModel/BookmarkViewModel.swift @@ -0,0 +1,105 @@ +// +// BookmarkViewModel.swift +// Box42 +// +// Created by Chan on 2023/09/04. +// + +import AppKit +import Combine + +class BookmarkViewModel: NSObject { + static let shared = BookmarkViewModel() + + @Published var bookMarkList: [URLItem] = [] + + private override init() { + self.bookMarkList = [URLItem(name: "42Box", url: "https://42box.kr/"), + URLItem(name: "23Coaltheme", url: "https://42box.github.io/front-end/"), + URLItem(name: "loopback", url: "http://127.0.0.1:3000/"), + URLItem(name: "Box 42", url: "https://42box.github.io/front-end/#/box"), + URLItem(name: "Intra 42", url: "https://intra.42.fr"), + URLItem(name: "Jiphyeonjeon", url: "https://42library.kr"), + URLItem(name: "42STAT", url: "https://stat.42seoul.kr/home"), + URLItem(name: "24Hane", url: "https://24hoursarenotenough.42seoul.kr"), + URLItem(name: "80kCoding", url: "https://80000coding.oopy.io"), + URLItem(name: "where42", url: "https://www.where42.kr"), + URLItem(name: "cabi", url: "https://cabi.42seoul.io/"), + URLItem(name: "42gg", url: "https://42gg.kr/")] + } + + // Create + func addBookmarkByFront(item: URLItem) { + bookMarkList.append(item) + loadWebView(item.name, item.url) + } + + // Create + func addBookmark(item: URLItem) { + bookMarkList.append(item) + loadWebView(item.name, item.url) + + let body = URLList(urlList: bookMarkList) + API.putUserMeUrlList(urlList: body) { result in + switch result { + case .success(_): + print("Successfully updated the scripts.") // 혹은 사용자에게 보여줄 알림 추가 + case .failure(let error): + print("Failed to update scripts: \(error.localizedDescription)") // 혹은 사용자에게 보여줄 알림 추가 + } + } + } + + func updateBookmark(index: Int, item: URLItem) { + WebViewManager.shared.list[item.name]?.navigationDelegate = nil + WebViewManager.shared.list[item.name]?.stopLoading() + WebViewManager.shared.list[item.name] = nil + bookMarkList[index] = item + loadWebView(item.name, item.url) + } + + // Delete + func deleteBookmark(item: URLItem) { + WebViewManager.shared.list[item.name]?.navigationDelegate = nil + WebViewManager.shared.list[item.name]?.stopLoading() + WebViewManager.shared.list[item.name] = nil + self.bookMarkList.removeAll(where: { $0 == item }) + } + + // 새로운 북마크 배열로 교체하는 메소드 + func replaceBookMarkList(with newBookMarkList: [URLItem]) { + DispatchQueue.main.async { + WebViewManager.shared.list.forEach { (key, webView) in + webView.navigationDelegate = nil + webView.stopLoading() + WebViewManager.shared.list[key] = nil + } + } + + newBookMarkList.forEach { (URLItem) in + loadWebView(URLItem.name, URLItem.url) + } + self.bookMarkList = newBookMarkList + } + + + func loadWebView(_ name: String, _ url: String) { + var url = url + if !url.hasPrefix("https://") && !url.hasPrefix("http://") { + url = "https://" + url + print(url) + } + if let loadURL = URL(string: url) { + DispatchQueue.main.async { + let wkWebView = WebView() + let request = URLRequest(url: loadURL) + + print(request) + WebViewManager.shared.list[name] = wkWebView + DispatchQueue.main.async { + wkWebView.load(request) + } + } + } + } +} diff --git a/Box42/Extensions/keyDown+Appdelegate.swift b/Box42/Extensions/keyDown+Appdelegate.swift new file mode 100644 index 0000000..75bf2e4 --- /dev/null +++ b/Box42/Extensions/keyDown+Appdelegate.swift @@ -0,0 +1,105 @@ +// +// keyDown+AppDelegate.swift +// Box42 +// +// Created by Chanhee Kim on 8/21/23. +// + +import AppKit + +extension AppDelegate { + func eventMonitoring() { + NSEvent.addLocalMonitorForEvents(matching: .keyUp) { (event) -> NSEvent? in + print("Key Up: \(event.keyCode)") + + if event.modifierFlags.contains(.control) { + switch event.keyCode { + case 12: // 'Q' 키의 keyCode + print("Control + Q pressed") + QuickSlotScriptsLogicController.shared.shortCutKeyUP(0) + case 13: // 'W' 키의 keyCode + print("Control + W pressed") + QuickSlotScriptsLogicController.shared.shortCutKeyUP(2) + case 14: // 'E' 키의 keyCode + print("Control + E pressed") + QuickSlotScriptsLogicController.shared.shortCutKeyUP(4) + case 15: // 'R' 키의 keyCode + print("Control + R pressed") + QuickSlotScriptsLogicController.shared.shortCutKeyUP(6) + case 0: // 'A' 키의 keyCode + print("Control + A pressed") + QuickSlotScriptsLogicController.shared.shortCutKeyUP(1) + case 1: // 'S' 키의 keyCode + print("Control + S pressed") + QuickSlotScriptsLogicController.shared.shortCutKeyUP(3) + case 2: // 'D' 키의 keyCode + print("Control + D pressed") + QuickSlotScriptsLogicController.shared.shortCutKeyUP(5) + case 3: // 'F' 키의 keyCode + print("Control + F pressed") + QuickSlotScriptsLogicController.shared.shortCutKeyUP(7) + case 6: // 'Z' 키의 keyCode + print("Control + Z pressed") + StateManager.shared.togglePin() // pin 처리 + case 7: // 'X' 키의 keyCode + print("Control + X pressed") + default: + break + } + } else if event.modifierFlags.contains(.command) { + switch event.keyCode { + case 12: // 'Q' 키의 keyCode + print("command + Q pressed") + case 13: // 'W' 키의 keyCode + print("command + W pressed") + case 14: // 'E' 키의 keyCode + print("command + E pressed") + case 15: // 'R' 키의 keyCode + print("command + R pressed") + WebViewManager.shared.hostingWebView?.reload() + case 0: // 'A' 키의 keyCode + print("command + A pressed") + case 1: // 'S' 키의 keyCode + print("command + S pressed") + case 2: // 'D' 키의 keyCode + print("command + D pressed") + case 3: // 'F' 키의 keyCode + print("command + F pressed") + default: + break + } + } else { + switch event.keyCode { + case 12: // 'Q' 키의 keyCode + print("Q pressed") + case 13: // 'W' 키의 keyCode + print("W pressed") + case 14: // 'E' 키의 keyCode + print("E pressed") + case 15: // 'R' 키의 keyCode + print("R pressed") + case 0: // 'A' 키의 keyCode + print("A pressed") + case 1: // 'S' 키의 keyCode + print("S pressed") + StorageConfig.shared.setThreshold(.percentage50) + StorageConfig.shared.setPeriod(.period10s) + case 2: // 'D' 키의 keyCode + print("D pressed") + DispatchQueue.main.async { + StorageConfig.shared.setThreshold(.percentage30) + } + StorageConfig.shared.setPeriod(.period1s) + case 3: // 'F' 키의 keyCode + print("F pressed") + case 53: // Escape 키 + print("escape") +// menubarVCDelegate?.toggleWindow(sender: nil) + default: + break + } + } + return event + } + } +} diff --git a/Box42/FunctionButton/View/BoxFunctionViewGroup.swift b/Box42/FunctionButton/View/BoxFunctionViewGroup.swift index 2e961a0..6510582 100644 --- a/Box42/FunctionButton/View/BoxFunctionViewGroup.swift +++ b/Box42/FunctionButton/View/BoxFunctionViewGroup.swift @@ -10,7 +10,7 @@ import SnapKit class BoxFunctionViewGroup: NSView { lazy var preferenceButton: PreferenceButtonView = PreferenceButtonView(image: NSImage(imageLiteralResourceName: "plus"), completion: { self.preferenceAction?() }) - lazy var pinButton: PinButtonView = PinButtonView(image: NSImage(imageLiteralResourceName: "pin-box"), completion: { self.pinAction?() }) + lazy var pinButton: PinButtonView = PinButtonView(title: "Pin Box", image: NSImage(imageLiteralResourceName: "pin-box"), completion: { self.pinAction?() }) lazy var quitButton: QuitButtonView = QuitButtonView(image: NSImage(imageLiteralResourceName: "figure.snowboarding"), completion: { self.quitAction?() }) lazy var boxButton: BoxFunctionButtonView = BoxFunctionButtonView(image: NSImage(imageLiteralResourceName: "shippingbox"), completion: { self.boxAction?() }) diff --git a/Box42/FunctionButton/View/PinButtonView.swift b/Box42/FunctionButton/View/PinButtonView.swift index 9c9db8b..158fbf3 100644 --- a/Box42/FunctionButton/View/PinButtonView.swift +++ b/Box42/FunctionButton/View/PinButtonView.swift @@ -11,35 +11,44 @@ import SnapKit class PinButtonView: NSView { private var callback: (() -> Void)? - private var pinBoxButton: NSButton! - private var pinBoxLabel: NSTextField! + private let pinBoxButton: NSButton = { + let button = NSButton() + return button + }() - init(image: NSImage, completion: @escaping () -> Void) { + init(title: String, image: NSImage, completion: @escaping () -> Void) { super.init(frame: .zero) - pinBoxButton = NSButton(image: image, target: self, action: #selector(pin)) + pinBoxButton.image = image + pinBoxButton.imagePosition = .imageLeading + pinBoxButton.image?.alignmentRect = NSRect(x: 0, y: 7, width: 22, height: 22) + + pinBoxButton.target = self + pinBoxButton.action = #selector(pin) + pinBoxButton.isBordered = false pinBoxButton.wantsLayer = true pinBoxButton.layer?.backgroundColor = NSColor.clear.cgColor + pinBoxButton.bezelStyle = .inline + let pinBoxTitle = title + + let attributes: [NSAttributedString.Key: Any] = [ + NSAttributedString.Key.font: NSFont.systemFont(ofSize: 14.0, weight: .semibold), // 원하는 폰트 및 무게로 설정 + NSAttributedString.Key.foregroundColor: NSColor(red: 0.41, green: 0.41, blue: 0.41, alpha: 1), + ] + + let attributedTitle = NSAttributedString(string: pinBoxTitle, attributes: attributes) + + pinBoxButton.attributedTitle = attributedTitle + pinBoxButton.attributedAlternateTitle = attributedTitle + self.addSubview(pinBoxButton) - - pinBoxLabel = NSTextField(labelWithString: "Pin Box") - pinBoxLabel.font = NSFont(name: "Inter", size: 14) - pinBoxLabel.textColor = NSColor(hex: "#696969") - pinBoxLabel.backgroundColor = NSColor.clear - pinBoxLabel.isBordered = false - - self.addSubview(pinBoxLabel) - pinBoxButton.snp.makeConstraints { make in make.left.equalToSuperview() make.centerY.equalToSuperview() - } - - pinBoxLabel.snp.makeConstraints { make in - make.left.equalTo(pinBoxButton.snp.right).offset(2) - make.centerY.equalToSuperview() + make.width.equalTo(89) + make.height.equalTo(27) } self.callback = completion diff --git a/Box42/Main/BoxBaseContainerViewController.swift b/Box42/Main/BoxBaseContainerViewController.swift index 49bdf4c..dda231d 100644 --- a/Box42/Main/BoxBaseContainerViewController.swift +++ b/Box42/Main/BoxBaseContainerViewController.swift @@ -7,6 +7,7 @@ import Cocoa import SnapKit +import Combine class BoxBaseContainerViewController: NSViewController { // MARK: - LeftContainer @@ -16,49 +17,443 @@ class BoxBaseContainerViewController: NSViewController { var quickSlotGroupVC: QuickSlotViewController = QuickSlotViewController() var functionGroupVC: BoxFunctionViewController = BoxFunctionViewController() let windowViewGroupVC: WindowButtonViewController = WindowButtonViewController() - var leftContainer: MovableContainerView = MovableContainerView() - var buttonGroupVC: ButtonGroupViewController = ButtonGroupViewController() + var leftView: MovableContainerView = MovableContainerView() +// var buttonGroupVC: ButtonGroupViewController = ButtonGroupViewController() // MARK: - QuickSlot var preferenceVC: PreferencesViewController = PreferencesViewController() var scriptsVC: ScriptsViewController = ScriptsViewController() - + weak var menubarVCDelegate: MenubarViewControllerDelegate? // extension + var quickSlotManagerVC: QuickSlotManagerViewController = QuickSlotManagerViewController() + var quickSlotButtonCollectionVC: QuickSlotButtonCollectionViewController = QuickSlotButtonCollectionViewController() + + // MARK: - table View + var viewModel: BookmarkViewModel? = BookmarkViewModel.shared + + var cancellables: Set = [] + + private let bookMarkView: NSView = { + let view = NSView() + return view + }() + + let tableView: NSTableView = { + let tableView = NSTableView() + tableView.autoresizingMask = [.width, .height] + tableView.headerView = nil + return tableView + }() + + + // MARK: - table View End + + private func setupBindings() { + print("Setting up bindings...") // 디버깅 로그 + viewModel?.$bookMarkList.sink(receiveValue: { [weak self] newScripts in + print("Received new scripts: \(newScripts)") // 디버깅 로그 + DispatchQueue.main.async { + self?.tableView.reloadData() + } + }).store(in: &cancellables) + } + + var selectedRow: Int? + var selectedButton: DraggableButton? + override func loadView() { + toolbarGroupVC.baseContainerVC = self + self.view = NSView() - self.view.addSubview(splitView) - splitView.delegate = self + self.view.wantsLayer=true + self.view.layer?.backgroundColor = NSColor(hex: "#E7E7E7").cgColor + self.view.snp.makeConstraints { make in + make.width.equalTo(BoxSizeManager.shared.size.width) + make.height.equalTo(BoxSizeManager.shared.size.height) + } + + // splitView.adjustSubviews() + // splitView.setNeedsDisplay(splitView.bounds) + // splitView.addArrangedSubview(leftView) + // splitView.addArrangedSubview(contentGroup) + // self.view.addSubview(splitView) + // + // splitView.snp.makeConstraints { make in + // make.edges.equalToSuperview().inset(10) + // } + self.view.addSubview(leftView) + self.view.addSubview(contentGroup) + + leftView.snp.makeConstraints { make in + make.top.bottom.equalToSuperview().inset(12) + make.leading.equalToSuperview().offset(16) + make.trailing.equalTo(contentGroup.snp.leading).offset(-18) + make.width.equalTo((268 + 16 + 18)) + } + + contentGroup.snp.makeConstraints { make in + make.top.bottom.trailing.equalToSuperview().inset(12) + make.leading.equalTo(leftView.snp.trailing) + } + + let borderView = NSView() + borderView.wantsLayer = true + borderView.layer?.backgroundColor = NSColor(red: 0.773, green: 0.773, blue: 0.773, alpha: 1).cgColor + leftView.addSubview(windowViewGroupVC.view) + leftView.addSubview(bookMarkView) + leftView.addSubview(toolbarGroupVC.view) + leftView.addSubview(borderView) + leftView.addSubview(quickSlotGroupVC.view) + leftView.addSubview(functionGroupVC.view) + + windowViewGroupVC.view.snp.makeConstraints { make in + make.top.equalTo(leftView) + make.left.equalTo(leftView).offset(3) + make.width.equalTo(77) + make.height.equalTo(21) + } + toolbarGroupVC.view.snp.makeConstraints { make in + make.top.equalTo(windowViewGroupVC.view.snp.bottom).offset(31) + make.right.equalTo(leftView) + make.left.equalTo(leftView) + make.height.equalTo(44 + 14 + 24) + } + bookMarkView.snp.makeConstraints { make in + make.top.equalTo(toolbarGroupVC.view.snp.bottom).offset(Constants.UI.groupAutolayout) + make.leading.trailing.equalToSuperview() + make.width.equalToSuperview() + make.bottom.equalTo(quickSlotGroupVC.view.snp.top).offset(-Constants.UI.groupAutolayout) + } + borderView.snp.makeConstraints { make in + make.top.equalTo(bookMarkView.snp.bottom).offset(15) + make.leading.trailing.equalToSuperview() + make.height.equalTo(1) + } + quickSlotGroupVC.view.snp.makeConstraints { make in + make.top.equalTo(borderView.snp.bottom).offset(0) + make.bottom.equalTo(functionGroupVC.view.snp.top).offset(-27) + make.right.equalTo(leftView).offset(0) + make.left.equalTo(leftView) + make.height.equalTo(178) + } + functionGroupVC.view.snp.makeConstraints { make in + make.right.equalTo(leftView).offset(-Constants.UI.groupAutolayout) + make.left.bottom.equalTo(leftView) + } + + let superView = NSView() + + let imageView = NSImageView() + imageView.image = NSImage(named: NSImage.Name("bookmark")) + imageView.image?.size = NSSize(width: 18, height: 18) + superView.addSubview(imageView) + + let label = NSTextField(labelWithString: "북마크") + label.textColor = NSColor.black + label.font = NSFont.boldSystemFont(ofSize: 16) + superView.addSubview(label) -// buttonGroup = BoxButtonViewGroupInit() + let buttonImage = NSImage(named: NSImage.Name("add"))! + buttonImage.size = NSSize(width: 24, height: 24) + let button = NSButton(image: buttonImage, target: self, action: #selector(addBookMarkButtonClicked(_:))) + button.setButtonType(.momentaryChange) + button.bezelStyle = .texturedRounded + button.isBordered = false + button.wantsLayer = true + button.layer?.backgroundColor = NSColor(hex:"#E7E7E7").cgColor + superView.addSubview(button) - leftContainerInit() - viewInit() + imageView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.centerY.equalToSuperview() + } + + label.snp.makeConstraints { make in + make.left.equalTo(imageView.snp.right).offset(8) + make.centerY.equalToSuperview() + } + + button.snp.makeConstraints { make in + make.right.equalToSuperview() + make.centerY.equalToSuperview() + } + + tableView.wantsLayer = true + tableView.backgroundColor = NSColor(hex: "#E7E7E7") + tableView.focusRingType = .none + tableView.headerView = nil + tableView.autoresizingMask = [.width, .height] + tableView.selectionHighlightStyle = .none + tableView.intercellSpacing = NSSize(width: 0, height: 0) + tableView.setDraggingSourceOperationMask(.move, forLocal: true) + + let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("column1")) + column.title = "" + // column.width = 100 + column.resizingMask = .autoresizingMask + + tableView.addTableColumn(column) + + let scrollView = NSScrollView() + scrollView.documentView = tableView + + bookMarkView.addSubview(superView) + bookMarkView.addSubview(scrollView) + // bookMarkView.addSubview(tableView) + + superView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(18) + make.leading.trailing.equalToSuperview().offset(0) + make.height.equalTo(24) + } + + // tableView.snp.makeConstraints { make in + // make.top.equalTo(stackView.snp.bottom).offset(0) + // make.leading.equalToSuperview() + // } + + scrollView.snp.makeConstraints { make in + make.top.equalTo(superView.snp.bottom).offset(0) + make.leading.trailing.equalToSuperview().offset(0) + make.width.equalToSuperview() + make.bottom.equalToSuperview() + } + + tableView.dataSource = self + tableView.delegate = self + tableView.registerForDraggedTypes([NSPasteboard.PasteboardType.string]) } - override func viewDidLoad() { - self.view.wantsLayer = true + @objc func addBookMarkButtonClicked(_ sender: NSButton) { + contentGroup.removeAllSubviews() + + let bookmarkTableView = BookmarkEditorTableView() + bookmarkTableView.setup() + bookmarkTableView.setupBindings() -// self.view.layer?.backgroundColor = NSColor(hex: "#FF9548").cgColor - self.view.layer?.backgroundColor = NSColor(hex: "#E7E7E7").cgColor + let scrollView = NSScrollView() + contentGroup.addSubview(scrollView) + scrollView.documentView = bookmarkTableView + + scrollView.snp.makeConstraints({ make in + make.edges.equalToSuperview() + }) + + bookmarkTableView.snp.makeConstraints({ make in + make.edges.equalToSuperview() + }) + } + + override func viewDidLoad() { +// self.view.wantsLayer = true + super.viewDidLoad() + setupBindings() +// +//// self.view.layer?.backgroundColor = NSColor(hex: "#FF9548").cgColor +// self.view.layer?.backgroundColor = NSColor(hex: "#E7E7E7").cgColor NotificationCenter.default.addObserver(self, selector: #selector(handleButtonTapped), name: .collectionButtonTapped, object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(headerTappedQuickSlotManagerHandle), name: .collectionHeaderTapped, object: nil) } - func BoxButtonViewGroupInit() -> BoxButtonViewGroup { +// func BoxButtonViewGroupInit() -> BoxButtonViewGroup { +// +// let buttonGroup = BoxButtonViewGroup { sender in +// self.clickBtn(sender: sender) +// } +// +// return buttonGroup +// } +// +// private func leftContainerInit() { +// leftContainer.frame.size.width = BoxSizeManager.shared.windowButtonGroupSize.width +// leftContainer.frame.size.height = BoxSizeManager.shared.windowButtonGroupSize.height +// leftContainer.addSubview(windowViewGroupVC.view) +// leftContainer.addSubview(buttonGroupVC.view) +// leftContainer.addSubview(toolbarGroupVC.view) +// leftContainer.addSubview(quickSlotGroupVC.view) +// leftContainer.addSubview(functionGroupVC.view) +// +// leftContainerAutolayout() +// } +// +// private func leftContainerAutolayout() { +// windowViewGroupVC.view.snp.makeConstraints { make in +// make.top.equalTo(leftContainer) +// make.left.equalTo(leftContainer).offset(3) +// make.width.equalTo(77) +// make.height.equalTo(21) +// } +// +// toolbarGroupVC.view.snp.makeConstraints { make in +// make.top.equalTo(windowViewGroupVC.view.snp.bottom).offset(31) +// make.right.equalTo(leftContainer) +// make.left.equalTo(leftContainer) +// make.height.equalTo(44 + 14 + 24) +// } +// +// buttonGroupVC.view.snp.makeConstraints { make in +// make.top.equalTo(toolbarGroupVC.view.snp.bottom).offset(Constants.UI.groupAutolayout) +// make.right.equalTo(leftContainer).offset(-Constants.UI.groupAutolayout) +// make.left.equalTo(leftContainer) +// make.bottom.equalTo(quickSlotGroupVC.view.snp.top).offset(-Constants.UI.groupAutolayout) +// } +// +// quickSlotGroupVC.view.snp.makeConstraints { make in +// make.bottom.equalTo(functionGroupVC.view.snp.top).offset(-27) +// make.right.equalTo(leftContainer).offset(-Constants.UI.groupAutolayout) +// make.left.equalTo(leftContainer) +// make.height.equalTo(178) +// } +// +// functionGroupVC.view.snp.makeConstraints { make in +// make.right.equalTo(leftContainer).offset(-Constants.UI.groupAutolayout) +// make.left.bottom.equalTo(leftContainer) +// } +// } +// +// func viewInit() { +// self.boxViewSizeInit() +// +// splitView.addArrangedSubview(leftContainer) +// splitView.addArrangedSubview(contentGroup) +// self.view.addSubview(splitView) +// +// splitView.snp.makeConstraints { make in +// make.top.equalToSuperview().offset(Constants.UI.groupAutolayout) +// make.left.equalToSuperview().offset(Constants.UI.groupAutolayout) +// make.right.equalToSuperview().offset(-Constants.UI.groupAutolayout) +// make.bottom.equalToSuperview().offset(-Constants.UI.groupAutolayout) +// } +// } +// +// func boxViewSizeInit() { +// self.view.frame.size.width = BoxSizeManager.shared.size.width +// self.view.frame.size.height = BoxSizeManager.shared.size.height +// } +} + +extension BoxBaseContainerViewController: NSSplitViewDelegate { + func splitView(_ splitView: NSSplitView, shouldAdjustSizeOfSubview view: NSView) -> Bool { + return false + } + +} + +extension BoxBaseContainerViewController: NSTableViewDelegate { + func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? { + let pasteboardItem = NSPasteboardItem() + pasteboardItem.setString(String(row), forType: .string) + return pasteboardItem + } +} + +class ButtonTableCellView: NSTableCellView { + var button: NSButton! + var deleteButton: NSButton! + var rowIndex: Int! + + override func viewWillDraw() { + super.viewWillDraw() + } +} + + +extension BoxBaseContainerViewController: NSTableViewDataSource { + func numberOfRows(in tableView: NSTableView) -> Int { + return BookmarkViewModel.shared.bookMarkList.count + } + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + let cellView = ButtonTableCellView() + cellView.rowIndex = row + + let button = DraggableButton(frame: NSRect(x: 0, y: 0, width: 268, height: 44)) + button.tag = row + button.bezelStyle = .inline + button.isBordered = false + button.title = "" + button.associatedString = BookmarkViewModel.shared.bookMarkList[row].name + button.registerForDraggedTypes([NSPasteboard.PasteboardType.string]) + button.target = self + button.action = #selector(buttonClicked(_:)) + button.delegate = self + + let label = NSTextField(frame: NSRect(x: 26 + 21 + 8, y: 25 / 2, width: button.bounds.width, height: button.bounds.height)) + + label.stringValue = BookmarkViewModel.shared.bookMarkList[row].name + label.backgroundColor = .clear + label.isBordered = false + label.isEditable = false + + let attributes : [NSAttributedString.Key : Any] = + [ + NSAttributedString.Key.font : NSFont.systemFont(ofSize:18.0, weight: .light), + NSAttributedString.Key.foregroundColor : NSColor.black, + ] + let attributedStringTitle = NSAttributedString(string: label.stringValue , attributes: + attributes) + label.attributedStringValue=attributedStringTitle + button.addSubview(label) - let buttonGroup = BoxButtonViewGroup { sender in - self.clickBtn(sender: sender) + // let image = NSImage(named: NSImage.Name("bookmark-default")) + // image?.size = NSSize(width: 21, height: 21) + // button.image = image + // button.imagePosition = .imageLeading + // button.image?.alignmentRect = NSRect(x: 0, y: 0, width: 21, height: 21) + + let imageView = NSImageView(frame: NSRect(x: 26, y: 25 / 2, width: 21, height: 21)) + imageView.image = NSImage(named: NSImage.Name("bookmark-default")) + imageView.imageScaling = .scaleProportionallyUpOrDown + imageView.imageAlignment = .alignCenter + button.addSubview(imageView) + + cellView.addSubview(button) + + button.snp.makeConstraints { make in + make.top.equalToSuperview().offset(2) + make.leading.equalToSuperview() + make.trailing.equalToSuperview() + make.width.equalToSuperview() + make.height.equalTo(44) + } + + tableView.rowHeight = 50 + + if row == selectedRow { + button.wantsLayer = true + button.layer?.cornerRadius = 12 + button.layer?.backgroundColor = NSColor.white.cgColor + } else { + button.wantsLayer = true + button.layer?.cornerRadius = 12 + button.layer?.backgroundColor = NSColor.clear.cgColor + } + + return cellView + } + + @objc func buttonClicked(_ sender: DraggableButton) { + selectedButton?.layer?.backgroundColor = NSColor.clear.cgColor + + selectedButton = sender + sender.layer?.backgroundColor = NSColor.white.cgColor + + if sender.tag < BookmarkViewModel.shared.bookMarkList.count { + if let url = URL(string: BookmarkViewModel.shared.bookMarkList[sender.tag].url) { + print(url) + clickBtn(sender: sender) + } } - - return buttonGroup } func clickBtn(sender: Any?) { if let button = sender as? NSButton { guard let clickCount = NSApp.currentEvent?.clickCount else { return } if clickCount == 2 { - WebViewManager.shared.list[button.title]!.reload() + WebViewManager.shared.list[button.title]?.reload() print("Dobule Click") } else if clickCount > 2 { if let currentURL = WebViewManager.shared.hostingWebView?.url { @@ -67,7 +462,7 @@ class BoxBaseContainerViewController: NSViewController { print("Triple Click") } else if clickCount < 2 { contentGroup.removeAllSubviews() - contentGroup.showWebviews(button) + contentGroup.showWebviews(button) // sender.tag } } else { if let str = sender as? String { @@ -79,107 +474,40 @@ class BoxBaseContainerViewController: NSViewController { } } - private func leftContainerInit() { - leftContainer.frame.size.width = BoxSizeManager.shared.windowButtonGroupSize.width - leftContainer.frame.size.height = BoxSizeManager.shared.windowButtonGroupSize.height - leftContainer.addSubview(windowViewGroupVC.view) - leftContainer.addSubview(buttonGroupVC.view) - leftContainer.addSubview(toolbarGroupVC.view) - leftContainer.addSubview(quickSlotGroupVC.view) - leftContainer.addSubview(functionGroupVC.view) - - leftContainerAutolayout() - } - - private func leftContainerAutolayout() { - windowViewGroupVC.view.snp.makeConstraints { make in - make.top.equalTo(leftContainer) - make.left.equalTo(leftContainer).offset(3) - make.width.equalTo(77) - make.height.equalTo(21) - } - - toolbarGroupVC.view.snp.makeConstraints { make in - make.top.equalTo(windowViewGroupVC.view.snp.bottom).offset(31) - make.right.equalTo(leftContainer) - make.left.equalTo(leftContainer) - make.height.equalTo(44 + 14 + 24) - } - - buttonGroupVC.view.snp.makeConstraints { make in - make.top.equalTo(toolbarGroupVC.view.snp.bottom).offset(Constants.UI.groupAutolayout) - make.right.equalTo(leftContainer).offset(-Constants.UI.groupAutolayout) - make.left.equalTo(leftContainer) - make.bottom.equalTo(quickSlotGroupVC.view.snp.top).offset(-Constants.UI.groupAutolayout) - } - - quickSlotGroupVC.view.snp.makeConstraints { make in - make.bottom.equalTo(functionGroupVC.view.snp.top).offset(-27) - make.right.equalTo(leftContainer).offset(-Constants.UI.groupAutolayout) - make.left.equalTo(leftContainer) - make.height.equalTo(178) - } - - functionGroupVC.view.snp.makeConstraints { make in - make.right.equalTo(leftContainer).offset(-Constants.UI.groupAutolayout) - make.left.bottom.equalTo(leftContainer) + func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation { + if dropOperation == .above { + return .move + } else { + return [] } } - func viewInit() { - self.boxViewSizeInit() + func tableView(_ aTableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool { + guard let str = info.draggingPasteboard.string(forType: .string), let from = Int(str) else { + return false + } - splitView.addArrangedSubview(leftContainer) - splitView.addArrangedSubview(contentGroup) - self.view.addSubview(splitView) + let to = (from < row) ? row - 1 : row + let item = BookmarkViewModel.shared.bookMarkList[from] + BookmarkViewModel.shared.bookMarkList.remove(at: from) + BookmarkViewModel.shared.bookMarkList.insert(item, at: to) - splitView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(Constants.UI.groupAutolayout) - make.left.equalToSuperview().offset(Constants.UI.groupAutolayout) - make.right.equalToSuperview().offset(-Constants.UI.groupAutolayout) - make.bottom.equalToSuperview().offset(-Constants.UI.groupAutolayout) + for (_, subview) in tableView.subviews.enumerated() { + guard let cellView = subview as? CustomTableCellView else { + continue + } + cellView.button.title = BookmarkViewModel.shared.bookMarkList[cellView.rowIndex].url } - } - - func boxViewSizeInit() { - self.view.frame.size.width = BoxSizeManager.shared.size.width - self.view.frame.size.height = BoxSizeManager.shared.size.height - } -} - -extension BoxBaseContainerViewController: NSSplitViewDelegate { - func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { - if dividerIndex == 0 { - return CGFloat(132).pointsToPixels() - } - return proposedMinimumPosition - } - - func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat { - if dividerIndex == 0 { - return CGFloat(312).pointsToPixels() - } - return proposedMaximumPosition - } - - func splitView(_ splitView: NSSplitView, resizeSubviewsWithOldSize oldSize: NSSize) { - let dividerThickness = splitView.dividerThickness - let newWidth = splitView.frame.width - dividerThickness - - let leftWidth = leftContainer.frame.width - let contentWidth = newWidth - leftWidth - - leftContainer.frame = NSRect(x: 0, y: 0, width: leftWidth, height: splitView.bounds.height) - contentGroup.frame = NSRect(x: leftWidth + dividerThickness, y: 0, width: contentWidth, height: splitView.bounds.height) + return true } } -extension BoxBaseContainerViewController: BoxFunctionViewControllerDelegate { - func didTapBoxButton() { - clickBtn(sender: "box") - } -} +//extension BoxBaseContainerViewController: BoxFunctionViewControllerDelegate { +// func didTapBoxButton() { +// clickBtn(sender: "box") +// } +//} extension BoxBaseContainerViewController { @objc func handleButtonTapped(notification: NSNotification) { @@ -195,14 +523,28 @@ extension BoxBaseContainerViewController { } if button.title == QuickSlotUI.title.user { - print("Button with title \(button.title) was tapped in BaseVC") - contentGroup.removeAllSubviews() - print(WebViewManager.shared.hostingWebView!) - contentGroup.addSubview(WebViewManager.shared.hostingWebView!) - WebViewManager.shared.hostingWebView!.snp.makeConstraints { make in - make.top.bottom.left.right.equalToSuperview() + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.keyboard") { + NSWorkspace.shared.open(url) + + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.general") { + NSWorkspace.shared.open(url) + } + } } } } } } + +extension BoxBaseContainerViewController { + @objc func headerTappedQuickSlotManagerHandle(notification: NSNotification) { + print("QuickSlotManager") + contentGroup.removeAllSubviews() + print(quickSlotManagerVC) + contentGroup.addSubview(quickSlotManagerVC.view) + quickSlotManagerVC.view.snp.makeConstraints { make in + make.top.bottom.left.right.equalToSuperview() + } + } +} diff --git a/Box42/BoxWindowController.swift b/Box42/Main/BoxWindowController.swift similarity index 100% rename from Box42/BoxWindowController.swift rename to Box42/Main/BoxWindowController.swift diff --git a/Box42/UI/HoverButton.swift b/Box42/Main/UI/HoverButton.swift similarity index 100% rename from Box42/UI/HoverButton.swift rename to Box42/Main/UI/HoverButton.swift diff --git a/Box42/UI/MovableContainerView.swift b/Box42/Main/UI/MovableContainerView.swift similarity index 87% rename from Box42/UI/MovableContainerView.swift rename to Box42/Main/UI/MovableContainerView.swift index bd8dba6..88a5d15 100644 --- a/Box42/UI/MovableContainerView.swift +++ b/Box42/Main/UI/MovableContainerView.swift @@ -10,6 +10,8 @@ import AppKit class MovableContainerView: NSView { init() { super.init(frame: .zero) + self.frame.size.width = 302 - 12 + self.frame.size.height = 1200 } required init?(coder: NSCoder) { diff --git a/Box42/View/BoxBaseSplitView.swift b/Box42/Main/View/BoxBaseSplitView.swift similarity index 100% rename from Box42/View/BoxBaseSplitView.swift rename to Box42/Main/View/BoxBaseSplitView.swift diff --git a/Box42/View/BoxContentsViewGroup.swift b/Box42/Main/View/BoxContentsViewGroup.swift similarity index 87% rename from Box42/View/BoxContentsViewGroup.swift rename to Box42/Main/View/BoxContentsViewGroup.swift index 959fb80..8c57685 100644 --- a/Box42/View/BoxContentsViewGroup.swift +++ b/Box42/Main/View/BoxContentsViewGroup.swift @@ -8,17 +8,22 @@ import WebKit import SnapKit -class BoxContentsViewGroup: NSView { +class BoxContentsViewGroup: NSView, WKUIDelegate { var webVC: WebViewController? var preferencesVC = PreferencesViewController() var scriptsVC = ScriptsViewController() +// var webView: WKWebView! +// static let shared = BoxContentsViewGroup() + init() { super.init(frame: .zero) webVC = WebViewController(nibName: nil, bundle: nil) + self.wantsLayer = true self.layer?.cornerRadius = 20 self.layer?.masksToBounds = true + self.addSubview(webVC!.view) webVC?.view.snp.makeConstraints { make in make.edges.equalTo(self) @@ -28,7 +33,7 @@ class BoxContentsViewGroup: NSView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func removeAllSubviews() { for subview in self.subviews { subview.removeFromSuperview() @@ -52,11 +57,11 @@ class BoxContentsViewGroup: NSView { } func showWebviews(_ sender: NSButton) { - guard let currentWebview = WebViewManager.shared.list[sender.title] else { - print("No WebView found for title: \(sender.title)") + guard let currentWebview = WebViewManager.shared.list[sender.associatedString ?? "42Box"] else { + print("No WebView found for title: \(sender.associatedString)") return } - + WebViewManager.shared.hostingname = sender.title WebViewManager.shared.hostingWebView = currentWebview diff --git a/Box42/Main/keyDown+BoxBaseContainerViewController.swift b/Box42/Main/keyDown+BoxBaseContainerViewController.swift deleted file mode 100644 index f73306a..0000000 --- a/Box42/Main/keyDown+BoxBaseContainerViewController.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// keyDown+BoxViewController.swift -// Box42 -// -// Created by Chanhee Kim on 8/21/23. -// - -import AppKit - -extension BoxBaseContainerViewController { - override func keyDown(with event: NSEvent) { - print(event.keyCode) - if event.keyCode == 1 { - StorageConfig.shared.setThreshold(.percentage50) - StorageConfig.shared.setPeriod(.period10s) - } - if event.keyCode == 2 { - // SdtorageConfig.shared.setThreshold(.percentage30) - DispatchQueue.main.async { - StorageConfig.shared.setThreshold(.percentage30) - } - StorageConfig.shared.setPeriod(.period1s) - } - - if event.modifierFlags.contains(.command) && event.keyCode == 15 { - print("Cmd + R pressed, reloading...") - WebViewManager.shared.hostingWebView?.reload() - } - - if event.keyCode == 53 { // Escape 키의 keyCode는 53입니다. - print("escape") - menubarVCDelegate?.toggleWindow(sender: nil) - } else { - super.keyDown(with: event) // 기타 키를 처리하기 위해 상위 클래스에게 전달 - } - } -} diff --git a/Box42/Preferences/View/Funtion/CPUView.swift b/Box42/Preferences/View/Funtion/CPUView.swift index 74a296b..651afa6 100644 --- a/Box42/Preferences/View/Funtion/CPUView.swift +++ b/Box42/Preferences/View/Funtion/CPUView.swift @@ -13,25 +13,25 @@ class CPUView: NSView { // UI Elements private let usageLabel: NSTextField = { let label = NSTextField(labelWithString: "Usage: ") - label.font = NSFont.systemFont(ofSize: 16) + label.font = NSFont.systemFont(ofSize: 16, weight: .semibold) return label }() private let systemLabel: NSTextField = { let label = NSTextField(labelWithString: "System: ") - label.font = NSFont.systemFont(ofSize: 16) + label.font = NSFont.systemFont(ofSize: 16, weight: .semibold) return label }() private let userLabel: NSTextField = { let label = NSTextField(labelWithString: "User: ") - label.font = NSFont.systemFont(ofSize: 16) + label.font = NSFont.systemFont(ofSize: 16, weight: .semibold) return label }() private let idleLabel: NSTextField = { let label = NSTextField(labelWithString: "Idle: ") - label.font = NSFont.systemFont(ofSize: 16) + label.font = NSFont.systemFont(ofSize: 16, weight: .semibold) return label }() diff --git a/Box42/Preferences/View/Funtion/IconSettingView.swift b/Box42/Preferences/View/Funtion/IconSettingView.swift index 4993aa1..bac701a 100644 --- a/Box42/Preferences/View/Funtion/IconSettingView.swift +++ b/Box42/Preferences/View/Funtion/IconSettingView.swift @@ -38,7 +38,7 @@ class CustomSwitch: NSButton { class IconSettingView: NSView { private let iconSettingLabel: NSTextField = { let label = NSTextField(labelWithString: "아이콘 설정") - label.font = NSFont.systemFont(ofSize: 20) + label.font = NSFont.systemFont(ofSize: 20, weight: .semibold) label.isEditable = false label.isSelectable = false return label @@ -46,6 +46,7 @@ class IconSettingView: NSView { private let flipLabel: NSTextField = { let label = NSTextField(labelWithString: "아이콘 좌우반전") + label.font = NSFont.systemFont(ofSize: 14, weight: .medium) return label }() @@ -85,14 +86,14 @@ class IconSettingView: NSView { self.addSubview(speedUpSwitch) iconSettingLabel.snp.makeConstraints { (make) in - make.top.equalToSuperview().offset(10) - make.left.equalToSuperview().offset(10) - make.right.equalToSuperview().offset(-10) + make.top.equalToSuperview().offset(20) + make.leading.equalToSuperview().offset(12) + make.trailing.equalToSuperview().offset(-12) } flipLabel.snp.makeConstraints { (make) in make.top.equalTo(iconSettingLabel.snp.bottom).offset(20) - make.left.equalToSuperview().offset(10) + make.leading.equalToSuperview().offset(22) } flipSwitch.snp.makeConstraints { (make) in diff --git a/Box42/Preferences/View/Funtion/NotificationSettingView.swift b/Box42/Preferences/View/Funtion/NotificationSettingView.swift index 978df63..f7384c9 100644 --- a/Box42/Preferences/View/Funtion/NotificationSettingView.swift +++ b/Box42/Preferences/View/Funtion/NotificationSettingView.swift @@ -13,7 +13,7 @@ class NotificationSettingView: NSView { // Create a label for the title let titleLabel: NSTextField = { let label = NSTextField(labelWithString: "Box 알림 설정") - label.font = NSFont.systemFont(ofSize: 20) + label.font = NSFont.systemFont(ofSize: 20, weight: .semibold) label.isEditable = false label.isSelectable = false return label @@ -22,6 +22,9 @@ class NotificationSettingView: NSView { // Create a switch button let toggleButton: NSButton = { let toggle = NSButton(checkboxWithTitle: "알림 활성화", target: nil, action: #selector(toggleChanged)) + let titleFont = NSFont.systemFont(ofSize: 14, weight: .medium) + toggle.attributedTitle = NSAttributedString(string: toggle.title, attributes: [.font: titleFont]) + return toggle }() @@ -40,14 +43,14 @@ class NotificationSettingView: NSView { addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(self).offset(20) - make.centerX.equalTo(self) + make.leading.equalToSuperview().offset(12) } // Add toggleButton to the view addSubview(toggleButton) toggleButton.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(20) - make.centerX.equalTo(self) + make.leading.equalToSuperview().offset(22) } toggleButton.target = self diff --git a/Box42/Preferences/View/Funtion/RequestAccessView.swift b/Box42/Preferences/View/Funtion/RequestAccessView.swift index b95e905..22b4612 100644 --- a/Box42/Preferences/View/Funtion/RequestAccessView.swift +++ b/Box42/Preferences/View/Funtion/RequestAccessView.swift @@ -11,7 +11,7 @@ import SnapKit class RequestAccessView: NSView { private let requestAccessLabel: NSTextField = { let label = NSTextField(labelWithString: "Request Access") - label.font = NSFont.systemFont(ofSize: 20) + label.font = NSFont.systemFont(ofSize: 20, weight: .semibold) label.isEditable = false label.isSelectable = false return label @@ -24,7 +24,7 @@ class RequestAccessView: NSView { override init(frame frameRect: NSRect) { super.init(frame: .zero) self.wantsLayer = true - self.layer?.backgroundColor = NSColor(hex: "#7FFFFFFF").cgColor +// self.layer?.backgroundColor = NSColor(hex: "#7FFFFFFF").cgColor self.layer?.cornerRadius = 13 // Add subviews @@ -51,7 +51,7 @@ class RequestAccessView: NSView { func textfieldInit() { requestAccessTextField.stringValue = "Script 및 기능들을 실행하기 위해서 루트 디렉토리의 권한이 필요합니다." - requestAccessTextField.font = NSFont.systemFont(ofSize: 15) + requestAccessTextField.font = NSFont.systemFont(ofSize: 14, weight: .medium) requestAccessTextField.isEditable = false requestAccessTextField.isBordered = false requestAccessTextField.backgroundColor = NSColor.clear @@ -61,17 +61,23 @@ class RequestAccessView: NSView { func buttonInit() { grantAccessButton.title = "권한 부여" + grantAccessButton.bezelStyle = .inline + grantAccessButton.wantsLayer = true + grantAccessButton.layer?.cornerRadius = 15 grantAccessButton.target = self grantAccessButton.action = #selector(requestFolderAccess(_:)) revokeAccessButton.title = "권한 취소" + revokeAccessButton.bezelStyle = .inline + revokeAccessButton.wantsLayer = true + revokeAccessButton.layer?.cornerRadius = 15 revokeAccessButton.target = self revokeAccessButton.action = #selector(revokeFolderAccess(_:)) } func directoryNameTextFieldInit() { + directoryNameTextField.font = NSFont.systemFont(ofSize: 12, weight: .light) directoryNameTextField.stringValue = "선택된 디렉터리: 없음" - directoryNameTextField.font = NSFont.systemFont(ofSize: 15) directoryNameTextField.isEditable = false directoryNameTextField.isBordered = false directoryNameTextField.backgroundColor = NSColor.clear @@ -80,35 +86,38 @@ class RequestAccessView: NSView { func directoryNameTextFieldConstraints() { directoryNameTextField.snp.makeConstraints { make in - make.top.equalTo(revokeAccessButton.snp.bottom).offset(10) - make.leading.equalToSuperview().offset(17) - make.trailing.equalToSuperview().offset(-17) + make.top.equalTo(revokeAccessButton.snp.centerY) + make.leading.equalToSuperview().offset(30) } } func textfieldConstraints() { requestAccessLabel.snp.makeConstraints { make in - make.top.equalToSuperview().offset(10) - make.leading.equalToSuperview().offset(17) - make.trailing.equalToSuperview().offset(-17) + make.top.equalToSuperview().offset(20) + make.leading.equalToSuperview().offset(12) + make.trailing.equalToSuperview().offset(-12) } requestAccessTextField.snp.makeConstraints { make in - make.top.equalTo(requestAccessLabel.snp.bottom).offset(10) - make.leading.equalToSuperview().offset(17) - make.trailing.equalToSuperview().offset(-17) + make.top.equalTo(requestAccessLabel.snp.bottom).offset(20) + make.leading.equalToSuperview().offset(22) + make.trailing.equalToSuperview().offset(-22) } } func buttonConstraints() { grantAccessButton.snp.makeConstraints { make in - make.top.equalTo(requestAccessTextField.snp.bottom).offset(10) - make.leading.equalToSuperview().offset(17) + make.top.equalTo(requestAccessTextField.snp.bottom).offset(20) + make.trailing.equalTo(revokeAccessButton.snp.leading).offset(-10) + make.width.equalTo(70) + make.height.equalTo(30) } revokeAccessButton.snp.makeConstraints { make in - make.top.equalTo(requestAccessTextField.snp.bottom).offset(10) - make.leading.equalTo(grantAccessButton.snp.trailing).offset(10) + make.top.equalTo(requestAccessTextField.snp.bottom).offset(20) + make.trailing.equalTo(requestAccessLabel.snp.trailing).offset(0) + make.width.equalTo(70) + make.height.equalTo(30) } } @@ -121,13 +130,22 @@ class RequestAccessView: NSView { openPanel.canCreateDirectories = true openPanel.allowsMultipleSelection = false openPanel.canChooseFiles = false +// openPanel.level = .popUpMenu +// openPanel.begin { (result) in +// if result == .OK { +//// completionHandler(openPanel.urls) +// } else { +//// completionHandler(nil) +// } +// } if openPanel.runModal() == NSApplication.ModalResponse.OK { let result = openPanel.url - + if let result = result { print("Selected folder is \(result.path)") - + +// directoryNameTextField.font = NSFont.systemFont(ofSize: 12, weight: .light) directoryNameTextField.stringValue = "선택된 디렉터리: \(result.path)" do { diff --git a/Box42/Preferences/View/Funtion/ShortcutSettingView.swift b/Box42/Preferences/View/Funtion/ShortcutSettingView.swift index b5e215d..d635ee7 100644 --- a/Box42/Preferences/View/Funtion/ShortcutSettingView.swift +++ b/Box42/Preferences/View/Funtion/ShortcutSettingView.swift @@ -13,7 +13,7 @@ class ShortcutSettingView: NSView { // Create a label for the title let titleLabel: NSTextField = { let label = NSTextField(labelWithString: "앱 내부 단축키 설정") - label.font = NSFont.systemFont(ofSize: 20) + label.font = NSFont.systemFont(ofSize: 20, weight: .semibold) label.isEditable = false label.isSelectable = false return label @@ -22,57 +22,61 @@ class ShortcutSettingView: NSView { // Create an array of labels and text fields for various shortcut settings let shortcutSettings: [(label: String, defaultKey: String)] = [ ("앱 Show 단축키 설정", "Middle Mouse"), - ("Pin Box 단축키 설정", "P"), - ("퀵슬롯 1 설정", "Q"), - ("퀵슬롯 2 설정", "W"), - ("퀵슬롯 3 설정", "E"), - ("퀵슬롯 4 설정", "R"), - ("퀵슬롯 5 설정", "A"), - ("퀵슬롯 6 설정", "S"), - ("퀵슬롯 7 설정", "D"), - ("퀵슬롯 8 설정", "F") + ("Pin Box 단축키 설정", "^B"), + ("퀵슬롯 1 설정", "^Q"), + ("퀵슬롯 2 설정", "^W"), + ("퀵슬롯 3 설정", "^E"), + ("퀵슬롯 4 설정", "^R"), + ("퀵슬롯 5 설정", "^A"), + ("퀵슬롯 6 설정", "^S"), + ("퀵슬롯 7 설정", "^D"), + ("퀵슬롯 8 설정", "^F") ] lazy var stackView: NSStackView = { - let stackView = NSStackView() - stackView.orientation = .vertical - stackView.distribution = .fillEqually - stackView.spacing = 20 - - var subStackView: NSStackView? + let stackView = NSStackView() + stackView.orientation = .vertical + stackView.distribution = .fillEqually + stackView.spacing = 20 + + var subStackView: NSStackView? + + for (index, (labelText, defaultKey)) in shortcutSettings.enumerated() { + let label = NSTextField(labelWithString: labelText) + label.font = NSFont.systemFont(ofSize: 14, weight: .medium) + label.isEditable = false + label.isSelectable = false + let textField = NSTextField() + textField.placeholderString = defaultKey + textField.snp.makeConstraints { make in + make.width.equalTo(50) + } - for (index, (labelText, defaultKey)) in shortcutSettings.enumerated() { - let label = NSTextField(labelWithString: labelText) - label.isEditable = false - label.isSelectable = false - let textField = NSTextField() - textField.placeholderString = defaultKey + if index == 0 || index == 1 { + let innerStackView = NSStackView(views: [label, textField]) + innerStackView.distribution = .fillProportionally + stackView.addArrangedSubview(innerStackView) + } else { + if index == 2 || index == 6 { + subStackView = NSStackView() + subStackView?.orientation = .horizontal + subStackView?.distribution = .fillEqually + subStackView?.spacing = 20 + } + + let innerStackView = NSStackView(views: [label, textField]) + innerStackView.distribution = .fillProportionally - if index == 0 || index == 1 { - let innerStackView = NSStackView(views: [label, textField]) - innerStackView.distribution = .fillProportionally - stackView.addArrangedSubview(innerStackView) - } else { - if index == 2 || index == 6 { - subStackView = NSStackView() - subStackView?.orientation = .horizontal - subStackView?.distribution = .fillEqually - subStackView?.spacing = 20 - } - - let innerStackView = NSStackView(views: [label, textField]) - innerStackView.distribution = .fillProportionally - - subStackView?.addArrangedSubview(innerStackView) - - if index == 4 || index == 8 { - stackView.addArrangedSubview(subStackView!) - } + subStackView?.addArrangedSubview(innerStackView) + + if index == 4 || index == 8 { + stackView.addArrangedSubview(subStackView!) } } - - return stackView - }() + } + + return stackView + }() override init(frame frameRect: NSRect) { super.init(frame: frameRect) @@ -89,17 +93,15 @@ class ShortcutSettingView: NSView { addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(self).offset(20) - make.centerX.equalTo(self) + make.leading.equalToSuperview().offset(12) } // Add stackView to the view addSubview(stackView) stackView.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(20) - make.left.equalTo(self).offset(20) - make.right.equalTo(self).offset(-20) + make.leading.equalToSuperview().offset(22) + make.trailing.equalToSuperview().offset(-22) } } - - // Further actions like saving the shortcuts, adding validations can be implemented } diff --git a/Box42/Preferences/View/Funtion/StorageView.swift b/Box42/Preferences/View/Funtion/StorageView.swift index 9a72a30..953fe24 100644 --- a/Box42/Preferences/View/Funtion/StorageView.swift +++ b/Box42/Preferences/View/Funtion/StorageView.swift @@ -42,24 +42,28 @@ class StorageView: NSView { func initTextFields() { // Initialize textfields - currentStorageTextField.stringValue = "Current Storage: ?? GB" - remainingStorageTextField.stringValue = "Remaining Storage: ?? GB" - totalStorageTextField.stringValue = "Total Storage: ?? GB" + let labels = [currentStorageTextField, remainingStorageTextField, totalStorageTextField, thresholdTextField, intervalTextField] + for label in labels { + label.font = NSFont.systemFont(ofSize: 14, weight: .semibold) + label.isEditable = false + label.isBordered = false + label.backgroundColor = NSColor.clear + } + currentStorageTextField.stringValue = "Current Storage : ?? GB" + remainingStorageTextField.stringValue = "Remaining Storage : ?? GB" + totalStorageTextField.stringValue = "Total Storage : ?? GB" thresholdTextField.placeholderString = "Enter threshold (%)" intervalTextField.placeholderString = "Enter interval (seconds)" - - [currentStorageTextField, remainingStorageTextField, totalStorageTextField, thresholdTextField, intervalTextField].forEach { textField in - textField.isEditable = false - textField.isBordered = false - textField.backgroundColor = NSColor.clear - } - + intervalTextField.isEditable = true thresholdTextField.isEditable = true } func initButton() { executeScriptButton.title = "Run Script" + executeScriptButton.bezelStyle = .inline + executeScriptButton.wantsLayer = true + executeScriptButton.layer?.cornerRadius = 15 executeScriptButton.target = self executeScriptButton.action = #selector(runScript(_:)) } @@ -95,8 +99,10 @@ class StorageView: NSView { } executeScriptButton.snp.makeConstraints { make in - make.top.equalTo(intervalTextField.snp.bottom).offset(20) + make.top.equalTo(intervalTextField.snp.bottom).offset(0) // 여기에 20을 추가합니다. make.leading.equalToSuperview().offset(20) + make.width.equalTo(70) + make.height.equalTo(30) } } diff --git a/Box42/Preferences/View/Table/PreferencesTableView.swift b/Box42/Preferences/View/Table/PreferencesTableView.swift index 9c8119f..a0427b4 100644 --- a/Box42/Preferences/View/Table/PreferencesTableView.swift +++ b/Box42/Preferences/View/Table/PreferencesTableView.swift @@ -58,14 +58,17 @@ class PreferencesTableView: NSTableView { self.delegate = self self.dataSource = self + self.headerView = nil + self.selectionHighlightStyle = .none + + self.backgroundColor = .white + if #available(macOS 10.14, *) { + self.appearance = NSAppearance(named: .aqua) + } + let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Preferences")) - column1.width = 100.0 - column1.title = "Preferences" self.addTableColumn(column1) - -// self.selectionHighlightStyle = .none } - } extension PreferencesTableView: NSTableViewDelegate, NSTableViewDataSource { diff --git a/Box42/QuickSlot/Controller/QuickSlotManagerViewController.swift b/Box42/QuickSlot/Controller/QuickSlotManagerViewController.swift new file mode 100644 index 0000000..84363fc --- /dev/null +++ b/Box42/QuickSlot/Controller/QuickSlotManagerViewController.swift @@ -0,0 +1,38 @@ +// +// QuickSlotManagerViewController.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import AppKit + +class QuickSlotManagerViewController: NSViewController { + var quickSlotManagerTableView: QuickSlotTableView? + var viewModel: QuickSlotViewModel? = QuickSlotViewModel.shared { + didSet { + quickSlotManagerTableView?.viewModel = viewModel + } + } + + override func loadView() { + self.view = NSView() + + quickSlotManagerTableView = QuickSlotTableView(frame: .zero) + quickSlotManagerTableView?.setup() + quickSlotManagerTableView?.viewModel = viewModel + quickSlotManagerTableView?.backgroundColor = .white + + let scrollView = NSScrollView() + scrollView.documentView = quickSlotManagerTableView + self.view.addSubview(scrollView) + + scrollView.snp.makeConstraints({ make in + make.edges.equalToSuperview() + }) + + quickSlotManagerTableView?.snp.makeConstraints({ make in + make.edges.equalToSuperview() + }) + } +} diff --git a/Box42/QuickSlot/Controller/QuickSlotScriptsLogicController.swift b/Box42/QuickSlot/Controller/QuickSlotScriptsLogicController.swift index 6c2ada1..0d24603 100644 --- a/Box42/QuickSlot/Controller/QuickSlotScriptsLogicController.swift +++ b/Box42/QuickSlot/Controller/QuickSlotScriptsLogicController.swift @@ -7,9 +7,8 @@ import AppKit -class ScriptsLogicController { - - static let shared = ScriptsLogicController() +class QuickSlotScriptsLogicController { + static let shared = QuickSlotScriptsLogicController() private init() { NotificationCenter.default.addObserver(self, selector: #selector(handleButtonTapped), name: .collectionButtonTapped, object: nil) @@ -17,32 +16,22 @@ class ScriptsLogicController { @objc func handleButtonTapped(notification: NSNotification) { if let button = notification.object as? NSButton { - SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: button.associatedString ?? "") + if let associatedString = button.associatedString { + if let lastThreeCharacters = button.associatedString?.suffix(3) { + if lastThreeCharacters == ".sh" { + ScriptsFileManager.downloadFile(from: "https://42box.kr/" + associatedString) + } + } + } } } - private func executeCleanScript() { - if let scriptPath = Bundle.main.path(forResource: "cleanCache", ofType: "sh") { - let task = Process() - task.launchPath = "/bin/sh" - task.arguments = [scriptPath] - - let outputPipe = Pipe() - task.standardOutput = outputPipe - task.standardError = outputPipe - - task.launch() - task.waitUntilExit() - - let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile() - let output = String(data: outputData, encoding: .utf8) ?? "" - - DispatchQueue.main.async { - print("Output: \(output)") - } - } else { - DispatchQueue.main.async { - print("Script not found") + func shortCutKeyUP(_ keyCode: Int) { + if QuickSlotViewModel.shared.buttons.count > keyCode { + if let lastThreeCharacters = QuickSlotViewModel.shared.buttons[keyCode].path?.suffix(3) { + if lastThreeCharacters == ".sh" { + ScriptsFileManager.downloadFile(from: "https://42box.kr/" + QuickSlotViewModel.shared.buttons[keyCode].path!) + } } } } diff --git a/Box42/QuickSlot/Controller/QuickSlotViewController.swift b/Box42/QuickSlot/Controller/QuickSlotViewController.swift index e60f49a..5a659d9 100644 --- a/Box42/QuickSlot/Controller/QuickSlotViewController.swift +++ b/Box42/QuickSlot/Controller/QuickSlotViewController.swift @@ -21,7 +21,7 @@ class QuickSlotViewController: NSViewController { } func headerAction() { - print("quick slot header") + NotificationCenter.default.post(name: .collectionHeaderTapped, object: nil) } @objc func handleButtonTapped(notification: NSNotification) { diff --git a/Box42/QuickSlot/Model/QuickSlotButtonModel.swift b/Box42/QuickSlot/Model/QuickSlotButtonModel.swift index 9a55e20..0f3b50f 100644 --- a/Box42/QuickSlot/Model/QuickSlotButtonModel.swift +++ b/Box42/QuickSlot/Model/QuickSlotButtonModel.swift @@ -7,15 +7,21 @@ import Foundation +struct QuickSlotModels: Codable { + let quickSlotList : [QuickSlotButtonModel] +} + // Model struct QuickSlotButtonModel: Codable { - let id: UUID + let scriptUuid: UUID var title: String var path: String? + var type: String? - init(id: UUID = UUID(), title: String = "Default", path: String? = nil) { - self.id = id + init(scriptUuid: UUID? = UUID(), title: String = "Default", path: String? = "none", type: String = "sh") { + self.scriptUuid = scriptUuid ?? UUID() self.title = title self.path = path + self.type = type } } diff --git a/Box42/QuickSlot/Model/QuickSlotUI.swift b/Box42/QuickSlot/Model/QuickSlotUI.swift index 096bcbc..f026d00 100644 --- a/Box42/QuickSlot/Model/QuickSlotUI.swift +++ b/Box42/QuickSlot/Model/QuickSlotUI.swift @@ -23,7 +23,7 @@ enum QuickSlotUI { } enum title { - static let clean = "Clean" + static let clean = "CleanCache" static let preferences = "Preferences" static let scripts = "Scripts" static let user = "User" diff --git a/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.swift b/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.swift index b1b31be..4c9b310 100644 --- a/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.swift +++ b/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.swift @@ -88,21 +88,52 @@ extension QuickSlotButtonCollectionViewController: NSCollectionViewDelegate, NSC func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "QuickSlotButtonViewItem"), for: indexPath) - + print("current item index>", indexPath.item) if let customItem = item as? QuickSlotButtonViewItem { - let buttonModel = viewModel.buttons[indexPath.item] - let btn = NSButton() - btn.title = buttonModel.title - btn.action = #selector(collectionButtonTapped) - btn.target = self - btn.wantsLayer = true - btn.layer?.backgroundColor = NSColor.red.cgColor - customItem.view.addSubview(btn) - btn.frame = CGRect(x: 0, y: 0, width: QuickSlotUI.size.button, height: QuickSlotUI.size.button) - btn.associatedString = buttonModel.path + + for subview in customItem.view.subviews { + subview.removeFromSuperview() + } + + if customItem.view.subviews.isEmpty { + let btn = QuickSlotItemButton(indexPath.item) + btn.action = #selector(collectionButtonTapped) + btn.target = self + + let label = QuickSlotItemLabel(indexPath.item) + + customItem.view.addSubview(btn) + customItem.view.addSubview(label) + + btn.snp.makeConstraints { make in + make.left.top.right.equalToSuperview() +// make.width.equalTo(58) + make.height.equalTo(58) + } + + label.snp.makeConstraints { make in + make.top.equalTo(btn.snp.bottom) + make.left.right.bottom.equalToSuperview() + } + } } return item } +// func collectionView(_ collectionView: NSCollectionView, mouseEnteredAt indexPath: IndexPath) { +// // 호버 효과를 적용하려는 코드 +// if let item = collectionView.item(at: indexPath) as? QuickSlotButtonViewItem, +// let button = item.view.subviews.first as? QuickSlotItemButton { +// button.mouseEntered(with: NSEvent()) // 호버 효과 메서드 호출 +// } +// } +// +// func collectionView(_ collectionView: NSCollectionView, mouseExitedAt indexPath: IndexPath) { +// // 호버 효과를 해제하려는 코드 +// if let item = collectionView.item(at: indexPath) as? QuickSlotButtonViewItem, +// let button = item.view.subviews.first as? QuickSlotItemButton { +// button.mouseExited(with: NSEvent()) // 호버 효과 해제 메서드 호출 +// } +// } } extension QuickSlotButtonCollectionViewController { @@ -114,4 +145,5 @@ extension QuickSlotButtonCollectionViewController { // MARK: - Notification Name collectionButtonTapped extension Notification.Name { static let collectionButtonTapped = Notification.Name("collectionButtonTapped") + static let collectionHeaderTapped = Notification.Name("collectionHeaderTapped") } diff --git a/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.xib b/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.xib index 0fda14b..a516887 100644 --- a/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.xib +++ b/Box42/QuickSlot/View/ButtonCollectionView/QuickSlotButtonCollectionViewController.xib @@ -1,8 +1,8 @@ - + - + diff --git a/Box42/QuickSlot/View/ButtonCollectionView/Vertical Item/QuickSlotItemButton.swift b/Box42/QuickSlot/View/ButtonCollectionView/Vertical Item/QuickSlotItemButton.swift new file mode 100644 index 0000000..d225eb7 --- /dev/null +++ b/Box42/QuickSlot/View/ButtonCollectionView/Vertical Item/QuickSlotItemButton.swift @@ -0,0 +1,67 @@ +// +// QuickSlotItemButton.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import AppKit + +class QuickSlotItemButton: NSButton { + + var viewModel = QuickSlotViewModel.shared + + init(_ item: Int) { + super.init(frame: .zero) + self.frame = CGRect(x: 0, y: 0, width: QuickSlotUI.size.button, height: QuickSlotUI.size.button) + let buttonModel = viewModel.buttons[item] + self.title = buttonModel.title + if buttonModel.title == "CleanCache" { + self.image = NSImage(imageLiteralResourceName: "trash") + } else if buttonModel.type == "sh" { + self.image = NSImage(imageLiteralResourceName: "document-text") + } else if buttonModel.type == "pref" { + self.image = NSImage(imageLiteralResourceName: "setting") + } else if buttonModel.type == "default-sh" { + self.image = NSImage(imageLiteralResourceName: "document-text") + } else if buttonModel.type == "default-pref" { + self.image = NSImage(imageLiteralResourceName: "setting") + } + self.target = self + self.action = #selector(mouseEntered) + self.isBordered = false + self.wantsLayer = true + self.layer?.backgroundColor = NSColor.white.cgColor // 흰색 배경 색상 + self.layer?.cornerRadius = 8.0 + self.layer?.zPosition = 1 + let trackingArea = NSTrackingArea( + rect: self.bounds, // 뷰의 경계를 기준으로 할 경우 + options: [.mouseEnteredAndExited, .activeAlways], + owner: self, + userInfo: nil + ) + self.addTrackingArea(trackingArea) + + self.associatedString = buttonModel.path + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func mouseEntered(with event: NSEvent) { + super.mouseEntered(with: event) + + self.wantsLayer = true +// self.layer?.frame.size = CGSize(width: 58.0, height: 58.0) + self.layer?.backgroundColor = NSColor(red: 0.848, green: 0.848, blue: 0.848, alpha: 1).cgColor + } + + override func mouseExited(with event: NSEvent) { + super.mouseExited(with: event) + + wantsLayer = true +// layer?.frame.size = CGSize(width: 58.0, height: 58.0) + layer?.backgroundColor = NSColor.white.cgColor + } +} diff --git a/Box42/QuickSlot/View/ButtonCollectionView/Vertical Item/QuickSlotItemLabel.swift b/Box42/QuickSlot/View/ButtonCollectionView/Vertical Item/QuickSlotItemLabel.swift new file mode 100644 index 0000000..0177e76 --- /dev/null +++ b/Box42/QuickSlot/View/ButtonCollectionView/Vertical Item/QuickSlotItemLabel.swift @@ -0,0 +1,32 @@ +// +// QuickSlotItemLabel.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import AppKit + +class QuickSlotItemLabel: NSTextField { + + var viewModel = QuickSlotViewModel.shared + + init(_ item: Int) { + super.init(frame: .zero) + let buttonModel = viewModel.buttons[item] + + self.stringValue = buttonModel.title + self.font = NSFont(name: "Inter", size: QuickSlotUI.size.font) + self.textColor = NSColor(hex: "#696969") + self.backgroundColor = NSColor.clear + self.isBordered = false + self.alignment = .center + self.isSelectable = false + self.isEditable = false + self.lineBreakMode = .byTruncatingTail + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Box42/QuickSlot/View/QuickSlotGroupView.swift b/Box42/QuickSlot/View/QuickSlotGroupView.swift index 228c26b..6352cbd 100644 --- a/Box42/QuickSlot/View/QuickSlotGroupView.swift +++ b/Box42/QuickSlot/View/QuickSlotGroupView.swift @@ -9,7 +9,7 @@ import AppKit import SnapKit class QuickSlotGroupView: NSView { - + lazy var divider: NSBox = Divider(completion: { [weak self] in self?.dividerAction?() }) lazy var headerView: QuickSlotHeaderView = QuickSlotHeaderView(image: NSImage(imageLiteralResourceName: "star"), completion: { [weak self] in self?.headerAction?() }) lazy var buttonCollectionView: QuickSlotButtonCollectionViewController = QuickSlotButtonCollectionViewController() @@ -30,17 +30,16 @@ class QuickSlotGroupView: NSView { } private func setupViews() { - self.addSubview(divider) +// self.addSubview(divider) self.addSubview(headerView) self.addSubview(buttonCollectionView.view) - } private func setupConstraints() { - divider.snp.makeConstraints { make in - make.top.equalToSuperview() - make.left.right.equalToSuperview() - } +// divider.snp.makeConstraints { make in +// make.top.equalToSuperview() +// make.left.right.equalToSuperview() +// } headerView.snp.makeConstraints { make in make.top.equalToSuperview() diff --git a/Box42/QuickSlot/View/QuickSlotHeaderView.swift b/Box42/QuickSlot/View/QuickSlotHeaderView.swift index 443ed69..da5e6ae 100644 --- a/Box42/QuickSlot/View/QuickSlotHeaderView.swift +++ b/Box42/QuickSlot/View/QuickSlotHeaderView.swift @@ -11,35 +11,49 @@ import SnapKit class QuickSlotHeaderView: NSView { private var callback: (() -> Void)? - private var QuickSlotHeaderButton: NSButton! - private var QuickSlotHeaderLabel: NSTextField! + // private var quickSlotHeaderButton: NSButton! + private var quickSlotIcon: NSImageView! + private var quickSlotHeaderLabel: NSTextField! + private var quickSlotManageButton: NSButton! init(image: NSImage, completion: @escaping () -> Void) { super.init(frame: .zero) - QuickSlotHeaderButton = NSButton(image: image, target: self, action: #selector(pin)) - QuickSlotHeaderButton.isBordered = false - QuickSlotHeaderButton.wantsLayer = true - QuickSlotHeaderButton.layer?.backgroundColor = NSColor.clear.cgColor + quickSlotIcon = NSImageView() + quickSlotIcon.image = image + quickSlotIcon.image?.size = NSSize(width: 18, height: 18) + self.addSubview(quickSlotIcon) - self.addSubview(QuickSlotHeaderButton) + quickSlotHeaderLabel = NSTextField(labelWithString: "퀵슬롯") + quickSlotHeaderLabel.font = NSFont.boldSystemFont(ofSize: 16) + quickSlotHeaderLabel.textColor = NSColor.black + quickSlotHeaderLabel.backgroundColor = NSColor.clear + quickSlotHeaderLabel.isBordered = false + self.addSubview(quickSlotHeaderLabel) - QuickSlotHeaderLabel = NSTextField(labelWithString: "Quick Slot") - QuickSlotHeaderLabel.font = NSFont(name: "Inter", size: QuickSlotUI.size.font) - QuickSlotHeaderLabel.textColor = NSColor(hex: "#696969") - QuickSlotHeaderLabel.backgroundColor = NSColor.clear - QuickSlotHeaderLabel.isBordered = false + let buttonImage = NSImage(named: NSImage.Name("add"))! + buttonImage.size = NSSize(width: 24, height: 24) + quickSlotManageButton = NSButton(image: buttonImage, target: self, action: #selector(btnAction)) + quickSlotManageButton.setButtonType(.momentaryChange) + quickSlotManageButton.bezelStyle = .texturedRounded + quickSlotManageButton.isBordered = false + quickSlotManageButton.wantsLayer = true + quickSlotManageButton.layer?.backgroundColor = NSColor.clear.cgColor + self.addSubview(quickSlotManageButton) - self.addSubview(QuickSlotHeaderLabel) - - QuickSlotHeaderButton.snp.makeConstraints { make in + quickSlotIcon.snp.makeConstraints { make in make.left.equalToSuperview() - make.top.equalToSuperview().offset(13) + make.centerY.equalToSuperview() + } + + quickSlotHeaderLabel.snp.makeConstraints { make in + make.left.equalTo(quickSlotIcon.snp.right).offset(8) + make.centerY.equalToSuperview() } - QuickSlotHeaderLabel.snp.makeConstraints { make in - make.left.equalTo(QuickSlotHeaderButton.snp.right).offset(4) - make.bottom.equalTo(QuickSlotHeaderButton.snp.bottom) + quickSlotManageButton.snp.makeConstraints { make in + make.right.equalToSuperview() + make.centerY.equalToSuperview() } self.callback = completion @@ -49,7 +63,7 @@ class QuickSlotHeaderView: NSView { fatalError("init(coder:) has not been implemented") } - @objc func pin() { + @objc func btnAction() { callback?() } } diff --git a/Box42/QuickSlot/View/Table/QuickSlotCell.swift b/Box42/QuickSlot/View/Table/QuickSlotCell.swift new file mode 100644 index 0000000..dcaaf24 --- /dev/null +++ b/Box42/QuickSlot/View/Table/QuickSlotCell.swift @@ -0,0 +1,116 @@ +// +// QuickSlotCell.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import AppKit +import SnapKit + +class QuickSlotCell: NSTableCellView { + var imageButton: NSButton = NSButton() + var titleLabel: NSTextField = NSTextField() + var pathLabel: NSTextField = NSTextField() + var typeLabel: NSTextField = NSTextField() + var deleteButton: QuickSlotCellDeleteButton = QuickSlotCellDeleteButton() + + var viewModel: QuickSlotViewModel? + var qsButton: QuickSlotButtonModel? + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupUI() { + let labels = [titleLabel, pathLabel, typeLabel] + for label in labels { + label.wantsLayer = true + label.layer?.cornerRadius = 15 + label.layer?.borderColor = NSColor(red: 0.781, green: 0.781, blue: 0.781, alpha: 1).cgColor + label.layer?.borderWidth = 1 + + label.font = NSFont.systemFont(ofSize: 16, weight: .medium) + label.textColor = NSColor.black + } + + addSubview(imageButton) + addSubview(titleLabel) + addSubview(pathLabel) + addSubview(typeLabel) + addSubview(deleteButton) + + imageButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(8) + make.top.equalToSuperview().offset(8) + make.bottom.equalToSuperview().offset(-8) + make.width.equalTo(imageButton.snp.height).multipliedBy(1) + } + + deleteButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-8) + make.centerY.equalToSuperview() + make.width.equalTo(100) + make.height.equalTo(50) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(imageButton.snp.right).offset(8) + make.right.equalTo(deleteButton.snp.left).offset(-8) + make.top.equalTo(imageButton) + make.height.equalTo(30) + } + + pathLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.right.equalTo(deleteButton.snp.left).offset(-8) + make.top.equalTo(titleLabel.snp.bottom).offset(4) + make.height.equalTo(30) + } + + typeLabel.snp.makeConstraints { make in + make.left.equalTo(pathLabel) + make.right.equalTo(deleteButton.snp.left).offset(-8) + make.top.equalTo(pathLabel.snp.bottom).offset(4) + make.height.equalTo(30) + } + } + + func configure(with qsItem: QuickSlotButtonModel, viewModel: QuickSlotViewModel?) { + self.qsButton = qsItem + self.viewModel = viewModel + titleLabel.stringValue = qsItem.title + pathLabel.stringValue = qsItem.path ?? "none" + + if qsItem.title == "CleanCache" { + imageButton.image = NSImage(imageLiteralResourceName: "trash") + } else if qsItem.type == "sh" { + imageButton.image = NSImage(imageLiteralResourceName: "document-text") + } else if qsItem.type == "pref" { + imageButton.image = NSImage(imageLiteralResourceName: "setting") + } else if qsItem.type == "default-sh" { + imageButton.image = NSImage(imageLiteralResourceName: "document-text") + } else if qsItem.type == "default-pref" { + imageButton.image = NSImage(imageLiteralResourceName: "setting") + } + imageButton.isBordered = false + + + deleteButton.target = self + deleteButton.action = #selector(deleteButtonClicked) + } + + @objc func deleteButtonClicked() { + // if let id = qsButton?.scriptUuid { + // viewModel?.removeButton(id) + // } + if let path = qsButton?.path { + viewModel?.removeButton(path) + } + } +} diff --git a/Box42/QuickSlot/View/Table/QuickSlotCellDeleteButton.swift b/Box42/QuickSlot/View/Table/QuickSlotCellDeleteButton.swift new file mode 100644 index 0000000..20d53a9 --- /dev/null +++ b/Box42/QuickSlot/View/Table/QuickSlotCellDeleteButton.swift @@ -0,0 +1,52 @@ +// +// QuickSlotCellDeleteButton.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import AppKit + +class QuickSlotCellDeleteButton: NSButton { + init() { + super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0)) + self.title = "삭제" + let attributes: [NSAttributedString.Key: Any] = [ + .font: NSFont.systemFont(ofSize: 14, weight: .medium), + .foregroundColor: NSColor.white + ] + let attributedTitle = NSAttributedString(string: title, attributes: attributes) + self.attributedTitle = attributedTitle + self.isBordered = false + self.wantsLayer = true + self.layer?.cornerRadius = 15 + self.layer?.backgroundColor = NSColor.red.cgColor + } + + override func layout() { + super.layout() + + for trackingArea in self.trackingAreas { + self.removeTrackingArea(trackingArea) + } + + let trackingArea = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .activeAlways], owner: self, userInfo: nil) + self.addTrackingArea(trackingArea) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func mouseEntered(with event: NSEvent) { + super.mouseEntered(with: event) + + self.layer?.backgroundColor = NSColor(red: 1, green: 0, blue: 0, alpha: 0.5).cgColor + } + + override func mouseExited(with event: NSEvent) { + super.mouseExited(with: event) + + self.layer?.backgroundColor = NSColor.red.cgColor + } +} diff --git a/Box42/QuickSlot/View/Table/QuickSlotCellManager.swift b/Box42/QuickSlot/View/Table/QuickSlotCellManager.swift new file mode 100644 index 0000000..29215c7 --- /dev/null +++ b/Box42/QuickSlot/View/Table/QuickSlotCellManager.swift @@ -0,0 +1,109 @@ +// +// QuickSlotCellManager.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import AppKit +import SnapKit + +// MARK: - 다음 버전에 추가 예정 +class QuickSlotCellManager: NSTableCellView { + var nameLabel: NSTextField = NSTextField() + var descriptionLabel: NSTextField = NSTextField() + var excuteButton: ScriptExcuteButton = ScriptExcuteButton() + var deleteButton: ScriptDeleteButton = ScriptDeleteButton() + var quickSlotButton: ScriptQuickSlotButton = ScriptQuickSlotButton() + + var viewModel: ScriptViewModel? + var script: Script? + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupUI() { + addSubview(nameLabel) + addSubview(descriptionLabel) + addSubview(quickSlotButton) + addSubview(excuteButton) + addSubview(deleteButton) + + nameLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(16) + make.width.lessThanOrEqualTo(200).priority(.high) // 최대 너비와 우선순위 설정 + } + + deleteButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-16) + make.width.equalTo(53) + make.height.equalTo(40) + } + + excuteButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalTo(deleteButton.snp.left).offset(-8) + make.width.equalTo(70) + make.height.equalTo(40) + } + + quickSlotButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalTo(excuteButton.snp.left).offset(-8) + make.width.equalTo(53) + make.height.equalTo(40) + } + + descriptionLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(nameLabel.snp.right).offset(8) + make.right.lessThanOrEqualTo(quickSlotButton.snp.left).offset(-8) + make.width.greaterThanOrEqualTo(100).priority(.low) // 최소 너비와 낮은 우선순위 설정 + } + } + + + + func configure(with script: Script, viewModel: ScriptViewModel?) { + self.script = script + self.viewModel = viewModel + nameLabel.stringValue = script.name + descriptionLabel.stringValue = script.description ?? "description" + + deleteButton.target = self + deleteButton.action = #selector(deleteButtonClicked) + + excuteButton.target = self + excuteButton.action = #selector(excuteButtonClicked) + + quickSlotButton.target = self + quickSlotButton.action = #selector(quickSlotButtonclicked) + + } + + @objc func deleteButtonClicked() { + if let id = script?.scriptUuid { + viewModel?.deleteScript(id: id) + } + } + + @objc func excuteButtonClicked() { + if let path = script?.path { + viewModel?.excuteScript(path: path) + } + } + + @objc func quickSlotButtonclicked() { + if let id = script?.scriptUuid { + viewModel?.quickSlotScript(id: id) + } + } +} diff --git a/Box42/QuickSlot/View/Table/QuickSlotTableView.swift b/Box42/QuickSlot/View/Table/QuickSlotTableView.swift new file mode 100644 index 0000000..bf9f054 --- /dev/null +++ b/Box42/QuickSlot/View/Table/QuickSlotTableView.swift @@ -0,0 +1,89 @@ +// +// QuickSlotTableView.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import AppKit +import SnapKit +import Combine + +class QuickSlotTableView: NSTableView { + var viewModel: QuickSlotViewModel? { + didSet { + print("ViewModel has been set.") + setupBindings() + } + } + + var cancellables: Set = [] + + private func setupBindings() { + print("Setting up QuickSlotTableView...") // 디버깅 로그 + viewModel?.$buttons.sink(receiveValue: { [weak self] newScripts in + print("Received new QuickSlotTableViewModel: \(newScripts)") // 디버깅 로그 + DispatchQueue.main.async { + self?.reloadData() + } + }).store(in: &cancellables) + } + + func setup() { + self.delegate = self + self.dataSource = self + + self.headerView = nil + self.selectionHighlightStyle = .none + + let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("QuickSlots")) + self.addTableColumn(column1) + } +} + +extension QuickSlotTableView: NSTableViewDelegate, NSTableViewDataSource { + func getCellForRow(at row: Int) -> NSView { + guard let viewModel = viewModel else { + return NSView() + } + + if row < viewModel.buttons.count { + return getQuickSlotCell(for: viewModel.buttons[row], viewModel: viewModel) + } else { + return getQuickSlotCellManager() + } + } + + private func getQuickSlotCell(for quickSlotItem: QuickSlotButtonModel, viewModel: QuickSlotViewModel) -> QuickSlotCell { + let cell = QuickSlotCell(frame: .zero) + cell.configure(with: quickSlotItem, viewModel: viewModel) + return cell + } + + private func getQuickSlotCellManager() -> QuickSlotCellManager { + let quickSlotCellManger = QuickSlotCellManager(frame: .zero) + return quickSlotCellManger + } + + func numberOfRows(in tableView: NSTableView) -> Int { + return (viewModel?.buttons.count ?? 0 ) // + 1 + } + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { +// getCellForRow(at: row) + let cellView = getCellForRow(at: row) + + // Remove the top border line for the first row (index 0) + if row == 0 { + let topBorder = CALayer() + topBorder.backgroundColor = NSColor.clear.cgColor + cellView.layer?.addSublayer(topBorder) + } + + return cellView + } + + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + return 120 + } +} diff --git a/Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift b/Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift index 568168d..c0daf9b 100644 --- a/Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift +++ b/Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift @@ -13,30 +13,93 @@ class QuickSlotViewModel { @Published var buttons: [QuickSlotButtonModel] = [] private init() { - let button1 = QuickSlotButtonModel(id: UUID(uuidString: "550e8400-e29b-41d4-a716-446655440000")!, + let button1 = QuickSlotButtonModel(scriptUuid: UUID(uuidString: "37a56076-e72c-4efe-ba7f-de0effe7f4c3f"), title: QuickSlotUI.title.clean, - path: Bundle.main.path(forResource: "cleanCache", ofType: "sh")) - let button2 = QuickSlotButtonModel(title: QuickSlotUI.title.preferences) - let button3 = QuickSlotButtonModel(title: QuickSlotUI.title.scripts) - let button4 = QuickSlotButtonModel(title: QuickSlotUI.title.user) + path: Bundle.main.path(forResource: "cleanCache", ofType: "sh"), + type: "sh" + ) + let button2 = QuickSlotButtonModel(title: QuickSlotUI.title.preferences, + path: "default-preferences", + type: "default-pref") + let button3 = QuickSlotButtonModel(title: QuickSlotUI.title.scripts, + path: "default-scripts", + type: "default-sh") + let button4 = QuickSlotButtonModel(title: QuickSlotUI.title.user, + path: "default-user", + type: "default-pref") buttons = [button1, button2, button3, button4] } + func setUpQuickSlot() { + if let newButtons = UserManager.shared.userProfile?.quickSlotList { + replaceQuickSlot(with: newButtons) + } + } + + // 새로운 스크립트 배열로 교체하는 메소드 + func replaceQuickSlot(with newQuickSlot: [QuickSlotButtonModel]) { + self.buttons = newQuickSlot + } + // 퀵슬롯 안에 해당 버튼이 없으면 추가 func addButton(_ button: QuickSlotButtonModel) { - if !buttons.contains(where: { $0.id == button.id }) { + if !buttons.contains(where: { $0.scriptUuid == button.scriptUuid }) { buttons.append(button) + updateMeQuickSlot() } } func removeButton(_ id: UUID) { - buttons.removeAll { $0.id == id } + if let index = buttons.firstIndex(where: { $0.scriptUuid == id }) { + let buttonToRemove = buttons[index] + if let type = buttonToRemove.type, !type.hasPrefix("default") { + buttons.remove(at: index) + updateMeQuickSlot() + } + } } + func removeButton(_ path: String) { + print("1. Attempting to remove button with path: \(path)") + print("2. Current buttons: \(buttons)") + if let index = buttons.firstIndex(where: { $0.path == path }) { + print("3. Found button at index: \(index)") + let buttonToRemove = buttons[index] + print("4. Button to remove: \(buttonToRemove)") + if let type = buttonToRemove.type { + print("5. Button type: \(type)") + if !type.hasPrefix("default") { + print("6. Removing button...") + buttons.remove(at: index) + updateMeQuickSlot() + } else { + print("Button type starts with 'default'. Skipping removal.") + } + } else { + print("Button type is nil. Skipping removal.") + } + } else { + print("Button not found.") + } + } + func updateButton(id: UUID, newTitle: String) { - if let index = buttons.firstIndex(where: { $0.id == id }) { + if let index = buttons.firstIndex(where: { $0.scriptUuid == id }) { buttons[index].title = newTitle } } + + func updateMeQuickSlot() { + let body = QuickSlotModels(quickSlotList: buttons) + API.putUserMeQuickSlot(quickSlots: body) { result in + switch result { + case .success(_): + print("Successfully updated the scripts.") // 혹은 사용자에게 보여줄 알림 추가 + case .failure(let error): + print("Failed to update scripts: \(error.localizedDescription)") // 혹은 사용자에게 보여줄 알림 추가 + } + } + } } + diff --git a/Box42/Resources/AppDelegate.swift b/Box42/Resources/AppDelegate.swift index 7d2ae45..1f52499 100644 --- a/Box42/Resources/AppDelegate.swift +++ b/Box42/Resources/AppDelegate.swift @@ -22,22 +22,23 @@ class AppDelegate: NSObject, NSApplicationDelegate { iconController = IconController() // alertAccessibility() // hotkey() + self.eventMonitoring() // storage.storageTimerEvent() _ = UserManager.shared - _ = ScriptsLogicController.shared + _ = QuickSlotScriptsLogicController.shared // MARK: - 유저데이터 동기화 WebViewManager.shared.getCookie() API.getUserProfile(WebViewManager.shared.getCookieWebKit) - _ = QuickSlotViewModel.shared - _ = ScriptViewModel.shared // 초기화와 동시에 + _ = ScriptViewModel.shared + _ = BookmarkViewModel.shared } func applicationWillTerminate(_ aNotification: Notification) { // Insert code here to tear down your application } - + func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true } diff --git a/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon 1.png b/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon 1.png new file mode 100644 index 0000000..b5c6cf7 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon 1.png differ diff --git a/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon 2.png b/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon 2.png new file mode 100644 index 0000000..b5c6cf7 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon 2.png differ diff --git a/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon.png b/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon.png new file mode 100644 index 0000000..b5c6cf7 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/42Box icon.png differ diff --git a/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/Contents.json b/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/Contents.json new file mode 100644 index 0000000..65b52ef --- /dev/null +++ b/Box42/Resources/Assets.xcassets/Icons/bookmark-default.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "42Box icon 2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "42Box icon 1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "42Box icon.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon.png b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon 1.png similarity index 100% rename from Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon.png rename to Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon 1.png diff --git a/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon 2.png b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon 2.png new file mode 100644 index 0000000..fa00ba0 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon 2.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon 3.png b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon 3.png new file mode 100644 index 0000000..fa00ba0 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Bookmark icon 3.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Contents.json b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Contents.json index c1d7e62..7c379d1 100644 --- a/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Contents.json +++ b/Box42/Resources/Assets.xcassets/uibuttons/Bookmark.imageset/Contents.json @@ -1,15 +1,17 @@ { "images" : [ { + "filename" : "Bookmark icon 3.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "bookmark icon.png", + "filename" : "Bookmark icon 2.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "Bookmark icon 1.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Box42/Resources/Assets.xcassets/uibuttons/add.imageset/Contents.json b/Box42/Resources/Assets.xcassets/uibuttons/add.imageset/Contents.json new file mode 100644 index 0000000..52e243f --- /dev/null +++ b/Box42/Resources/Assets.xcassets/uibuttons/add.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "add.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Box42/Resources/Assets.xcassets/uibuttons/add.imageset/add.png b/Box42/Resources/Assets.xcassets/uibuttons/add.imageset/add.png new file mode 100644 index 0000000..12a1110 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/add.imageset/add.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/add.png b/Box42/Resources/Assets.xcassets/uibuttons/add.png new file mode 100644 index 0000000..12a1110 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/add.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/document-text.imageset/Contents.json b/Box42/Resources/Assets.xcassets/uibuttons/document-text.imageset/Contents.json new file mode 100644 index 0000000..2072db0 --- /dev/null +++ b/Box42/Resources/Assets.xcassets/uibuttons/document-text.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "document-text.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Box42/Resources/Assets.xcassets/uibuttons/document-text.imageset/document-text.png b/Box42/Resources/Assets.xcassets/uibuttons/document-text.imageset/document-text.png new file mode 100644 index 0000000..ac603c5 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/document-text.imageset/document-text.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/document-text.png b/Box42/Resources/Assets.xcassets/uibuttons/document-text.png new file mode 100644 index 0000000..ac603c5 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/document-text.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/setting.imageset/Contents.json b/Box42/Resources/Assets.xcassets/uibuttons/setting.imageset/Contents.json new file mode 100644 index 0000000..c7225a3 --- /dev/null +++ b/Box42/Resources/Assets.xcassets/uibuttons/setting.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "setting.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Box42/Resources/Assets.xcassets/uibuttons/setting.imageset/setting.png b/Box42/Resources/Assets.xcassets/uibuttons/setting.imageset/setting.png new file mode 100644 index 0000000..5ede7c6 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/setting.imageset/setting.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/setting.png b/Box42/Resources/Assets.xcassets/uibuttons/setting.png new file mode 100644 index 0000000..5ede7c6 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/setting.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/trash.imageset/Contents.json b/Box42/Resources/Assets.xcassets/uibuttons/trash.imageset/Contents.json new file mode 100644 index 0000000..2a3e98a --- /dev/null +++ b/Box42/Resources/Assets.xcassets/uibuttons/trash.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "trash.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Box42/Resources/Assets.xcassets/uibuttons/trash.imageset/trash.png b/Box42/Resources/Assets.xcassets/uibuttons/trash.imageset/trash.png new file mode 100644 index 0000000..f361bf2 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/trash.imageset/trash.png differ diff --git a/Box42/Resources/Assets.xcassets/uibuttons/trash.png b/Box42/Resources/Assets.xcassets/uibuttons/trash.png new file mode 100644 index 0000000..f361bf2 Binary files /dev/null and b/Box42/Resources/Assets.xcassets/uibuttons/trash.png differ diff --git a/Box42/Resources/Info.plist b/Box42/Resources/Info.plist index 4187051..e470c4d 100644 --- a/Box42/Resources/Info.plist +++ b/Box42/Resources/Info.plist @@ -2,8 +2,8 @@ - NSAppleEventsUsageDescription - This app needs access to control System Preferences. + NSAppleEventsUsageDescription + This app needs access to control System Preferences. NSDocumentsFolderUsageDescription 원활한 앱 구동을 위해 유저 디렉토리의 권한을 요청합니다. NSAppTransportSecurity diff --git a/Box42/Scripts/Model/Scripts.swift b/Box42/Scripts/Model/Scripts.swift index 40d59fa..313511f 100644 --- a/Box42/Scripts/Model/Scripts.swift +++ b/Box42/Scripts/Model/Scripts.swift @@ -12,15 +12,15 @@ struct Scripts: Codable { } struct Script: Codable { - var id: UUID? + var scriptUuid: UUID? var name: String - var description: String + var description: String? var path: String var savedId: Int? var userUuid: String? - init(id: UUID = UUID(), name: String, description: String, path: String, savedId: Int, userUuid: String?) { - self.id = id + init(scriptUuid: UUID?, name: String, description: String?, path: String, savedId: Int?, userUuid: String?) { + self.scriptUuid = scriptUuid self.name = name self.description = description self.path = path diff --git a/Box42/Scripts/View/Button/ScriptDeleteButton.swift b/Box42/Scripts/View/Button/ScriptDeleteButton.swift index 3d592c8..26a9f5a 100644 --- a/Box42/Scripts/View/Button/ScriptDeleteButton.swift +++ b/Box42/Scripts/View/Button/ScriptDeleteButton.swift @@ -13,10 +13,16 @@ class ScriptDeleteButton: NSButton { super.init(frame: NSRect(x: 0, y: 0, width: 53, height: 40)) self.title = "삭제" + let attributes: [NSAttributedString.Key: Any] = [ + .font: NSFont.systemFont(ofSize: 13, weight: .medium), + .foregroundColor: NSColor.white + ] + let attributedTitle = NSAttributedString(string: title, attributes: attributes) + self.attributedTitle = attributedTitle self.isBordered = false self.wantsLayer = true - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius - self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + self.layer?.cornerRadius = 15 + self.layer?.backgroundColor = NSColor.red.cgColor let trackingArea = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .activeAlways], owner: self, userInfo: nil) self.addTrackingArea(trackingArea) @@ -28,41 +34,13 @@ class ScriptDeleteButton: NSButton { override func mouseEntered(with event: NSEvent) { super.mouseEntered(with: event) - - let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") - bgColorAnimation.fromValue = WindowButtonUI.color.opacityWhite - bgColorAnimation.toValue = WindowButtonUI.color.close - bgColorAnimation.duration = WindowButtonUI.animation.duration - - let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") - cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius - cornerAnimation.toValue = WindowButtonUI.size.cornerRadius / 2 - cornerAnimation.duration = WindowButtonUI.animation.duration - - self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") - self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") - - self.layer?.backgroundColor = WindowButtonUI.color.close - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius / 2 + + self.layer?.backgroundColor = NSColor.red.withAlphaComponent(0.5).cgColor } override func mouseExited(with event: NSEvent) { super.mouseExited(with: event) - - let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") - bgColorAnimation.fromValue = WindowButtonUI.color.close - bgColorAnimation.toValue = WindowButtonUI.color.opacityWhite - bgColorAnimation.duration = WindowButtonUI.animation.duration - let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") - cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius / 2 - cornerAnimation.toValue = WindowButtonUI.size.cornerRadius - cornerAnimation.duration = WindowButtonUI.animation.duration - - self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") - self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") - - self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + self.layer?.backgroundColor = NSColor.red.cgColor } } diff --git a/Box42/Scripts/View/Button/ScriptExcuteButton.swift b/Box42/Scripts/View/Button/ScriptExcuteButton.swift index e6d3b0c..54f4969 100644 --- a/Box42/Scripts/View/Button/ScriptExcuteButton.swift +++ b/Box42/Scripts/View/Button/ScriptExcuteButton.swift @@ -13,10 +13,16 @@ class ScriptExcuteButton: NSButton { super.init(frame: NSRect(x: 0, y: 0, width: 70, height: 40)) self.title = "실행하기" + let attributes: [NSAttributedString.Key: Any] = [ + .font: NSFont.systemFont(ofSize: 13, weight: .medium), + .foregroundColor: NSColor.white + ] + let attributedTitle = NSAttributedString(string: title, attributes: attributes) + self.attributedTitle = attributedTitle self.isBordered = false self.wantsLayer = true - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius - self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + self.layer?.cornerRadius = 15 + self.layer?.backgroundColor = NSColor(hex: "#008000").cgColor let trackingArea = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .activeAlways], owner: self, userInfo: nil) self.addTrackingArea(trackingArea) @@ -29,40 +35,12 @@ class ScriptExcuteButton: NSButton { override func mouseEntered(with event: NSEvent) { super.mouseEntered(with: event) - let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") - bgColorAnimation.fromValue = WindowButtonUI.color.opacityWhite - bgColorAnimation.toValue = WindowButtonUI.color.maximize - bgColorAnimation.duration = WindowButtonUI.animation.duration - - let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") - cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius - cornerAnimation.toValue = WindowButtonUI.size.cornerRadius / 2 - cornerAnimation.duration = WindowButtonUI.animation.duration - - self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") - self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") - - self.layer?.backgroundColor = WindowButtonUI.color.maximize - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius / 2 + self.layer?.backgroundColor = NSColor(hex: "#008000").withAlphaComponent(0.5).cgColor } override func mouseExited(with event: NSEvent) { super.mouseExited(with: event) - let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") - bgColorAnimation.fromValue = WindowButtonUI.color.maximize - bgColorAnimation.toValue = WindowButtonUI.color.opacityWhite - bgColorAnimation.duration = WindowButtonUI.animation.duration - - let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") - cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius / 2 - cornerAnimation.toValue = WindowButtonUI.size.cornerRadius - cornerAnimation.duration = WindowButtonUI.animation.duration - - self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") - self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") - - self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + self.layer?.backgroundColor = NSColor(hex: "#008000").cgColor } } diff --git a/Box42/Scripts/View/Button/ScriptQuickSlotButton.swift b/Box42/Scripts/View/Button/ScriptQuickSlotButton.swift index e2351d7..7e3268c 100644 --- a/Box42/Scripts/View/Button/ScriptQuickSlotButton.swift +++ b/Box42/Scripts/View/Button/ScriptQuickSlotButton.swift @@ -13,10 +13,16 @@ class ScriptQuickSlotButton: NSButton { super.init(frame: NSRect(x: 0, y: 0, width: 53, height: 40)) self.title = "퀵슬롯" + let attributes: [NSAttributedString.Key: Any] = [ + .font: NSFont.systemFont(ofSize: 13, weight: .medium), + .foregroundColor: NSColor.white + ] + let attributedTitle = NSAttributedString(string: title, attributes: attributes) + self.attributedTitle = attributedTitle self.isBordered = false self.wantsLayer = true - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius - self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite + self.layer?.cornerRadius = 15 + self.layer?.backgroundColor = NSColor(hex: "#FFCE51").cgColor let trackingArea = NSTrackingArea(rect: self.bounds, options: [.mouseEnteredAndExited, .activeAlways], owner: self, userInfo: nil) self.addTrackingArea(trackingArea) @@ -29,41 +35,13 @@ class ScriptQuickSlotButton: NSButton { override func mouseEntered(with event: NSEvent) { super.mouseEntered(with: event) - let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") - bgColorAnimation.fromValue = WindowButtonUI.color.opacityWhite - bgColorAnimation.toValue = WindowButtonUI.color.minimize - bgColorAnimation.duration = WindowButtonUI.animation.duration - - let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") - cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius - cornerAnimation.toValue = WindowButtonUI.size.cornerRadius / 2 - cornerAnimation.duration = WindowButtonUI.animation.duration - - self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") - self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") - - self.layer?.backgroundColor = WindowButtonUI.color.minimize - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius / 2 + self.layer?.backgroundColor = NSColor(hex: "#FFCE51").withAlphaComponent(0.5).cgColor } override func mouseExited(with event: NSEvent) { super.mouseExited(with: event) - let bgColorAnimation = CABasicAnimation(keyPath: "backgroundColor") - bgColorAnimation.fromValue = WindowButtonUI.color.minimize - bgColorAnimation.toValue = WindowButtonUI.color.opacityWhite - bgColorAnimation.duration = WindowButtonUI.animation.duration - - let cornerAnimation = CABasicAnimation(keyPath: "cornerRadius") - cornerAnimation.fromValue = WindowButtonUI.size.cornerRadius / 2 - cornerAnimation.toValue = WindowButtonUI.size.cornerRadius - cornerAnimation.duration = WindowButtonUI.animation.duration - - self.layer?.add(bgColorAnimation, forKey: "backgroundColorAnimation") - self.layer?.add(cornerAnimation, forKey: "cornerRadiusAnimation") - - self.layer?.backgroundColor = WindowButtonUI.color.opacityWhite - self.layer?.cornerRadius = WindowButtonUI.size.cornerRadius + self.layer?.backgroundColor = NSColor(hex: "#FFCE51").cgColor } override func mouseDown(with event: NSEvent) { diff --git a/Box42/Scripts/View/Table/ScriptCell.swift b/Box42/Scripts/View/Table/ScriptCell.swift index 57d421b..b7f5b0b 100644 --- a/Box42/Scripts/View/Table/ScriptCell.swift +++ b/Box42/Scripts/View/Table/ScriptCell.swift @@ -28,6 +28,17 @@ class ScriptCell: NSTableCellView { } private func setupUI() { + let labels = [nameLabel, descriptionLabel] + for label in labels { + label.wantsLayer = true + label.layer?.cornerRadius = 15 + label.layer?.borderColor = NSColor(red: 0.781, green: 0.781, blue: 0.781, alpha: 1).cgColor + label.layer?.borderWidth = 1 + + label.font = NSFont.systemFont(ofSize: 16, weight: .medium) + label.textColor = NSColor.black + } + addSubview(nameLabel) addSubview(descriptionLabel) addSubview(quickSlotButton) @@ -38,13 +49,24 @@ class ScriptCell: NSTableCellView { make.centerY.equalToSuperview() make.left.equalToSuperview().offset(16) make.width.lessThanOrEqualTo(200).priority(.high) // 최대 너비와 우선순위 설정 + make.height.equalTo(30) + } + + descriptionLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(nameLabel.snp.right).offset(8) + make.right.lessThanOrEqualTo(quickSlotButton.snp.left).offset(-8) + make.width.greaterThanOrEqualTo(100).priority(.low) // 최소 너비와 낮은 우선순위 설정 + make.height.equalTo(30) } + deleteButton.snp.makeConstraints { make in make.centerY.equalToSuperview() make.right.equalToSuperview().offset(-16) make.width.equalTo(53) make.height.equalTo(40) + } excuteButton.snp.makeConstraints { make in @@ -60,13 +82,6 @@ class ScriptCell: NSTableCellView { make.width.equalTo(53) make.height.equalTo(40) } - - descriptionLabel.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.left.equalTo(nameLabel.snp.right).offset(8) - make.right.lessThanOrEqualTo(quickSlotButton.snp.left).offset(-8) - make.width.greaterThanOrEqualTo(100).priority(.low) // 최소 너비와 낮은 우선순위 설정 - } } @@ -75,7 +90,7 @@ class ScriptCell: NSTableCellView { self.script = script self.viewModel = viewModel nameLabel.stringValue = script.name - descriptionLabel.stringValue = script.description + descriptionLabel.stringValue = script.description ?? "description" deleteButton.target = self deleteButton.action = #selector(deleteButtonClicked) @@ -89,35 +104,34 @@ class ScriptCell: NSTableCellView { } @objc func deleteButtonClicked() { - if let id = script?.id { + if let id = script?.scriptUuid { viewModel?.deleteScript(id: id) } } @objc func excuteButtonClicked() { - if let id = script?.id { - viewModel?.excuteScript(id: id) + if let path = script?.path { + viewModel?.excuteScript(path: path) } } - // script 내부 클릭시 1차 실행 - // 있는거면 지우고 없는거면 추가 @objc func quickSlotButtonclicked() { - guard let id = script?.id else { + guard let path = script?.path else { return } - let alreadyExists = QuickSlotViewModel.shared.buttons.contains { $0.id == id } + let alreadyExists = QuickSlotViewModel.shared.buttons.contains { $0.path == path } if alreadyExists { - QuickSlotViewModel.shared.removeButton(id) + QuickSlotViewModel.shared.removeButton(path) quickSlotButton.title = "퀵슬롯" } else { if QuickSlotViewModel.shared.buttons.count > 7 { return } else { quickSlotButton.title = "저장됨" - viewModel?.quickSlotScript(id: id) +// viewModel?.quickSlotScript(id: id) + viewModel?.quickSlotScript(path: path) } } } diff --git a/Box42/Scripts/View/Table/ScriptCellManager.swift b/Box42/Scripts/View/Table/ScriptCellManager.swift index b471493..c3aa555 100644 --- a/Box42/Scripts/View/Table/ScriptCellManager.swift +++ b/Box42/Scripts/View/Table/ScriptCellManager.swift @@ -8,16 +8,14 @@ import AppKit import SnapKit -// MARK: - 다음 버전에 추가 예정 class ScriptCellManager: NSTableCellView { var nameLabel: NSTextField = NSTextField() var descriptionLabel: NSTextField = NSTextField() - var excuteButton: ScriptExcuteButton = ScriptExcuteButton() var deleteButton: ScriptDeleteButton = ScriptDeleteButton() var quickSlotButton: ScriptQuickSlotButton = ScriptQuickSlotButton() - var viewModel: ScriptViewModel? - var script: Script? + var viewModel: BookmarkViewModel? + var urlitem: URLItem? override init(frame frameRect: NSRect) { super.init(frame: frameRect) @@ -32,13 +30,12 @@ class ScriptCellManager: NSTableCellView { addSubview(nameLabel) addSubview(descriptionLabel) addSubview(quickSlotButton) - addSubview(excuteButton) addSubview(deleteButton) nameLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.left.equalToSuperview().offset(16) - make.width.lessThanOrEqualTo(200).priority(.high) // 최대 너비와 우선순위 설정 + make.width.lessThanOrEqualTo(200).priority(.high) } deleteButton.snp.makeConstraints { make in @@ -48,16 +45,9 @@ class ScriptCellManager: NSTableCellView { make.height.equalTo(40) } - excuteButton.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.right.equalTo(deleteButton.snp.left).offset(-8) - make.width.equalTo(70) - make.height.equalTo(40) - } - quickSlotButton.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.right.equalTo(excuteButton.snp.left).offset(-8) + make.right.equalTo(deleteButton.snp.left).offset(-8) make.width.equalTo(53) make.height.equalTo(40) } @@ -72,38 +62,50 @@ class ScriptCellManager: NSTableCellView { - func configure(with script: Script, viewModel: ScriptViewModel?) { - self.script = script + func configure(with urlitem: URLItem, viewModel: BookmarkViewModel?) { + self.urlitem = urlitem self.viewModel = viewModel - nameLabel.stringValue = script.name - descriptionLabel.stringValue = script.description + nameLabel.stringValue = urlitem.name + descriptionLabel.stringValue = urlitem.url deleteButton.target = self deleteButton.action = #selector(deleteButtonClicked) - excuteButton.target = self - excuteButton.action = #selector(excuteButtonClicked) - quickSlotButton.target = self quickSlotButton.action = #selector(quickSlotButtonclicked) } @objc func deleteButtonClicked() { - if let id = script?.id { - viewModel?.deleteScript(id: id) - } +// if let id = script?.scriptUuid { +// viewModel?.deleteScript(id: id) +// } } @objc func excuteButtonClicked() { - if let id = script?.id { - viewModel?.excuteScript(id: id) - } +// if let path = script?.path { +// viewModel?.excuteScript(path: path) +// } } @objc func quickSlotButtonclicked() { - if let id = script?.id { - viewModel?.quickSlotScript(id: id) - } +// guard let path = script?.path else { +// return +// } +// +// let alreadyExists = QuickSlotViewModel.shared.buttons.contains { $0.path == path } +// +// if alreadyExists { +// QuickSlotViewModel.shared.removeButton(path) +// quickSlotButton.title = "퀵슬롯" +// } else { +// if QuickSlotViewModel.shared.buttons.count > 7 { +// return +// } else { +// quickSlotButton.title = "저장됨" +//// viewModel?.quickSlotScript(id: id) +// viewModel?.quickSlotScript(path: path) +// } +// } } } diff --git a/Box42/Scripts/View/Table/ScriptsTableView.swift b/Box42/Scripts/View/Table/ScriptsTableView.swift index ccb1343..6d1f3ab 100644 --- a/Box42/Scripts/View/Table/ScriptsTableView.swift +++ b/Box42/Scripts/View/Table/ScriptsTableView.swift @@ -32,10 +32,9 @@ class ScriptsTableView: NSTableView { func setup() { self.delegate = self self.dataSource = self - + self.headerView = nil + self.backgroundColor = .white let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Scripts")) - column1.width = 100.0 - column1.title = "Scripts" self.addTableColumn(column1) } } diff --git a/Box42/Scripts/ViewModel/ScriptsViewModel.swift b/Box42/Scripts/ViewModel/ScriptsViewModel.swift index 04a0a37..a2f2a51 100644 --- a/Box42/Scripts/ViewModel/ScriptsViewModel.swift +++ b/Box42/Scripts/ViewModel/ScriptsViewModel.swift @@ -15,59 +15,85 @@ class ScriptViewModel: NSObject { private override init() { self.scripts = [ - Script(name: "cleanCache", + Script(scriptUuid: UUID(uuidString: "37a56076-e72c-4efe-ba7f-de0effe7f4c3"), + name: "CleanCache", description: "Cleaning cache", path: Bundle.main.path(forResource: "cleanCache", ofType: "sh") ?? "", savedId: -1 , userUuid: nil), -// Script(name: "brewInGoinfre", -// description: "Brew download in goinfre", -// path: Bundle.main.path(forResource: "brewInGoinfre", ofType: "sh") ?? "", savedId: -1, userUuid: nil), -// Script(name: "exportMacOSInfo", -// description: "export setting MacOS Info", -// path: Bundle.main.path(forResource: "exportMacOSInfo", ofType: "sh") ?? "", savedId: -1, userUuid: nil), -// Script(name: "importMacOSInfo", -// description: "import MacOS Info", -// path: Bundle.main.path(forResource: "importMacOSInfo", ofType: "sh") ?? "", savedId: -1, userUuid: nil), -// Script(name: "key Mapping", -// description: "key Mapping", -// path: Bundle.main.path(forResource: "keyMapping", ofType: "sh") ?? "", savedId: -1, userUuid: nil), -// Script(name: "nodeInstall", -// description: "node Install", -// path: Bundle.main.path(forResource: "nodeInstall", ofType: "sh") ?? "", savedId: -1, userUuid: nil) + // Script(name: "brewInGoinfre", + // description: "Brew download in goinfre", + // path: Bundle.main.path(forResource: "brewInGoinfre", ofType: "sh") ?? "", savedId: -1, userUuid: nil), + // Script(name: "exportMacOSInfo", + // description: "export setting MacOS Info", + // path: Bundle.main.path(forResource: "exportMacOSInfo", ofType: "sh") ?? "", savedId: -1, userUuid: nil), + // Script(name: "importMacOSInfo", + // description: "import MacOS Info", + // path: Bundle.main.path(forResource: "importMacOSInfo", ofType: "sh") ?? "", savedId: -1, userUuid: nil), + // Script(name: "key Mapping", + // description: "key Mapping", + // path: Bundle.main.path(forResource: "keyMapping", ofType: "sh") ?? "", savedId: -1, userUuid: nil), + // Script(name: "nodeInstall", + // description: "node Install", + // path: Bundle.main.path(forResource: "nodeInstall", ofType: "sh") ?? "", savedId: -1, userUuid: nil) ] API.initializeUserMeScripts(WebViewManager.shared.getCookieWebKit) } // Create - func addScript(id: UUID = UUID(), name: String, description: String, path: String, savedId: Int, userUuid: String) { - let newScript = Script(id: UUID(), name: name, description: description, path: path, savedId: savedId, userUuid: userUuid) + func addScript(scriptUuid: UUID?, name: String, description: String?, path: String, savedId: Int?, userUuid: String?) { + let newScript = Script(scriptUuid: scriptUuid, name: name, description: description, path: path, savedId: savedId, userUuid: userUuid) scripts.append(newScript) } + // // Read + // func excuteScript(id: UUID) { + // if let index = scripts.firstIndex(where: { $0.scriptUuid == id }) { + // // MARK: - 파일스크립트 매니저에서 권한을 얻은 실행으로 실행합니다. + // ScriptsFileManager.downloadFile(from: "https://42box.kr/" + scripts[index].path) + // } + // } + // Read - func excuteScript(id: UUID) { - if let index = scripts.firstIndex(where: { $0.id == id }) { -// ExecuteScripts.executeShellScript(path: scripts[index].name) + func excuteScript(path: String) { + if scripts.firstIndex(where: { $0.path == path }) != nil { // MARK: - 파일스크립트 매니저에서 권한을 얻은 실행으로 실행합니다. - SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: scripts[index].path) + ScriptsFileManager.downloadFile(from: "https://42box.kr/" + path) } } // Update func updateScript(id: UUID, newName: String, newDescription: String) { - if let index = scripts.firstIndex(where: { $0.id == id }) { + if let index = scripts.firstIndex(where: { $0.scriptUuid == id }) { scripts[index].name = newName scripts[index].description = newDescription } } // Delete - func deleteScript(id: UUID) { - if let script = scripts.first(where: { $0.id == id }) { + func deleteScript(id: UUID?) { + if let script = scripts.first(where: { $0.scriptUuid == id }) { API.deleteUserMeScripts(WebViewManager.shared.getCookieWebKit, savedId: script.savedId!) { result in switch result { case .success(_): - self.scripts.removeAll(where: { $0.id == id }) - QuickSlotViewModel.shared.removeButton(id) + self.scripts.removeAll(where: { $0.scriptUuid == id }) + if let scriptUuid = id { + QuickSlotViewModel.shared.removeButton(scriptUuid) + } + + case .failure(let error): + print("Failed to delete script: \(error)") + } + } + } + } + + // Delete + func deleteScript(path: String) { + if let script = scripts.first(where: { $0.path == path }) { + API.deleteUserMeScripts(WebViewManager.shared.getCookieWebKit, savedId: script.savedId!) { result in + switch result { + case .success(_): + self.scripts.removeAll(where: { $0.path == path }) + QuickSlotViewModel.shared.removeButton(path) case .failure(let error): print("Failed to delete script: \(error)") @@ -83,13 +109,21 @@ class ScriptViewModel: NSObject { // VM class 시작시 최초 1회 실행되는 메소드 func setupScripts(with newScripts: [Script]) { - self.scripts += newScripts + self.scripts = newScripts } // 스크립트안에서 해당하는 스크립트를 찾아서 quickslotVM에 추가 func quickSlotScript(id: UUID) { - if let index = scripts.firstIndex(where: { $0.id == id }) { - let button = QuickSlotButtonModel(id: id, title: scripts[index].name, path: scripts[index].path) + if let index = scripts.firstIndex(where: { $0.scriptUuid == id }) { + let button = QuickSlotButtonModel(scriptUuid: id, title: scripts[index].name, path: scripts[index].path) + QuickSlotViewModel.shared.addButton(button) + } + } + + // 스크립트안에서 해당하는 스크립트를 찾아서 quickslotVM에 추가 + func quickSlotScript(path: String) { + if let index = scripts.firstIndex(where: { $0.path == path }) { + let button = QuickSlotButtonModel(scriptUuid: scripts[index].scriptUuid, title: scripts[index].name, path: scripts[index].path) QuickSlotViewModel.shared.addButton(button) } } diff --git a/Box42/Shared/API/API.swift b/Box42/Shared/API/API.swift index 5d709ef..c4fbde4 100644 --- a/Box42/Shared/API/API.swift +++ b/Box42/Shared/API/API.swift @@ -67,4 +67,28 @@ class API { task.resume() } + // PUT + static func putDataFromAPI(withURL urlString: String, completion: @escaping (Result) -> Void) { + + let url = URL(string: urlString)! + var request = URLRequest(url: url) + request.httpMethod = "PUT" + request.httpShouldHandleCookies = true + + let task = URLSession.shared.dataTask(with: request) { (data, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 { + completion(.failure(NSError(domain: "InvalidStatusCode", code: httpResponse.statusCode, userInfo: nil))) + return + } + completion(.success(data)) + } + task.resume() + } + + } diff --git a/Box42/Shared/API/GetUserMeQuickSlot.swift b/Box42/Shared/API/GetUserMeQuickSlot.swift new file mode 100644 index 0000000..c4481bc --- /dev/null +++ b/Box42/Shared/API/GetUserMeQuickSlot.swift @@ -0,0 +1,8 @@ +// +// GetUserMeQuickSlot.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import Foundation diff --git a/Box42/Shared/API/GetUserMeScripts.swift b/Box42/Shared/API/GetUserMeScripts.swift index cd09521..05e1348 100644 --- a/Box42/Shared/API/GetUserMeScripts.swift +++ b/Box42/Shared/API/GetUserMeScripts.swift @@ -42,7 +42,7 @@ extension API { fetchDataFromAPI(withURL: "https://api.42box.kr/user-service/users/me/scripts", forType: [Script].self) { (result: Result<[Script], Error>) in switch result { case .success(let scripts): - print(">> MacOS Get :", scripts) + print(">> Initalize Script MacOS Get :", scripts) DispatchQueue.main.async { ScriptViewModel.shared.setupScripts(with: scripts) } diff --git a/Box42/Shared/API/GetUserProfile.swift b/Box42/Shared/API/GetUserProfile.swift index f029f45..5188273 100644 --- a/Box42/Shared/API/GetUserProfile.swift +++ b/Box42/Shared/API/GetUserProfile.swift @@ -21,8 +21,10 @@ extension API { fetchDataFromAPI(withURL: "https://api.42box.kr/user-service/users/me", forType: UserProfile.self) { (result: Result) in switch result { case .success(let userProfile): - print(">> MacOS Get :", userProfile) + print(">> User MacOS Get :", userProfile) UserManager.shared.updateUserProfile(newProfile: userProfile) + QuickSlotViewModel.shared.setUpQuickSlot() + BookmarkViewModel.shared.replaceBookMarkList(with: userProfile.urlList) case .failure(let error): print("Error: \(error)") } diff --git a/Box42/Shared/API/PutUserMeQuickSlot.swift b/Box42/Shared/API/PutUserMeQuickSlot.swift new file mode 100644 index 0000000..918561f --- /dev/null +++ b/Box42/Shared/API/PutUserMeQuickSlot.swift @@ -0,0 +1,50 @@ +// +// PutUserMeQuickSlot.swift +// Box42 +// +// Created by Chanhee Kim on 9/3/23. +// + +import WebKit + +extension API { + // MARK: - Scripts PUT: https://api.42box.kr/user-service/users/me/quick-slot + // TODO: refactoring 필수 + static func putUserMeQuickSlot(quickSlots: QuickSlotModels, completion: @escaping (Result) -> Void) { + WebViewManager.shared.storageSetCookie() + + let url = "https://api.42box.kr/user-service/users/me/quick-slot" + + var request = URLRequest(url: URL(string: url)!) + request.httpMethod = "PUT" + request.httpShouldHandleCookies = true + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + // Scripts 객체를 JSON 데이터로 인코딩 + print(quickSlots) + do { + let jsonData = try JSONEncoder().encode(quickSlots) + request.httpBody = jsonData + print(request.httpBody!) + } catch { + print("Failed to encode scripts: \(error)") + completion(.failure(error)) + return + } + + let task = URLSession.shared.dataTask(with: request) { (data, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 { + completion(.failure(NSError(domain: "InvalidStatusCode", code: httpResponse.statusCode, userInfo: nil))) + return + } + completion(.success(())) + } + task.resume() + } +} + diff --git a/Box42/Shared/API/PutUserMeUrlList.swift b/Box42/Shared/API/PutUserMeUrlList.swift new file mode 100644 index 0000000..695fe4a --- /dev/null +++ b/Box42/Shared/API/PutUserMeUrlList.swift @@ -0,0 +1,50 @@ +// +// PutUserMeUrlList.swift +// Box42 +// +// Created by Chanhee Kim on 9/5/23. +// + +import WebKit + +extension API { + // MARK: - Scripts PUT: https://api.42box.kr/user-service/users/me/url-list + // TODO: refactoring 필수 + static func putUserMeUrlList(urlList: URLList, completion: @escaping (Result) -> Void) { + + WebViewManager.shared.storageSetCookie() + + let url = "https://api.42box.kr/user-service/users/me/url-list" + + var request = URLRequest(url: URL(string: url)!) + request.httpMethod = "PUT" + request.httpShouldHandleCookies = true + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + // Scripts 객체를 JSON 데이터로 인코딩 + print(urlList) + do { + let jsonData = try JSONEncoder().encode(urlList) + request.httpBody = jsonData + print(request.httpBody!) + } catch { + print("Failed to encode scripts: \(error)") + completion(.failure(error)) + return + } + + let task = URLSession.shared.dataTask(with: request) { (data, response, error) in + if let error = error { + completion(.failure(error)) + return + } + + if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 { + completion(.failure(NSError(domain: "InvalidStatusCode", code: httpResponse.statusCode, userInfo: nil))) + return + } + completion(.success(())) + } + task.resume() + } +} diff --git a/Box42/Shared/User/UserManager.swift b/Box42/Shared/User/UserManager.swift index ec61323..18e4677 100644 --- a/Box42/Shared/User/UserManager.swift +++ b/Box42/Shared/User/UserManager.swift @@ -9,7 +9,7 @@ import Foundation class UserManager { static let shared = UserManager() - private var userProfile: UserProfile? { + var userProfile: UserProfile? { didSet { NotificationCenter.default.post(name: .didUpdateUserProfile, object: nil) } diff --git a/Box42/Shared/User/UserProfile.swift b/Box42/Shared/User/UserProfile.swift index ce4e8a8..40c984d 100644 --- a/Box42/Shared/User/UserProfile.swift +++ b/Box42/Shared/User/UserProfile.swift @@ -19,11 +19,6 @@ struct UserProfile: Codable { let quickSlotList: [QuickSlotButtonModel] } -struct URLItem: Codable { - let name: String - let url: String -} - extension UserProfile { static func defaultProfile() -> UserProfile { return UserProfile( @@ -31,22 +26,11 @@ extension UserProfile { nickname: "fox", theme: 0, icon: "fox", - urlList: [URLItem(name: "home", url: "https://42box.kr/"), - URLItem(name: "23Coaltheme", url: "https://42box.github.io/front-end/"), - URLItem(name: "loopback", url: "http://127.0.0.1:3000/"), - URLItem(name: "Box 42", url: "https://42box.github.io/front-end/#/box"), - URLItem(name: "Intra 42", url: "https://intra.42.fr"), - URLItem(name: "Jiphyeonjeon", url: "https://42library.kr"), - URLItem(name: "42STAT", url: "https://stat.42seoul.kr/home"), - URLItem(name: "24Hane", url: "https://24hoursarenotenough.42seoul.kr"), - URLItem(name: "80kCoding", url: "https://80000coding.oopy.io"), - URLItem(name: "where42", url: "https://www.where42.kr"), - URLItem(name: "cabi", url: "https://cabi.42seoul.io/"), - URLItem(name: "42gg", url: "https://42gg.kr/")], + urlList: BookmarkViewModel.shared.bookMarkList, statusMessage: "hello 42Box!", profileImageUrl: "https://42box.kr/user_profile_image/a52671f9-fca9-43ad-b0c0-1c5360831cf2.png", profileImagePath: "user_profile_image/a52671f9-fca9-43ad-b0c0-1c5360831cf2.png", - quickSlotList: [ QuickSlotButtonModel(id: UUID(uuidString: "550e8400-e29b-41d4-a716-446655440000")!, title: "cleanCache", path: Bundle.main.path(forResource: "cleanCache", ofType: "sh")), + quickSlotList: [ QuickSlotButtonModel(scriptUuid: UUID(uuidString: "37a56076-e72c-4efe-ba7f-de0effe7f4c3")!, title: "cleanCache", path: Bundle.main.path(forResource: "cleanCache", ofType: "sh")), ] ) } diff --git a/Box42/Toolbar/Controller/ToolbarViewController.swift b/Box42/Toolbar/Controller/ToolbarViewController.swift index c5df281..18f0ddc 100644 --- a/Box42/Toolbar/Controller/ToolbarViewController.swift +++ b/Box42/Toolbar/Controller/ToolbarViewController.swift @@ -19,14 +19,82 @@ class ToolbarViewController: NSViewController { self.view = toolbarViewGroup } - + var baseContainerVC: BoxBaseContainerViewController? + var toolbarViewGroup: BoxToolbarViewGroup? override func viewDidLoad() { super.viewDidLoad() - // Do view setup here. + + toolbarViewGroup = BoxToolbarViewGroup() + toolbarViewGroup?.sidebar = sidebar + } + // func runPrefsHelperApplication() { + // let prefsHelperAppPath = "/Users/daskim/Downloads/prefsHelper.app" // prefsHelper.app의 경로 + // + // let appURL = URL(fileURLWithPath: prefsHelperAppPath) + // + // let workspace = NSWorkspace.shared + // do { + // try workspace.open([appURL], withAppBundleIdentifier: nil, options: [], additionalEventParamDescriptor: nil, launchIdentifiers: nil) + // } catch { + // print("Error opening app: \(error)") + // } + // } + + lazy var sidebarLeading: SideBarLeading = SideBarLeading(image: NSImage(imageLiteralResourceName: "toggle-on"), completion: { [weak self] in self?.sidebar() }) + lazy var pinButton: PinButtonView = PinButtonView(title: "", image: NSImage(imageLiteralResourceName: "pin-box"), completion: { StateManager.shared.togglePin() + + let newImage: NSImage + if StateManager.shared.pin { + newImage = NSImage(imageLiteralResourceName: "pin-box-ver") + } else { + newImage = NSImage(imageLiteralResourceName: "pin-box") + } + + self.pinButton.changePinImage(to: newImage) + }) + func sidebar() { - print("sidebar") + // print("sidebar") + // BookmarkViewModel.shared.addBookmark(item: URLItem(name: "chan", url: "https://42box.kr/")) + toolbarViewGroup = BoxToolbarViewGroup() + if let baseContainerVC = baseContainerVC { + baseContainerVC.leftView.isHidden.toggle() + + if baseContainerVC.leftView.isHidden { + baseContainerVC.contentGroup.snp.remakeConstraints { make in + make.top.bottom.trailing.equalToSuperview().inset(12) + make.leading.equalToSuperview().offset(24 + 24) + } + + baseContainerVC.view.addSubview(sidebarLeading) + sidebarLeading.snp.makeConstraints { make in + make.top.equalToSuperview().inset(63) + make.leading.equalToSuperview().inset(12) + make.width.equalTo(24) + make.height.equalTo(24) + } + + baseContainerVC.view.addSubview(pinButton) + pinButton.snp.makeConstraints { make in + make.leading.equalToSuperview().inset(10) + make.left.bottom.equalTo(baseContainerVC.leftView) + make.width.equalTo(FunctionButtonUI.size.pinWidth) + make.height.equalTo(FunctionButtonUI.size.pinHeight) + } + } else { + baseContainerVC.contentGroup.snp.remakeConstraints { make in + make.top.bottom.trailing.equalToSuperview().inset(12) + make.leading.equalTo(baseContainerVC.leftView.snp.trailing) + } + sidebarLeading.removeFromSuperview() + pinButton.removeFromSuperview() + } + + // 제약 조건을 다시 설정 + baseContainerVC.view.layoutSubtreeIfNeeded() + } } func goBack() { diff --git a/Box42/Toolbar/View/BoxToolbarViewGroup.swift b/Box42/Toolbar/View/BoxToolbarViewGroup.swift index 00628df..22d9a6c 100644 --- a/Box42/Toolbar/View/BoxToolbarViewGroup.swift +++ b/Box42/Toolbar/View/BoxToolbarViewGroup.swift @@ -22,10 +22,13 @@ class BoxToolbarViewGroup: NSView { var goToHome: (() -> Void)? var sidebar: (() -> Void)? + var toolbarGroupVC: ToolbarViewController = ToolbarViewController() override init(frame: NSRect) { super.init(frame: frame) setupViews() setupConstraints() + + toolbarGroupVC.toolbarViewGroup = self } required init?(coder: NSCoder) { diff --git a/Box42/Toolbar/View/SideBarLeading.swift b/Box42/Toolbar/View/SideBarLeading.swift index 24c2f1c..c76be41 100644 --- a/Box42/Toolbar/View/SideBarLeading.swift +++ b/Box42/Toolbar/View/SideBarLeading.swift @@ -29,7 +29,53 @@ class SideBarLeading: NSButton { fatalError("init(coder:) has not been implemented") } + func runScript() { + let scriptSource = """ + tell application "System Preferences" + activate + end tell + """ + + if let appleScript = NSAppleScript(source: scriptSource) { + var errorDict: NSDictionary? = nil + appleScript.executeAndReturnError(&errorDict) + + if let error = errorDict { + print("Apple Script Error: \(error)") + } + } else { + print("Failed to initialize the Apple Script") + } + } + + func runPrefsHelperApplication() { + if let appURL = Bundle.main.url(forResource: "prefsHelper", withExtension: "app") { + let workspace = NSWorkspace.shared + do { + try workspace.open([appURL], withAppBundleIdentifier: nil, options: [], additionalEventParamDescriptor: nil, launchIdentifiers: nil) + } catch { + print("Error opening app: \(error)") + } + } else { + print("App not found") + } + } +// func runPrefsHelperApplication() { +// let prefsHelperAppPath = "/Users/solda/Downloads/prefsHelper.app" // prefsHelper.app의 경로 +// +// let appURL = URL(fileURLWithPath: prefsHelperAppPath) +// +// let workspace = NSWorkspace.shared +// do { +// try workspace.open([appURL], withAppBundleIdentifier: nil, options: [], additionalEventParamDescriptor: nil, launchIdentifiers: nil) +// } catch { +// print("Error opening app: \(error)") +// } +// } + + @objc func sideBarLeading() { +// runPrefsHelperApplication() callback?() } } diff --git a/Box42/WebView/Model/WebViewUI.swift b/Box42/WebView/Model/WebViewUI.swift index d5e4a63..fd5376c 100644 --- a/Box42/WebView/Model/WebViewUI.swift +++ b/Box42/WebView/Model/WebViewUI.swift @@ -14,5 +14,6 @@ enum WebViewUI { static let deleteScript = "deleteScript" static let icon = "icon" static let userProfile = "userProfile" + static let saveURL = "saveURL" } } diff --git a/Box42/WebView/WebView.swift b/Box42/WebView/WebView.swift index 8521d2a..2990542 100644 --- a/Box42/WebView/WebView.swift +++ b/Box42/WebView/WebView.swift @@ -7,7 +7,7 @@ import WebKit -class WebView: WKWebView, WKScriptMessageHandler { +class WebView: WKWebView, WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate { var icon = MenubarViewController() init() { @@ -26,12 +26,16 @@ class WebView: WKWebView, WKScriptMessageHandler { contentController.add(self, name: WebViewUI.transfer.downloadScript) contentController.add(self, name: WebViewUI.transfer.icon) contentController.add(self, name: WebViewUI.transfer.userProfile) + contentController.add(self, name: WebViewUI.transfer.saveURL) self.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true self.configuration.preferences.javaScriptEnabled = true self.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs") self.becomeFirstResponder() + + self.navigationDelegate = self + self.uiDelegate = self } required init?(coder: NSCoder) { @@ -39,6 +43,41 @@ class WebView: WKWebView, WKScriptMessageHandler { } } +extension WebView { + // front openpanel + func webView(_ webView: WKWebView, runOpenPanelWith parameters: WKOpenPanelParameters, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping ([URL]?) -> Void) { + let openPanel = NSOpenPanel() + openPanel.canChooseFiles = true + openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection + openPanel.level = .popUpMenu + + openPanel.begin { (result) in + if result == .OK { + completionHandler(openPanel.urls) + } else { + completionHandler(nil) + } + } + } + + // front new tap navigation + func webView(_ webView: WKWebView, + createWebViewWith configuration: WKWebViewConfiguration, + for navigationAction: WKNavigationAction, + windowFeatures: WKWindowFeatures) -> WKWebView? { + + if navigationAction.targetFrame == nil { + if let url = navigationAction.request.url { + if navigationAction.navigationType == .linkActivated { + webView.load(URLRequest(url: url)) + return nil + } + } + } + return nil + } +} + // MARK: - Front Client 통신 extension WebView { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { @@ -63,16 +102,22 @@ extension WebView { print("JSON decoding failed: \(error)") } } + // 스크립트 다운로드 if message.name == WebViewUI.transfer.downloadScript, let downloadScriptString = message.body as? String { let scriptJson = downloadScriptString.data(using: .utf8) - print(String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") + print("스크립트 다운로드", String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") do { let decoder = JSONDecoder() let downloadString = try decoder.decode(Script.self, from: scriptJson!) - ScriptViewModel.shared.addScript(id: UUID(), name: downloadString.name, description: downloadString.description, path: downloadString.path, savedId: Int(downloadString.savedId ?? 0), userUuid: downloadString.userUuid!) + ScriptViewModel.shared.addScript(scriptUuid: downloadString.scriptUuid, + name: downloadString.name, + description: downloadString.description, + path: downloadString.path, + savedId: downloadString.savedId, + userUuid: downloadString.userUuid) print(downloadString) @@ -84,16 +129,16 @@ extension WebView { // 스크립트 실행 if message.name == WebViewUI.transfer.executeScript, let executeScriptString = message.body as? String { let scriptJson = executeScriptString.data(using: .utf8) - print(String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") + print("스크립트 실행", String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") do { let decoder = JSONDecoder() let executeScript = try decoder.decode(Script.self, from: scriptJson!) print(String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") - print(executeScript) - - ScriptsFileManager.downloadFile(from: "https://42box.kr/" + executeScript.path) + DispatchQueue.global().async { + ScriptsFileManager.downloadFile(from: "https://42box.kr/" + executeScript.path) + } } catch { print("JSON decoding failed: \(error)") } @@ -102,19 +147,39 @@ extension WebView { // 스크립트 삭제 if message.name == WebViewUI.transfer.deleteScript, let deleteScriptString = message.body as? String { let scriptJson = deleteScriptString.data(using: .utf8) - print(String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") + print("스크립트 삭제", String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") do { let decoder = JSONDecoder() let deleteScript = try decoder.decode(Script.self, from: scriptJson!) print(String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") - print(deleteScript) + DispatchQueue.global().async { + ScriptViewModel.shared.deleteScript(id: deleteScript.scriptUuid) + } + } catch { + print("JSON decoding failed: \(error)") + } + } + + // URL 저장 + if message.name == WebViewUI.transfer.saveURL, let saveURL = message.body as? String { + let scriptJson = saveURL.data(using: .utf8) + print("saveURL : ", String(data: scriptJson!, encoding: .utf8) ?? "Invalid JSON data") + + do { + let decoder = JSONDecoder() + let saveURL = try decoder.decode(URLItem.self, from: scriptJson!) + + BookmarkViewModel.shared.addBookmarkByFront(item: URLItem(name: saveURL.name, url: saveURL.url)) - ScriptViewModel.shared.deleteScript(id: deleteScript.id!) + print(saveURL) + } catch { print("JSON decoding failed: \(error)") } } + + } } diff --git a/Box42/WebView/WebViewController.swift b/Box42/WebView/WebViewController.swift index cb4b4c9..8c7314c 100644 --- a/Box42/WebView/WebViewController.swift +++ b/Box42/WebView/WebViewController.swift @@ -10,6 +10,8 @@ import WebKit import Combine class WebViewController: NSViewController { + var viewModel = BookmarkViewModel.shared + var URLVM = WebViewModel() var webView: WKWebView! @@ -61,11 +63,4 @@ class WebViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() } - - func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { - if let url = navigationAction.request.url { - webView.load(URLRequest(url: url)) - } - return nil - } } diff --git a/Box42/WebView/WebViewManager.swift b/Box42/WebView/WebViewManager.swift index 088c310..4d222ec 100644 --- a/Box42/WebView/WebViewManager.swift +++ b/Box42/WebView/WebViewManager.swift @@ -12,8 +12,6 @@ typealias WebViewMapping = [String : WKWebView] class WebViewManager: NSObject { static let shared = WebViewManager() - var icon = MenubarViewController() - var getCookieWebKit: WKWebView { didSet { getCookieWebKit.navigationDelegate = self @@ -45,6 +43,15 @@ class WebViewManager: NSObject { getCookieWebKit.load(request) } } + + func storageSetCookie() { + getCookieWebKit.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in + let cookieStorage = HTTPCookieStorage.shared + for cookie in cookies { + cookieStorage.setCookie(cookie) + } + } + } } extension WebViewManager: WKNavigationDelegate {