Skip to content

Commit 509f1e8

Browse files
committed
Merge branch 'worktree-workspaces'
# Conflicts: # app-workspace-searcher/src/main/java/eu/darken/butler/searcher/ui/search/SearchToolbarCard.kt # app-workspace-searcher/src/main/java/eu/darken/butler/searcher/ui/search/SearcherWorkspacePage.kt
2 parents 9767177 + 0b7c9ca commit 509f1e8

12 files changed

Lines changed: 63 additions & 2 deletions

File tree

app-workspace-editor/src/main/java/eu/darken/butler/editor/ui/editor/EditorWorkspacePage.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ fun EditorWorkspacePageHost(
8080

8181
state?.let { state ->
8282
EditorWorkspacePage(
83+
workspaceId = id,
8384
workspaceButtonState = workspaceButtonState,
8485
workspaceActionHandler = workspaceButtonVm,
8586
design = design,
@@ -110,6 +111,7 @@ fun EditorWorkspacePageHost(
110111

111112
@Composable
112113
fun EditorWorkspacePage(
114+
workspaceId: Workspace.Id,
113115
workspaceButtonState: WorkspaceButtonViewModel.State? = null,
114116
workspaceActionHandler: WorkspaceActionHandler? = null,
115117
design: WorkspaceDesign,
@@ -138,6 +140,7 @@ fun EditorWorkspacePage(
138140
) {
139141
// Header that draws under status bar
140142
EditorHeader(
143+
workspaceId = workspaceId,
141144
design = design,
142145
fileName = if (state.hasFile) state.fileName else stringResource(R.string.editor_file_untitled),
143146
isModified = state.isModified,
@@ -250,6 +253,7 @@ fun EditorWorkspacePage(
250253

251254
@Composable
252255
private fun EditorHeader(
256+
workspaceId: Workspace.Id,
253257
design: WorkspaceDesign,
254258
fileName: String,
255259
isModified: Boolean,
@@ -303,6 +307,7 @@ private fun EditorHeader(
303307

304308
WorkspaceButton(
305309
state = workspaceButtonState,
310+
currentWorkspaceId = workspaceId,
306311
workspaceActionHandler = workspaceActionHandler,
307312
)
308313
}
@@ -611,6 +616,7 @@ private fun SearchDialog(
611616
private fun EditorPagePreview() {
612617
PreviewWrapper {
613618
EditorWorkspacePage(
619+
workspaceId = Workspace.Id(),
614620
design = WorkspaceDesign(),
615621
state = EditorWorkspaceViewModel.State(
616622
id = Workspace.Id(),

app-workspace-explorer/src/main/java/eu/darken/butler/explorer/ui/explorer/ExplorerTopBar.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ import eu.darken.butler.common.files.saf.location.SAFLocationManager
1919
import eu.darken.butler.explorer.R
2020
import eu.darken.butler.explorer.core.ExplorerBreadcrumb
2121
import eu.darken.butler.explorer.core.ExplorerNavigation
22+
import eu.darken.butler.workspace.core.Workspace
2223
import eu.darken.butler.workspace.ui.manager.WorkspaceActionHandler
2324
import eu.darken.butler.workspace.ui.manager.WorkspaceButton
2425
import eu.darken.butler.workspace.ui.manager.WorkspaceButtonViewModel
2526

2627
@Composable
2728
fun ExplorerTopBar(
29+
workspaceId: Workspace.Id,
2830
modifier: Modifier = Modifier,
2931
breadcrumbs: List<ExplorerBreadcrumb>,
3032
scrollBehavior: TopAppBarScrollBehavior? = null,
@@ -55,6 +57,7 @@ fun ExplorerTopBar(
5557
WorkspaceButton(
5658
modifier = Modifier.padding(end = 8.dp),
5759
state = workspaceButtonState,
60+
currentWorkspaceId = workspaceId,
5861
workspaceActionHandler = workspaceActionHandler,
5962
)
6063
}
@@ -89,6 +92,7 @@ fun ExplorerTopBarPreview() {
8992

9093
PreviewWrapper {
9194
ExplorerTopBar(
95+
workspaceId = Workspace.Id(),
9296
breadcrumbs = mockBreadcrumbs,
9397
scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(),
9498
onBreadcrumbClick = {},

app-workspace-explorer/src/main/java/eu/darken/butler/explorer/ui/explorer/ExplorerWorkspacePage.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ fun ExplorerWorkspacePageHost(
114114
}
115115

116116
ExplorerWorkspacePage(
117+
workspaceId = id,
117118
design = design,
118119
mainStateSource = vm.state,
119120
clipboardStateSource = vm.clipboard,
@@ -127,6 +128,7 @@ fun ExplorerWorkspacePageHost(
127128
@OptIn(ExperimentalMaterial3Api::class)
128129
@Composable
129130
fun ExplorerWorkspacePage(
131+
workspaceId: Workspace.Id,
130132
design: WorkspaceDesign = WorkspaceDesign(),
131133
mainStateSource: Flow<ExplorerWorkspaceViewModel.State>,
132134
operationsStateSource: Flow<ExplorerWorkspaceViewModel.OperationsState>,
@@ -284,6 +286,7 @@ fun ExplorerWorkspacePage(
284286
} else {
285287
// Normal mode - use full explorer top bar
286288
ExplorerTopBar(
289+
workspaceId = workspaceId,
287290
breadcrumbs = mainState.breadcrumbs,
288291
scrollBehavior = scrollBehavior,
289292
onBreadcrumbClick = { target -> vm?.navigate(target) },

app-workspace-explorer/src/main/java/eu/darken/butler/explorer/ui/explorer/ExplorerWorkspacePagePreviews.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import eu.darken.butler.explorer.core.ExplorerNavigation
1111
import eu.darken.butler.explorer.core.engine.ExplorerLocation
1212
import eu.darken.butler.explorer.ui.explorer.actions.ExplorerAction
1313
import eu.darken.butler.explorer.ui.explorer.preview.MockDataProvider
14+
import eu.darken.butler.workspace.core.Workspace
1415
import eu.darken.butler.workspace.ui.manager.WorkspaceButtonViewModel
1516
import kotlinx.coroutines.flow.flowOf
1617

@@ -62,6 +63,7 @@ fun ExplorerWorkspacePagePreview() {
6263
)
6364
PreviewWrapper {
6465
ExplorerWorkspacePage(
66+
workspaceId = Workspace.Id(),
6567
mainStateSource = flowOf(mockState),
6668
workspaceStateSource = flowOf(null),
6769
clipboardStateSource = flowOf(ExplorerWorkspaceViewModel.ClipboardState()),
@@ -83,6 +85,7 @@ fun ExplorerWorkspacePageLoadingPreview() {
8385
)
8486
PreviewWrapper {
8587
ExplorerWorkspacePage(
88+
workspaceId = Workspace.Id(),
8689
mainStateSource = flowOf(mockState),
8790
workspaceStateSource = flowOf(null),
8891
clipboardStateSource = flowOf(ExplorerWorkspaceViewModel.ClipboardState()),
@@ -106,6 +109,7 @@ fun ExplorerWorkspacePageEmptyPreview() {
106109
)
107110
PreviewWrapper {
108111
ExplorerWorkspacePage(
112+
workspaceId = Workspace.Id(),
109113
mainStateSource = flowOf(mockState),
110114
workspaceStateSource = flowOf(null),
111115
clipboardStateSource = flowOf(ExplorerWorkspaceViewModel.ClipboardState()),
@@ -137,6 +141,7 @@ fun ExplorerWorkspacePageWithSelectionPreview() {
137141
)
138142
PreviewWrapper {
139143
ExplorerWorkspacePage(
144+
workspaceId = Workspace.Id(),
140145
mainStateSource = flowOf(mockState),
141146
workspaceStateSource = flowOf(null),
142147
clipboardStateSource = flowOf(ExplorerWorkspaceViewModel.ClipboardState()),
@@ -174,6 +179,7 @@ fun ExplorerWorkspacePageGridModePreview() {
174179
)
175180
PreviewWrapper {
176181
ExplorerWorkspacePage(
182+
workspaceId = Workspace.Id(),
177183
mainStateSource = flowOf(mockState),
178184
workspaceStateSource = flowOf(null),
179185
clipboardStateSource = flowOf(ExplorerWorkspaceViewModel.ClipboardState()),
@@ -232,6 +238,7 @@ fun ExplorerWorkspacePageWithAllBarsPreview() {
232238

233239
PreviewWrapper {
234240
ExplorerWorkspacePage(
241+
workspaceId = Workspace.Id(),
235242
mainStateSource = flowOf(mockState),
236243
clipboardStateSource = flowOf(mockClipboardEntries),
237244
operationsStateSource = flowOf(mockOperations),
@@ -290,6 +297,7 @@ fun ExplorerWorkspacePageWithExpandedBarsPreview() {
290297

291298
PreviewWrapper {
292299
ExplorerWorkspacePage(
300+
workspaceId = Workspace.Id(),
293301
mainStateSource = flowOf(mockState),
294302
clipboardStateSource = flowOf(mockClipboardEntries),
295303
operationsStateSource = flowOf(mockOperations),

app-workspace-searcher/src/main/java/eu/darken/butler/searcher/ui/search/SearchToolbarCard.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import eu.darken.butler.workspace.ui.manager.WorkspaceDesign
4040

4141
@Composable
4242
fun SearchToolbarCard(
43+
workspaceId: Workspace.Id,
4344
modifier: Modifier = Modifier,
4445
state: SearcherWorkspaceViewModel.State,
4546
design: WorkspaceDesign,
@@ -136,6 +137,7 @@ fun SearchToolbarCard(
136137

137138
WorkspaceButton(
138139
state = workspaceButtonState,
140+
currentWorkspaceId = workspaceId,
139141
workspaceActionHandler = workspaceActionHandler,
140142
)
141143
}
@@ -178,6 +180,7 @@ fun SearchToolbarCard(
178180
private fun SearchToolbarCardPreview() {
179181
PreviewWrapper {
180182
SearchToolbarCard(
183+
workspaceId = Workspace.Id(),
181184
state = SearcherWorkspaceViewModel.State(
182185
id = Workspace.Id(),
183186
searchTargets = listOf(

app-workspace-searcher/src/main/java/eu/darken/butler/searcher/ui/search/SearcherWorkspacePage.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import kotlinx.coroutines.flow.Flow
7171
@OptIn(ExperimentalMaterial3Api::class)
7272
@Composable
7373
fun SearcherWorkspacePage(
74+
workspaceId: Workspace.Id,
7475
design: WorkspaceDesign = WorkspaceDesign(),
7576
stateSource: Flow<SearcherWorkspaceViewModel.State>,
7677
clipboardStateSource: Flow<SearcherWorkspaceViewModel.ClipboardState>,
@@ -290,6 +291,7 @@ fun SearcherWorkspacePage(
290291

291292
// Pinned search toolbar at top - collapses on scroll
292293
SearchToolbarCard(
294+
workspaceId = workspaceId,
293295
state = currentState,
294296
design = design,
295297
collapsedFraction = topToolbarScrollBehavior.state.collapsedFraction,
@@ -530,6 +532,7 @@ fun SearcherWorkspacePageHost(
530532
ErrorEventHandler(vm)
531533

532534
SearcherWorkspacePage(
535+
workspaceId = id,
533536
design = design,
534537
stateSource = vm.state,
535538
clipboardStateSource = vm.clipboard,

app-workspace-templates/src/main/java/eu/darken/butler/templates/ui/TemplatesWorkspacePage.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ fun TemplatesWorkspacePageHost(
8989

9090
state?.let { state ->
9191
TemplatesWorkspacePage(
92+
workspaceId = id,
9293
design = design,
9394
state = state,
9495
onNavToSettings = { vm.navTo(Nav.Main.settings()) },
@@ -100,6 +101,7 @@ fun TemplatesWorkspacePageHost(
100101

101102
@Composable
102103
fun TemplatesWorkspacePage(
104+
workspaceId: Workspace.Id,
103105
design: WorkspaceDesign = WorkspaceDesign(),
104106
state: TemplatesWorkspaceViewModel.State,
105107
onNavToSettings: () -> Unit,
@@ -139,6 +141,7 @@ fun TemplatesWorkspacePage(
139141

140142
WorkspaceButton(
141143
state = workspaceButtonState,
144+
currentWorkspaceId = workspaceId,
142145
workspaceActionHandler = workspaceActionHandler,
143146
)
144147
}
@@ -538,6 +541,7 @@ private fun TemplatesWorkspacePagePreview() {
538541
PreviewWrapper {
539542
val workspaceId = Workspace.Id()
540543
TemplatesWorkspacePage(
544+
workspaceId = workspaceId,
541545
state = TemplatesWorkspaceViewModel.State(
542546
id = workspaceId,
543547
templates = listOf(

app-workspace/src/main/java/eu/darken/butler/workspace/ui/manager/WorkspaceButton.kt

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.size
1212
import androidx.compose.foundation.shape.CircleShape
1313
import androidx.compose.foundation.shape.RoundedCornerShape
1414
import androidx.compose.material.icons.Icons
15+
import androidx.compose.material.icons.twotone.Close
1516
import androidx.compose.material.icons.twotone.Settings
1617
import androidx.compose.material.icons.twotone.Workspaces
1718
import androidx.compose.material3.DropdownMenu
@@ -37,6 +38,7 @@ import eu.darken.butler.common.compose.ButlerIcon
3738
import eu.darken.butler.common.compose.Preview2
3839
import eu.darken.butler.common.compose.PreviewWrapper
3940
import eu.darken.butler.workspace.R
41+
import eu.darken.butler.workspace.core.Workspace
4042
import eu.darken.butler.workspace.core.WorkspaceAction
4143

4244
@OptIn(ExperimentalFoundationApi::class)
@@ -46,6 +48,7 @@ fun WorkspaceButton(
4648
state: WorkspaceButtonViewModel.State?,
4749
containerColor: Color? = null,
4850
contentColor: Color? = null,
51+
currentWorkspaceId: Workspace.Id? = null,
4952
workspaceActionHandler: WorkspaceActionHandler? = null,
5053
) {
5154
var expanded by remember { mutableStateOf(false) }
@@ -100,6 +103,21 @@ fun WorkspaceButton(
100103
)
101104
}
102105
)
106+
if (currentWorkspaceId != null) {
107+
DropdownMenuItem(
108+
text = { Text(stringResource(R.string.workspace_button_menu_close_current_action)) },
109+
onClick = {
110+
expanded = false
111+
workspaceActionHandler?.executeWorkspaceAction(WorkspaceAction.Close(currentWorkspaceId))
112+
},
113+
leadingIcon = {
114+
Icon(
115+
imageVector = Icons.TwoTone.Close,
116+
contentDescription = null
117+
)
118+
}
119+
)
120+
}
103121
}
104122

105123
// Badge showing workspace count (top-left)
@@ -211,13 +229,14 @@ private fun WorkspaceButtonPreview() {
211229
),
212230
)
213231

214-
// Multiple workspaces with operations
232+
// Multiple workspaces with operations (with focused workspace)
215233
WorkspaceButton(
216234
state = WorkspaceButtonViewModel.State(
217235
workspaceCount = 3,
218236
operationsCount = 2,
219237
attentionCount = 0,
220238
),
239+
currentWorkspaceId = Workspace.Id(),
221240
)
222241

223242
// All badges active

app-workspace/src/main/java/eu/darken/butler/workspace/ui/manager/WorkspaceButtonViewModel.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ class WorkspaceButtonViewModel @Inject constructor(
2121
private val workspaceRemote: WorkspaceRemote,
2222
) : ViewModel4(dispatchers, logTag("Workspace", "Button", "VM"), navCtrl), WorkspaceActionHandler {
2323

24+
init {
25+
log(TAG) { "init(): $this" }
26+
}
27+
2428
val state = workspaceRemote.state.map {
2529
State(
2630
workspaceCount = it.workspaceCount,
@@ -54,4 +58,8 @@ class WorkspaceButtonViewModel @Inject constructor(
5458
val operationsCount: Int = 0,
5559
val attentionCount: Int = 0,
5660
)
61+
62+
companion object {
63+
private val TAG = logTag("Workspace", "Button")
64+
}
5765
}

app-workspace/src/main/res/values/strings.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
<string name="workspace_templates_label">Templates</string>
4747

4848
<!-- Workspace Button Menu -->
49-
<string name="workspace_button_menu_manager_action">Tab Manager</string>
49+
<string name="workspace_button_menu_manager_action">Tab manager</string>
50+
<string name="workspace_button_menu_close_current_action">Close current tab</string>
5051
<string name="workspace_button_menu_settings_action">Settings</string>
5152

5253
<!-- Operations -->

0 commit comments

Comments
 (0)