Skip to content

Commit a0b8567

Browse files
committed
Improving Utility Area Terminal
1 parent 5096db5 commit a0b8567

1 file changed

Lines changed: 72 additions & 31 deletions

File tree

CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,78 @@
1-
// UtilityAreaViewModel.swift
2-
// Atualizado para suportar drag-and-drop com reordenação visual e final com ScrollView + VStack
1+
//
2+
// UtilityAreaViewModel.swift
3+
// CodeEdit
4+
//
5+
// Created by Lukas Pistrol on 20.03.22.
6+
//
37

48
import SwiftUI
59
import UniformTypeIdentifiers
610

7-
/// # UtilityAreaViewModel
8-
/// A model class to host and manage data for the Utility area.
11+
/// View model responsible for managing terminal groups, individual terminals,
12+
/// selection state, drag-and-drop operations, and utility panel configuration.
913
class UtilityAreaViewModel: ObservableObject {
1014

15+
// MARK: - UI State
16+
17+
/// Currently selected tab in the utility area.
1118
@Published var selectedTab: UtilityAreaTab? = .terminal
1219

20+
/// Flat list of all terminals, derived from `terminalGroups`.
1321
@Published var terminals: [UtilityAreaTerminal] = []
22+
23+
/// List of terminal groups.
24+
/// Automatically updates the flat `terminals` array when changed.
1425
@Published var terminalGroups: [UtilityAreaTerminalGroup] = [] {
1526
didSet {
1627
self.terminals = terminalGroups.flatMap { $0.terminals }
1728
}
1829
}
1930

31+
/// Set of selected terminal IDs.
2032
@Published var selectedTerminals: Set<UUID> = []
21-
@Published var dragOverTerminalID: UUID? = nil
22-
@Published var draggedTerminalID: UUID? = nil
33+
34+
/// ID of the terminal currently hovered as a drop target.
35+
@Published var dragOverTerminalID: UUID?
36+
37+
/// ID of the terminal being dragged.
38+
@Published var draggedTerminalID: UUID?
39+
40+
/// Whether the utility area is currently collapsed.
2341
@Published var isCollapsed: Bool = false
42+
43+
/// Whether the panel collapse/expand action should animate.
2444
@Published var animateCollapse: Bool = true
45+
46+
/// Whether the utility area is maximized.
2547
@Published var isMaximized: Bool = false
48+
49+
/// Current height of the utility area panel.
2650
@Published var currentHeight: Double = 0
27-
@Published var editingTerminalID: UUID? = nil
51+
52+
/// ID of the terminal currently being edited (e.g. for inline title editing).
53+
@Published var editingTerminalID: UUID?
54+
55+
/// Available tabs in the utility area.
2856
@Published var tabItems: [UtilityAreaTab] = UtilityAreaTab.allCases
57+
58+
/// View model for the current tab (e.g. terminal tab).
2959
@Published var tabViewModel = UtilityAreaTabViewModel()
30-
@Published var editingGroupID: UUID? = nil
60+
61+
/// ID of the group currently being edited (e.g. renaming).
62+
@Published var editingGroupID: UUID?
63+
64+
/// Focus state for managing terminal keyboard focus.
3165
@FocusState private var focusedTerminalID: UUID?
32-
// MARK: - Drag Support
3366

67+
// MARK: - Drag-and-Drop Support
68+
69+
/// Previews a terminal move by temporarily updating the groups' array structure.
3470
func previewMoveTerminal(_ terminalID: UUID, toGroup groupID: UUID, before destinationID: UUID?) {
35-
guard let currentGroupIndex = terminalGroups.firstIndex(where: { $0.terminals.contains(where: { $0.id == terminalID }) }),
36-
let currentTerminalIndex = terminalGroups[currentGroupIndex].terminals.firstIndex(where: { $0.id == terminalID }) else {
71+
guard let currentGroupIndex = terminalGroups.firstIndex(where: {
72+
$0.terminals.contains(where: { $0.id == terminalID })
73+
}),
74+
let currentTerminalIndex = terminalGroups[currentGroupIndex]
75+
.terminals.firstIndex(where: { $0.id == terminalID }) else {
3776
return
3877
}
3978

@@ -50,20 +89,18 @@ class UtilityAreaViewModel: ObservableObject {
5089

5190
if let targetIndex = terminalGroups.firstIndex(where: { $0.id == groupID }) {
5291
var group = terminalGroups[targetIndex]
53-
5492
if let destID = destinationID,
5593
let destIndex = group.terminals.firstIndex(where: { $0.id == destID }) {
5694
group.terminals.insert(terminal, at: destIndex)
5795
} else {
5896
group.terminals.append(terminal)
5997
}
60-
6198
terminalGroups[targetIndex] = group
6299
}
63100
}
64101

102+
/// Finalizes a terminal move across or within groups, updating the actual structure.
65103
func finalizeMoveTerminal(_ terminal: UtilityAreaTerminal, toGroup groupID: UUID, before destinationID: UUID?) {
66-
67104
let alreadyInGroup = terminalGroups.contains { group in
68105
group.id == groupID &&
69106
group.terminals.count == 1 &&
@@ -72,17 +109,16 @@ class UtilityAreaViewModel: ObservableObject {
72109

73110
guard !alreadyInGroup else { return }
74111

112+
// Remove terminal from all groups
75113
for index in terminalGroups.indices {
76114
terminalGroups[index].terminals.removeAll { $0.id == terminal.id }
77115
}
78116

79-
// Remove grupos vazios após a remoção
117+
// Remove empty groups
80118
terminalGroups.removeAll { $0.terminals.isEmpty }
81119

82-
// Adiciona ao grupo destino
83-
guard let groupIndex = terminalGroups.firstIndex(where: { $0.id == groupID }) else {
84-
return
85-
}
120+
// Insert into new group
121+
guard let groupIndex = terminalGroups.firstIndex(where: { $0.id == groupID }) else { return }
86122

87123
if let destinationID,
88124
let destinationIndex = terminalGroups[groupIndex].terminals.firstIndex(where: { $0.id == destinationID }) {
@@ -91,18 +127,18 @@ class UtilityAreaViewModel: ObservableObject {
91127
terminalGroups[groupIndex].terminals.append(terminal)
92128
}
93129

94-
// Atualiza seleção
130+
// Update selection
95131
if !selectedTerminals.contains(terminal.id) {
96132
selectedTerminals = [terminal.id]
97133
}
98134

99-
for index in terminalGroups.indices {
100-
if !terminalGroups[index].userName {
101-
terminalGroups[index].name = "\(terminalGroups[index].terminals.count) Terminals"
102-
}
135+
// Auto-name group if it wasn't named by user
136+
for index in terminalGroups.indices where !terminalGroups[index].userName {
137+
terminalGroups[index].name = "\(terminalGroups[index].terminals.count) Terminals"
103138
}
104139
}
105140

141+
/// Removes a terminal from all groups by ID and returns it.
106142
private func removeTerminal(withID id: UUID) -> UtilityAreaTerminal? {
107143
for index in terminalGroups.indices {
108144
if let terminalIndex = terminalGroups[index].terminals.firstIndex(where: { $0.id == id }) {
@@ -112,20 +148,23 @@ class UtilityAreaViewModel: ObservableObject {
112148
return nil
113149
}
114150

115-
// MARK: - State Restoration
151+
// MARK: - Panel State Restoration
116152

153+
/// Restores panel state from the workspace object (collapsed, height, maximized).
117154
func restoreFromState(_ workspace: WorkspaceDocument) {
118155
isCollapsed = workspace.getFromWorkspaceState(.utilityAreaCollapsed) as? Bool ?? false
119156
currentHeight = workspace.getFromWorkspaceState(.utilityAreaHeight) as? Double ?? 300.0
120157
isMaximized = workspace.getFromWorkspaceState(.utilityAreaMaximized) as? Bool ?? false
121158
}
122159

160+
/// Persists current panel state into the workspace object.
123161
func saveRestorationState(_ workspace: WorkspaceDocument) {
124162
workspace.addToWorkspaceState(key: .utilityAreaCollapsed, value: isCollapsed)
125163
workspace.addToWorkspaceState(key: .utilityAreaHeight, value: currentHeight)
126164
workspace.addToWorkspaceState(key: .utilityAreaMaximized, value: isMaximized)
127165
}
128166

167+
/// Toggles panel collapse with optional animation.
129168
func togglePanel(animation: Bool = true) {
130169
self.animateCollapse = animation
131170
self.isMaximized = false
@@ -134,22 +173,21 @@ class UtilityAreaViewModel: ObservableObject {
134173

135174
// MARK: - Terminal Management
136175

176+
/// Removes terminals by their IDs and updates groups and selection.
137177
func removeTerminals(_ ids: Set<UUID>) {
138178
for index in terminalGroups.indices {
139179
terminalGroups[index].terminals.removeAll { ids.contains($0.id) }
140180
}
141-
142-
// Remove grupos vazios
143181
terminalGroups.removeAll { $0.terminals.isEmpty }
144182

145-
// Atualiza seleção
146183
selectedTerminals.subtract(ids)
147184
if selectedTerminals.isEmpty,
148185
let last = terminalGroups.last?.terminals.last {
149186
selectedTerminals = [last.id]
150187
}
151188
}
152189

190+
/// Updates a terminal's title, or resets it if `nil`.
153191
func updateTerminal(_ id: UUID, title: String?) {
154192
for index in terminalGroups.indices {
155193
if let terminalIndex = terminalGroups[index].terminals.firstIndex(where: { $0.id == id }) {
@@ -163,15 +201,14 @@ class UtilityAreaViewModel: ObservableObject {
163201
}
164202
}
165203

204+
/// Initializes a default terminal if none exist.
166205
func initializeTerminals(workspaceURL: URL) {
167206
guard terminalGroups.flatMap({ $0.terminals }).isEmpty else { return }
168207
addTerminal(rootURL: workspaceURL)
169208
}
170209

210+
/// Adds a new terminal, optionally to a specific group and with a specific shell.
171211
func addTerminal(to groupID: UUID? = nil, shell: Shell? = nil, rootURL: URL?) {
172-
173-
print("Did add temrinal")
174-
175212
let newTerminal = UtilityAreaTerminal(
176213
id: UUID(),
177214
url: rootURL ?? URL(filePath: "~/"),
@@ -189,6 +226,7 @@ class UtilityAreaViewModel: ObservableObject {
189226
selectedTerminals = [newTerminal.id]
190227
}
191228

229+
/// Replaces a terminal with a new instance, useful for restarting.
192230
func replaceTerminal(_ replacing: UUID) {
193231
for index in terminalGroups.indices {
194232
if let idx = terminalGroups[index].terminals.firstIndex(where: { $0.id == replacing }) {
@@ -211,10 +249,12 @@ class UtilityAreaViewModel: ObservableObject {
211249
}
212250
}
213251

252+
/// Reorders terminals in the flat `terminals` list (UI only).
214253
func reorderTerminals(from source: IndexSet, to destination: Int) {
215254
terminals.move(fromOffsets: source, toOffset: destination)
216255
}
217256

257+
/// Moves a terminal to a specific group and index.
218258
func moveTerminal(_ terminal: UtilityAreaTerminal, toGroup targetGroupID: UUID, at index: Int) {
219259
for index in terminalGroups.indices {
220260
terminalGroups[index].terminals.removeAll { $0.id == terminal.id }
@@ -224,6 +264,7 @@ class UtilityAreaViewModel: ObservableObject {
224264
}
225265
}
226266

267+
/// Creates a new terminal group with the given terminals.
227268
func createGroup(with terminals: [UtilityAreaTerminal]) {
228269
terminalGroups.append(.init(name: "\(terminalGroups.count) Terminals", terminals: terminals))
229270
}

0 commit comments

Comments
 (0)