diff --git a/CodeEdit/Assets.xcassets/custom.breakpoint.fill.symbolset/Contents.json b/CodeEdit/Assets.xcassets/custom.breakpoint.fill.symbolset/Contents.json new file mode 100644 index 0000000000..970b1de6de --- /dev/null +++ b/CodeEdit/Assets.xcassets/custom.breakpoint.fill.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "filename" : "custom.breakpoint.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/CodeEdit/Assets.xcassets/custom.breakpoint.fill.symbolset/custom.breakpoint.fill.svg b/CodeEdit/Assets.xcassets/custom.breakpoint.fill.symbolset/custom.breakpoint.fill.svg new file mode 100644 index 0000000000..59e8ba76fc --- /dev/null +++ b/CodeEdit/Assets.xcassets/custom.breakpoint.fill.symbolset/custom.breakpoint.fill.svg @@ -0,0 +1,167 @@ + + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.3.0 + Requires Xcode 13 or greater + Generated from custom.breakpoint.fill + Typeset at 100 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CodeEdit/Assets.xcassets/custom.breakpoint.symbolset/Contents.json b/CodeEdit/Assets.xcassets/custom.breakpoint.symbolset/Contents.json new file mode 100644 index 0000000000..b55d72d295 --- /dev/null +++ b/CodeEdit/Assets.xcassets/custom.breakpoint.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "filename" : "custom.breakpoint.svg", + "idiom" : "universal" + } + ] +} diff --git a/CodeEdit/Assets.xcassets/custom.breakpoint.symbolset/custom.breakpoint.svg b/CodeEdit/Assets.xcassets/custom.breakpoint.symbolset/custom.breakpoint.svg new file mode 100644 index 0000000000..51d6528a95 --- /dev/null +++ b/CodeEdit/Assets.xcassets/custom.breakpoint.symbolset/custom.breakpoint.svg @@ -0,0 +1,167 @@ + + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.3.0 + Requires Xcode 13 or greater + Generated from custom.breakpoint.outline + Typeset at 100 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CodeEdit/Documents/WorkspaceDocument.swift b/CodeEdit/Documents/WorkspaceDocument.swift index fa4813d302..f0e8b160ea 100644 --- a/CodeEdit/Documents/WorkspaceDocument.swift +++ b/CodeEdit/Documents/WorkspaceDocument.swift @@ -15,6 +15,7 @@ import Search import QuickOpen import CodeEditKit import ExtensionsStore +import StatusBar @objc(WorkspaceDocument) final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { @@ -25,6 +26,7 @@ final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { @Published var sortFoldersOnTop: Bool = true @Published var selectionState: WorkspaceSelectionState = .init() + var statusBarModel: StatusBarModel? var searchState: SearchState? var quickOpenState: QuickOpenState? private var cancellables = Set() @@ -124,7 +126,7 @@ final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { self.addWindowController(windowController) } - override func read(from url: URL, ofType typeName: String) throws { + private func initWorkspaceState(_ url: URL) throws { self.workspaceClient = try .default( fileManager: .default, folderURL: url, @@ -132,6 +134,11 @@ final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate { ) self.searchState = .init(self) self.quickOpenState = .init(fileURL: url) + self.statusBarModel = .init(workspaceURL: url) + } + + override func read(from url: URL, ofType typeName: String) throws { + try initWorkspaceState(url) // Initialize Workspace do { diff --git a/CodeEdit/WorkspaceView.swift b/CodeEdit/WorkspaceView.swift index 567297d240..2af3dde5f2 100644 --- a/CodeEdit/WorkspaceView.swift +++ b/CodeEdit/WorkspaceView.swift @@ -36,12 +36,10 @@ struct WorkspaceView: View { var body: some View { ZStack { - if workspace.workspaceClient != nil { + if workspace.workspaceClient != nil, let model = workspace.statusBarModel { WorkspaceCodeFileView(windowController: windowController, workspace: workspace) .safeAreaInset(edge: .bottom) { - if let url = workspace.fileURL { - StatusBarView(workspaceURL: url) - } + StatusBarView(model: model) } } else { EmptyView() diff --git a/CodeEditModules/Modules/StatusBar/src/Model/StatusBarModel.swift b/CodeEditModules/Modules/StatusBar/src/Model/StatusBarModel.swift index 311686a1c9..6636b98da9 100644 --- a/CodeEditModules/Modules/StatusBar/src/Model/StatusBarModel.swift +++ b/CodeEditModules/Modules/StatusBar/src/Model/StatusBarModel.swift @@ -8,11 +8,29 @@ import GitClient import SwiftUI +public enum StatusBarTab: String, CaseIterable, Identifiable { + case terminal + case debugger + case output + + public var id: String { return self.rawValue } + public static var allOptions: [String] { + return StatusBarTab.allCases.map { $0.rawValue.capitalized } + } +} + /// # StatusBarModel /// /// A model class to host and manage data for the ``StatusBarView`` /// public class StatusBarModel: ObservableObject { + /// The selected tab in the main section. + /// - **0**: Terminal + /// - **1**: Debugger + /// - **2**: Output + @Published + public var selectedTab: Int = 1 + // TODO: Implement logic for updating values /// Returns number of errors during comilation @Published @@ -42,6 +60,10 @@ public class StatusBarModel: ObservableObject { @Published public var isExpanded: Bool = false + /// Returns true when the drawer is visible + @Published + public var isMaximized: Bool = false + /// The current height of the drawer. Zero if hidden @Published public var currentHeight: Double = 0 @@ -50,6 +72,14 @@ public class StatusBarModel: ObservableObject { @Published public var isDragging: Bool = false + /// Indicates whether the breakpoint is enabled or not + @Published + public var isBreakpointEnabled: Bool = true + + /// Search value to filter in drawer + @Published + public var searchText: String = "" + /// Returns the font for status bar items to use private(set) var toolbarFont: Font = .system(size: 11) @@ -60,7 +90,8 @@ public class StatusBarModel: ObservableObject { private(set) var workspaceURL: URL /// The maximum height of the drawer - private(set) var maxHeight: Double = 500 + /// when isMaximized is true the height gets set to maxHeight + private(set) var maxHeight: Double = 5000 /// The default height of the drawer private(set) var standardHeight: Double = 300 diff --git a/CodeEditModules/Modules/StatusBar/src/StatusBar.swift b/CodeEditModules/Modules/StatusBar/src/StatusBar.swift index b0482de3a2..0353acc220 100644 --- a/CodeEditModules/Modules/StatusBar/src/StatusBar.swift +++ b/CodeEditModules/Modules/StatusBar/src/StatusBar.swift @@ -7,6 +7,7 @@ import SwiftUI import GitClient +import CodeEditUI /// # StatusBarView /// @@ -21,10 +22,10 @@ public struct StatusBarView: View { @ObservedObject private var model: StatusBarModel - /// Initialize with GitClient - /// - Parameter gitClient: a GitClient - public init(workspaceURL: URL) { - self.model = .init(workspaceURL: workspaceURL) + /// Initialize with model + /// - Parameter model: The statusbar model + public init(model: StatusBarModel) { + self.model = model } public var body: some View { @@ -46,16 +47,11 @@ public struct StatusBarView: View { .foregroundStyle(.bar) HStack(spacing: 15) { HStack(spacing: 5) { - StatusBarLabelButton( - model: model, - title: model.errorCount.formatted(), - image: "xmark.octagon" - ) - StatusBarLabelButton( - model: model, - title: model.warningCount.formatted(), - image: "exclamationmark.triangle" - ) + StatusBarBreakpointButton(model: model) + Divider() + .frame(maxHeight: 12) + .padding(.horizontal, 7) + SegmentedControl($model.selectedTab, options: StatusBarTab.allOptions) } if model.selectedBranch != nil { StatusBarBranchPicker(model: model) @@ -111,7 +107,7 @@ struct SwiftUIView_Previews: PreviewProvider { static var previews: some View { ZStack(alignment: .bottom) { Color.white - StatusBarView(workspaceURL: URL(fileURLWithPath: "")) + StatusBarView(model: StatusBarModel(workspaceURL: URL(fileURLWithPath: ""))) .previewLayout(.fixed(width: 1.336, height: 500.0)) .preferredColorScheme(.light) } diff --git a/CodeEditModules/Modules/StatusBar/src/StatusBarItems/FilterTextField.swift b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/FilterTextField.swift new file mode 100644 index 0000000000..d82685d945 --- /dev/null +++ b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/FilterTextField.swift @@ -0,0 +1,53 @@ +// +// FilterTextField.swift +// +// +// Created by Stef Kors on 12/04/2022. +// + +import SwiftUI + +struct FilterTextField: View { + let title: String + + @Binding + var text: String + + var body: some View { + HStack { + Image(systemName: "line.3.horizontal.decrease.circle") + .foregroundColor(Color(nsColor: .secondaryLabelColor)) + textField + if !text.isEmpty { clearButton } + } + .padding(.horizontal, 5) + .padding(.vertical, 3) + .overlay( + RoundedRectangle(cornerRadius: 4) + .stroke(Color.gray, lineWidth: 0.5).cornerRadius(4) + ) + } + + private var textField: some View { + TextField(title, text: $text) + .disableAutocorrection(true) + .textFieldStyle(PlainTextFieldStyle()) + } + + private var clearButton: some View { + Button { + self.text = "" + } label: { + Image(systemName: "xmark.circle.fill") + } + .foregroundColor(.secondary) + .buttonStyle(PlainButtonStyle()) + } +} + +struct FilterTextField_Previews: PreviewProvider { + static var previews: some View { + FilterTextField(title: "Filter", text: .constant("")) + FilterTextField(title: "Filter", text: .constant("codeedi")) + } +} diff --git a/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarBreakpointButton.swift b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarBreakpointButton.swift new file mode 100644 index 0000000000..c00fff77af --- /dev/null +++ b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarBreakpointButton.swift @@ -0,0 +1,39 @@ +// +// StatusBarBreakpointButton.swift +// +// +// Created by Stef Kors on 14/04/2022. +// + +import SwiftUI + +internal struct StatusBarBreakpointButton: View { + @ObservedObject + private var model: StatusBarModel + + internal init(model: StatusBarModel) { + self.model = model + } + + internal var body: some View { + Button { + model.isBreakpointEnabled.toggle() + } label: { + if model.isBreakpointEnabled { + Image("custom.breakpoint.fill") + .foregroundColor(.accentColor) + } else { + Image("custom.breakpoint") + .foregroundColor(.secondary) + } + } + .buttonStyle(.plain) + } +} + +struct StatusBarBreakpointButton_Previews: PreviewProvider { + static var previews: some View { + let url = URL(string: "~/Developer")! + StatusBarBreakpointButton(model: StatusBarModel(workspaceURL: url)) + } +} diff --git a/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarClearButton.swift b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarClearButton.swift new file mode 100644 index 0000000000..5d5a091c93 --- /dev/null +++ b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarClearButton.swift @@ -0,0 +1,34 @@ +// +// StatusBarClearButton.swift +// +// +// Created by Stef Kors on 12/04/2022. +// + +import SwiftUI + +internal struct StatusBarClearButton: View { + @ObservedObject + private var model: StatusBarModel + + internal init(model: StatusBarModel) { + self.model = model + } + + internal var body: some View { + Button { + // Clear terminal + } label: { + Image(systemName: "trash") + .foregroundColor(.secondary) + } + .buttonStyle(.plain) + } +} + +struct StatusBarClearButton_Previews: PreviewProvider { + static var previews: some View { + let url = URL(string: "~/Developer")! + StatusBarClearButton(model: StatusBarModel(workspaceURL: url)) + } +} diff --git a/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarDrawer.swift b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarDrawer.swift index 1b40727184..ff53e5c145 100644 --- a/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarDrawer.swift +++ b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarDrawer.swift @@ -12,14 +12,40 @@ internal struct StatusBarDrawer: View { @ObservedObject private var model: StatusBarModel + @State + private var searchText = "" + internal init(model: StatusBarModel) { self.model = model } + var height: CGFloat { + if model.isMaximized { + return model.maxHeight + } + if model.isExpanded { + return model.currentHeight + } + return 0 + } + internal var body: some View { - TerminalEmulatorView(url: model.workspaceURL) - .frame(minHeight: 0, - idealHeight: model.isExpanded ? model.currentHeight : 0, - maxHeight: model.isExpanded ? model.currentHeight : 0) + ZStack(alignment: .bottom) { + TerminalEmulatorView(url: model.workspaceURL) + .frame(minHeight: 0, + idealHeight: height, + maxHeight: height) + HStack(alignment: .center, spacing: 10) { + FilterTextField(title: "Filter", text: $searchText) + .frame(maxWidth: 300) + Spacer() + StatusBarClearButton(model: model) + Divider() + StatusBarSplitTerminalButton(model: model) + StatusBarMaximizeButton(model: model) + } + .padding(.all, 10) + .frame(maxHeight: 34) + } } } diff --git a/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarMaximizeButton.swift b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarMaximizeButton.swift new file mode 100644 index 0000000000..821d4d7dfa --- /dev/null +++ b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarMaximizeButton.swift @@ -0,0 +1,34 @@ +// +// StatusBarMaximizeButton.swift +// +// +// Created by Stef Kors on 12/04/2022. +// + +import SwiftUI + +internal struct StatusBarMaximizeButton: View { + @ObservedObject + private var model: StatusBarModel + + internal init(model: StatusBarModel) { + self.model = model + } + + internal var body: some View { + Button { + model.isMaximized.toggle() + } label: { + Image(systemName: "arrowtriangle.up.square") + .foregroundColor(model.isMaximized ? .accentColor : .primary) + } + .buttonStyle(.plain) + } +} + +struct StatusBarMaximizeButton_Previews: PreviewProvider { + static var previews: some View { + let url = URL(string: "~/Developer")! + StatusBarMaximizeButton(model: StatusBarModel(workspaceURL: url)) + } +} diff --git a/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarSplitTerminalButton.swift b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarSplitTerminalButton.swift new file mode 100644 index 0000000000..122eb9e3eb --- /dev/null +++ b/CodeEditModules/Modules/StatusBar/src/StatusBarItems/StatusBarSplitTerminalButton.swift @@ -0,0 +1,34 @@ +// +// StatusBarSplitTerminalButton.swift +// +// +// Created by Stef Kors on 14/04/2022. +// + +import SwiftUI + +internal struct StatusBarSplitTerminalButton: View { + @ObservedObject + private var model: StatusBarModel + + internal init(model: StatusBarModel) { + self.model = model + } + + internal var body: some View { + Button { + // todo + } label: { + Image(systemName: "square.split.2x1") + .foregroundColor(.secondary) + } + .buttonStyle(.plain) + } +} + +struct StatusBarSplitTerminalButton_Previews: PreviewProvider { + static var previews: some View { + let url = URL(string: "~/Developer")! + StatusBarSplitTerminalButton(model: StatusBarModel(workspaceURL: url)) + } +} diff --git a/CodeEditModules/Package.swift b/CodeEditModules/Package.swift index 619472ea9d..2d38de2b68 100644 --- a/CodeEditModules/Package.swift +++ b/CodeEditModules/Package.swift @@ -176,6 +176,7 @@ let package = Package( "GitClient", "TerminalEmulator", "CodeFile", + "CodeEditUI", ], path: "Modules/StatusBar/src" ),