diff --git a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 568d20787..114f6ef0c 100644 --- a/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -168,8 +168,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/groue/Semaphore", "state" : { - "revision" : "f1c4a0acabeb591068dea6cffdd39660b86dec28", - "version" : "0.0.8" + "revision" : "2543679282aa6f6c8ecf2138acd613ed20790bc2", + "version" : "0.1.0" } }, { @@ -240,8 +240,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/siteline/SwiftUI-Introspect.git", "state" : { - "revision" : "668a65735751432b640260c56dfa621cec568368", - "version" : "1.2.0" + "revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336", + "version" : "1.3.0" } }, { diff --git a/CodeEdit/Features/UtilityArea/TerminalUtility/UtilityAreaTerminalView.swift b/CodeEdit/Features/UtilityArea/TerminalUtility/UtilityAreaTerminalView.swift index 63d29d92e..7110bfd00 100644 --- a/CodeEdit/Features/UtilityArea/TerminalUtility/UtilityAreaTerminalView.swift +++ b/CodeEdit/Features/UtilityArea/TerminalUtility/UtilityAreaTerminalView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Cocoa final class UtilityAreaTerminal: ObservableObject, Identifiable, Equatable { let id: UUID @@ -36,6 +37,12 @@ struct UtilityAreaTerminalView: View { private var darkAppearance @AppSettings(\.theme.useThemeBackground) private var useThemeBackground + @AppSettings(\.textEditing.font) + private var textEditingFont + @AppSettings(\.terminal.font) + private var terminalFont + @AppSettings(\.terminal.useTextEditorFont) + private var useTextEditorFont @Environment(\.colorScheme) private var colorScheme @@ -52,6 +59,10 @@ struct UtilityAreaTerminalView: View { @State private var popoverSource: CGRect = .zero + var font: NSFont { + useTextEditorFont == true ? textEditingFont.current : terminalFont.current + } + private func initializeTerminals() { let id = UUID() @@ -101,31 +112,52 @@ struct UtilityAreaTerminalView: View { utilityAreaViewModel.terminals.move(fromOffsets: source, toOffset: destination) } + func fontTotalHeight(nsFont: NSFont) -> CGFloat { + let ctFont = nsFont as CTFont + let ascent = CTFontGetAscent(ctFont) + let descent = CTFontGetDescent(ctFont) + let leading = CTFontGetLeading(ctFont) + + return ascent + descent + leading + } + var body: some View { UtilityAreaTabView(model: utilityAreaViewModel.tabViewModel) { tabState in ZStack { if utilityAreaViewModel.selectedTerminals.isEmpty { CEContentUnavailableView("No Selection") - } - ForEach(utilityAreaViewModel.terminals) { terminal in - TerminalEmulatorView( - url: terminal.url!, - shellType: terminal.shell, - onTitleChange: { [weak terminal] newTitle in - guard let id = terminal?.id else { return } - // This can be called whenever, even in a view update so it needs to be dispatched. - DispatchQueue.main.async { [weak utilityAreaViewModel] in - utilityAreaViewModel?.updateTerminal(id, title: newTitle) + } else { + GeometryReader { geometry in + let containerHeight = geometry.size.height + let totalFontHeight = fontTotalHeight(nsFont: font).rounded(.up) + let constrainedHeight = containerHeight - containerHeight.truncatingRemainder( + dividingBy: totalFontHeight + ) + ForEach(utilityAreaViewModel.terminals) { terminal in + VStack(spacing: 0) { + Spacer(minLength: 0) + .frame(minHeight: 0) + TerminalEmulatorView( + url: terminal.url!, + shellType: terminal.shell, + onTitleChange: { [weak terminal] newTitle in + guard let id = terminal?.id else { return } + // This can be called whenever, even in a view update + // so it needs to be dispatched. + DispatchQueue.main.async { [weak utilityAreaViewModel] in + utilityAreaViewModel?.updateTerminal(id, title: newTitle) + } + } + ) + .frame(height: constrainedHeight - totalFontHeight + 1) } + .disabled(terminal.id != utilityAreaViewModel.selectedTerminals.first) + .opacity(terminal.id == utilityAreaViewModel.selectedTerminals.first ? 1 : 0) } - ) - .padding(.top, 10) - .padding(.horizontal, 10) - .contentShape(Rectangle()) - .disabled(terminal.id != utilityAreaViewModel.selectedTerminals.first) - .opacity(terminal.id == utilityAreaViewModel.selectedTerminals.first ? 1 : 0) + } } } + .padding(.horizontal, 10) .paneToolbar { PaneToolbarSection { UtilityAreaTerminalPicker( diff --git a/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift b/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift index cf7388edf..72f7d790b 100644 --- a/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift +++ b/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift @@ -54,9 +54,8 @@ class UtilityAreaViewModel: ObservableObject { } func togglePanel() { - withAnimation { - self.isCollapsed.toggle() - } + self.isMaximized = false + self.isCollapsed.toggle() } /// Update a terminal's title. diff --git a/CodeEdit/Features/UtilityArea/Views/PaneToolbar.swift b/CodeEdit/Features/UtilityArea/Views/PaneToolbar.swift index 3ea22fd54..658cc46fc 100644 --- a/CodeEdit/Features/UtilityArea/Views/PaneToolbar.swift +++ b/CodeEdit/Features/UtilityArea/Views/PaneToolbar.swift @@ -26,7 +26,6 @@ struct PaneToolbar: View { .frame(width: 24) } .opacity(0) - Divider().opacity(0) } content if model.hasTrailingSidebar @@ -35,16 +34,13 @@ struct PaneToolbar: View { && model.trailingSidebarIsCollapsed) || paneArea == .trailing ) || !model.hasTrailingSidebar { - Divider().opacity(0) - PaneToolbarSection { - if model.hasTrailingSidebar { + if model.hasTrailingSidebar { + PaneToolbarSection { Spacer() .frame(width: 24) } - Spacer() - .frame(width: 24) + .opacity(0) } - .opacity(0) } } .buttonStyle(.icon(size: 24)) diff --git a/CodeEdit/Features/UtilityArea/Views/UtilityAreaView.swift b/CodeEdit/Features/UtilityArea/Views/UtilityAreaView.swift index 97bb62445..a89e1e665 100644 --- a/CodeEdit/Features/UtilityArea/Views/UtilityAreaView.swift +++ b/CodeEdit/Features/UtilityArea/Views/UtilityAreaView.swift @@ -41,28 +41,5 @@ struct UtilityAreaView: View { .overlay(Color(nsColor: colorScheme == .dark ? .black : .clear)) } } - .overlay(alignment: .bottomTrailing) { - HStack(spacing: 5) { - Divider() - HStack(spacing: 0) { - Button { - utilityAreaViewModel.isMaximized.toggle() - } label: { - Image(systemName: "arrowtriangle.up.square") - } - .buttonStyle(.icon(isActive: utilityAreaViewModel.isMaximized, size: 24)) - } - } - .colorScheme( - utilityAreaViewModel.selectedTerminals.isEmpty - ? colorScheme - : matchAppearance && darkAppearance - ? themeModel.selectedDarkTheme?.appearance == .dark ? .dark : .light - : themeModel.selectedTheme?.appearance == .dark ? .dark : .light - ) - .padding(.horizontal, 5) - .padding(.vertical, 8) - .frame(maxHeight: 27) - } } } diff --git a/CodeEdit/WorkspaceView.swift b/CodeEdit/WorkspaceView.swift index ff303aa9f..8e3c9f224 100644 --- a/CodeEdit/WorkspaceView.swift +++ b/CodeEdit/WorkspaceView.swift @@ -28,6 +28,10 @@ struct WorkspaceView: View { @State private var showingAlert = false @State private var terminalCollapsed = true @State private var editorCollapsed = false + @State private var editorsHeight: CGFloat = 0 + @State private var drawerHeight: CGFloat = 0 + + private let statusbarHeight: CGFloat = 29 private var keybindings: KeybindingManager = .shared @@ -36,28 +40,75 @@ struct WorkspaceView: View { VStack { SplitViewReader { proxy in SplitView(axis: .vertical) { - EditorLayoutView( - layout: editorManager.isFocusingActiveEditor - ? editorManager.activeEditor.getEditorLayout() ?? editorManager.editorLayout - : editorManager.editorLayout, - focus: $focusedEditor - ) + ZStack { + GeometryReader { geo in + EditorLayoutView( + layout: editorManager.isFocusingActiveEditor + ? editorManager.activeEditor.getEditorLayout() ?? editorManager.editorLayout + : editorManager.editorLayout, + focus: $focusedEditor + ) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .onChange(of: geo.size.height) { newHeight in + editorsHeight = newHeight + } + .onAppear { + editorsHeight = geo.size.height + } + } + } + .frame(minHeight: 170 + 29 + 29) .collapsable() .collapsed($utilityAreaViewModel.isMaximized) - .frame(minHeight: 170 + 29 + 29) - .frame(maxWidth: .infinity, maxHeight: .infinity) .holdingPriority(.init(1)) - .safeAreaInset(edge: .bottom, spacing: 0) { - StatusBarView(proxy: proxy) - } - UtilityAreaView() + Rectangle() .collapsable() .collapsed($utilityAreaViewModel.isCollapsed) + .opacity(0) .frame(idealHeight: 260) .frame(minHeight: 100) + .background { + GeometryReader { geo in + Rectangle() + .opacity(0) + .onChange(of: geo.size.height) { newHeight in + drawerHeight = newHeight + } + .onAppear { + drawerHeight = geo.size.height + } + } + } } .edgesIgnoringSafeArea(.top) .frame(maxWidth: .infinity, maxHeight: .infinity) + .overlay(alignment: .top) { + ZStack(alignment: .top) { + UtilityAreaView() + .frame(height: utilityAreaViewModel.isMaximized ? nil : drawerHeight) + .frame(maxHeight: utilityAreaViewModel.isMaximized ? .infinity : nil) + .padding(.top, utilityAreaViewModel.isMaximized ? statusbarHeight + 1 : 0) + .offset(y: utilityAreaViewModel.isMaximized ? 0 : editorsHeight + 1) + VStack(spacing: 0) { + StatusBarView(proxy: proxy) + if utilityAreaViewModel.isMaximized { + PanelDivider() + } + } + .offset(y: utilityAreaViewModel.isMaximized ? 0 : editorsHeight - statusbarHeight) + } + } + .onChange(of: focusedEditor) { newValue in + /// update active tab group only if the new one is not the same with it. + if let newValue, editorManager.activeEditor != newValue { + editorManager.activeEditor = newValue + } + } + .onChange(of: editorManager.activeEditor) { newValue in + if newValue != focusedEditor { + focusedEditor = newValue + } + } .task { themeModel.colorScheme = colorScheme